Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
6bad682
feat(openviking-controlplane): enterprise library tier + custom reque…
t0saki Jul 24, 2026
b382ab9
feat(openviking-controlplane): user management + collection update
t0saki Jul 24, 2026
5a22816
feat(openviking-controlplane): billing config on create/update
t0saki Jul 30, 2026
c97bb25
feat(openviking-controlplane): default create billing to agentplan_pe…
t0saki Jul 30, 2026
1c63876
docs(openviking-controlplane): formalize billing wording
t0saki Jul 30, 2026
a0a9b2d
fix(openviking-controlplane): validate HTTP header encoding
t0saki Jul 31, 2026
672f49e
fix(openviking-controlplane): keep CLI parse errors concise
t0saki Jul 31, 2026
537697d
feat(openviking-controlplane): expose hourly billing estimates
t0saki Jul 31, 2026
dd602bc
feat(openviking-controlplane): add adaptive terminal views
t0saki Jul 31, 2026
60d3f8b
fix(openviking-controlplane): align user management contracts
t0saki Jul 31, 2026
1bba701
fix(openviking-controlplane): preserve model credentials on update
t0saki Jul 31, 2026
94f50ed
fix(openviking-controlplane): remove unsupported version updates
t0saki Jul 31, 2026
1829be7
fix(openviking-controlplane): simplify collection creation
t0saki Aug 20, 2026
e8434f4
fix(openviking-controlplane): replay collection credentials on update
t0saki Aug 20, 2026
58c0fc3
feat(openviking-controlplane): allow overwriting the model key on update
t0saki Aug 20, 2026
b125664
fix(openviking-controlplane): pin the MCP SDK below 2.0
t0saki Aug 21, 2026
e96002a
Merge branch 'main' into feat/openviking-controlplane-enterprise-tier
t0saki Aug 26, 2026
3529103
feat(openviking-controlplane): support stateless streamable HTTP tran…
t0saki Aug 26, 2026
107e577
chore(openviking-controlplane): bump version to 0.2.0 and document Py…
t0saki Aug 26, 2026
88ebc88
feat(openviking-controlplane): resolve AgentPlan credentials per request
t0saki Aug 26, 2026
970d1f1
feat(openviking-controlplane): upgrade to MCP protocol 2026-07-28 (mc…
t0saki Aug 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 144 additions & 6 deletions server/mcp_server_openviking_controlplane/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,27 @@ MCP server **and** CLI for the OpenViking control plane (topapi) — manage OV
libraries (`Collection`). Both front-ends share one core (`client.py`), so a tool
added once is available from MCP and the CLI alike.

Covers the 6 core control-plane Actions:
Covers 11 collection lifecycle, billing, and user-management Actions:

| Action | MCP tool | CLI command |
|---|---|---|
| `ListOpenVikingCollections` | `list_collections` | `ov-cp list` |
| `CreateOpenVikingCollection` | `create_collection` ⚠️ | `ov-cp create` |
| `GetOpenVikingCollection` | `get_collection` | `ov-cp get <rid>` |
| `UpdateOpenVikingCollection` | `update_collection` | `ov-cp update <rid>` |
| `DeleteOpenVikingCollection` | `delete_collection` ⚠️ | `ov-cp delete <rid>` |
| `GetOpenVikingUsage` | `get_usage` | `ov-cp usage <rid>` |
| `GetOpenVikingCollectionUserAccess` | `get_collection_api_key` | `ov-cp api-key <rid>` |
| `AccessOpenVikingApiKey` (`/GetOpenVikingCollectionUserAccess`) | `get_collection_api_key` | `ov-cp api-key <rid>` |
| `ListOpenVikingUser` (`/ListOpenVikingCollectionUser`) | `list_collection_users` | `ov-cp user list <rid>` |
| `RegisterOpenVikingUser` | `register_collection_user` | `ov-cp user register <rid>` |
| `UpdateOpenVikingUser` | `update_collection_user` | `ov-cp user update <rid> <uid>` |
| `DeleteOpenVikingUser` | `delete_collection_user` ⚠️ | `ov-cp user delete <rid> <uid>` |

The `user *` actions manage the multiple users of an enterprise-tier library; they
require the AgentPlan key to be **associated with the target library**. A user's
`ApiKey` from `user list` is **masked** — fetch a selected user's plaintext
data-plane key via `api-key <rid> --user-id <uid>`. Newly registered users always
have role `user`; `user update` currently supports API Key rotation only.

## Endpoint

Expand Down Expand Up @@ -52,6 +63,12 @@ later without touching the rest.
| Control-plane endpoint (base URL) | `VIKING_ENDPOINT` | `--endpoint` / `-e` | `https://api.vikingdb.cn-beijing.volces.com/openviking` |
| AgentPlan ApiKey | `AGENTPLAN_API_KEY` | `--api-key` / `-k` | — (required) |
| Default project | `OPENVIKING_PROJECT` | `--project` | `default` |
| Extra request headers | `VIKING_EXTRA_HEADERS` | `--header` / `-H` (repeatable) | — |

`VIKING_EXTRA_HEADERS` is a comma-separated list of `Key: Value` pairs; `--header`
takes one pair and may be repeated (CLI wins over env). Both are merged onto every
request — useful for swim-lane routing, e.g. `-H 'x-tt-env: lujiakun'`. The
`Authorization` and `Content-Type` headers are protected and cannot be overridden.

## CLI usage

Expand All @@ -66,24 +83,103 @@ uv run ov-cp list
uv run ov-cp get <ResourceID>
uv run ov-cp usage <ResourceID>
uv run ov-cp api-key <ResourceID>
uv run ov-cp api-key <ResourceID> --user-id xiaohong

# create (consumes paid quota; with source=agentplan only --name is needed —
# model names default, and the model ApiKey falls back to the configured key)
# create (consumes paid quota; always uses the AgentPlan model path and the
# configured AgentPlan key; model source/parameters and image version are hidden)
uv run ov-cp create --name my_kb

# create an enterprise-tier library (higher capacity, enterprise billing rates)
uv run ov-cp create --name my_kb --version enterprise

# billing (--pay-type): who pays for the library — orthogonal to --version,
# which only sets the rate. Omitted => defaults to agentplan_personal (AFP
# deduction from the account's personal AgentPlan). volc_pay (Volcano
# pay-as-you-go, billed to the Volcano account) must be chosen explicitly.
# ⚠️ Enterprise seat keys must not rely on the default (no personal plan =>
# deduction fails and the library is disabled) — pass agentplan_enterprise
# + --seat-id.
uv run ov-cp create --name my_kb # = --pay-type agentplan_personal
uv run ov-cp create --name my_kb --pay-type volc_pay
uv run ov-cp create --name my_kb --version enterprise \
--pay-type agentplan_enterprise --seat-id seat-2026xxxx
# --seat-id: the enterprise seat that pays. Copy it manually from the Ark
# console seat-management page — the server does NOT verify the seat exists;
# a typo only surfaces at the next hourly deduction, disabling the library.

# update mutable fields (only the flags you pass change);
# also switches billing (volc_pay <-> AgentPlan, or re-bind a seat)
uv run ov-cp update <ResourceID> --description "new description"
uv run ov-cp update <ResourceID> --pay-type volc_pay
# overwrite the library's AgentPlan MODEL credential (VLM and Embedding share it);
# the library's other credentials are replayed unchanged
uv run ov-cp update <ResourceID> --model-api-key ark-xxxxxxxx
# Without --model-api-key no model configuration is sent. If the control plane
# still rebuilds both models on every update, `update` replays the library's
# own credentials once and says so in the response Note; a credential with no
# ApiKeyID (other than AgentPlan) cannot be replayed and the update is refused.

# manage users of an enterprise-tier library (key must be associated with it)
uv run ov-cp user list <ResourceID>
uv run ov-cp user list <ResourceID> --role user --page 1 --limit 20
uv run ov-cp user register <ResourceID> xiaohong
uv run ov-cp user update <ResourceID> xiaohong --regenerate-key
uv run ov-cp user delete <ResourceID> xiaohong --yes

# delete (irreversible)
uv run ov-cp delete <ResourceID> --yes
```

When stdout is a terminal, `--output auto` (the default) renders structured
Rich views: tables for collection/user lists, sectioned detail panels for
`get`/`usage`, compact success cards for mutations, and a warning panel for
plaintext API keys. Piping or redirecting automatically keeps standard JSON:

```bash
uv run ov-cp list # Rich table in a terminal
uv run ov-cp list | jq '.Collections' # standard JSON
uv run ov-cp --json list # force standard JSON
uv run ov-cp --output json-compact list
uv run ov-cp --output pretty list # force the terminal view
```

`usage` preserves the backend's legacy `EstimatedCosts` field and also returns
`EstimatedBilling` with an explicit hourly period and CNY unit. For collections
paid by AgentPlan it includes the equivalent AFP deduction and payment scenario;
for `volc_pay` it reports CNY only.

Flags override env. The endpoint defaults to the public gateway; override it only
for testing (e.g. against a port-forward) with `-e` / `VIKING_ENDPOINT` —
`uv run ov-cp -e http://localhost:18080 list`.
`ov-cp --help` works without any config.

## MCP usage (stdio / uvx)
## MCP usage (stdio / uvx / streamable HTTP)

The server defaults to **stdio** transport, so it can be launched as a subprocess by
any MCP client. Add to `.mcp.json`:
any MCP client, and can also be served over stateless streamable HTTP behind a
gateway. Add to `.mcp.json`:

### Install from PyPI

```json
{
"mcpServers": {
"openviking-controlplane": {
"command": "uvx",
"args": [
"--from",
"mcp-server-openviking-controlplane>=0.2.0",
"mcp-server-openviking-controlplane"
],
"env": {
"AGENTPLAN_API_KEY": "ark-xxxxxxxx"
}
}
}
}
```

### Install from source

```json
{
Expand Down Expand Up @@ -120,6 +216,48 @@ For local development point it at your checkout instead:
}
```

### Streamable HTTP (stateless)

```bash
mcp-server-openviking-controlplane --transport streamable-http
# -> http://0.0.0.0:8000/mcp
```

Stateless is the default: every request carries its own context, so no request
depends on a prior `Mcp-Session-Id` and the process can be scaled horizontally
behind a gateway.

The server speaks MCP protocol revision **2026-07-28** — the per-request-envelope
revision, reached via `server/discover` rather than an `initialize` handshake — and
still negotiates the older handshake revisions (down to `2024-11-05`) for clients
that ask for them. This requires the mcp SDK 2.x line.

| Env var | Meaning | Default |
|---|---|---|
| `MCP_SERVER_HOST` | HTTP bind address | `0.0.0.0` |
| `MCP_SERVER_PORT` | HTTP port (`PORT` is still honoured) | `8000` |
| `STREAMABLE_HTTP_PATH` | Mount path for streamable HTTP | `/mcp` |
| `STATLESS_HTTP` | Enable stateless HTTP (`STATELESS_HTTP` also works) | `true` |

> Binding `127.0.0.1` makes the MCP SDK enable DNS-rebinding protection, which
> only allows localhost `Host` headers — a gateway-forwarded request would then be
> rejected. Keep the `0.0.0.0` default when running behind one.

#### Credentials over HTTP

Under HTTP transports the AgentPlan ApiKey is resolved **per request**, so one
process can serve several callers:

| Source | Precedence |
|---|---|
| `X-AgentPlan-Api-Key` header | 1 (highest) |
| `Authorization: Bearer <key>` header | 2 |
| `AGENTPLAN_API_KEY` env var | 3 (fallback) |

Only the `Bearer` scheme is read from `Authorization`; any other scheme is ignored
and the env var is used instead, so a gateway that terminates its own auth there
does not leak its credential into a caller's collection.

Run with SSE instead via `mcp-server-openviking-controlplane --transport sse`.

## Agent skill
Expand Down
136 changes: 130 additions & 6 deletions server/mcp_server_openviking_controlplane/README_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,26 @@ OpenViking 控制面(topapi)的 MCP Server **与** CLI —— 用于管理 O
(`Collection`)。两个前端共用同一套核心(`client.py`),新增一个能力即可同时被 MCP
和 CLI 使用。

覆盖 6 个核心控制面 Action:
覆盖 11 个库生命周期、计费与用户管理 Action:

| Action | MCP tool | CLI 命令 |
|---|---|---|
| `ListOpenVikingCollections` | `list_collections` | `ov-cp list` |
| `CreateOpenVikingCollection` | `create_collection` ⚠️ | `ov-cp create` |
| `GetOpenVikingCollection` | `get_collection` | `ov-cp get <rid>` |
| `UpdateOpenVikingCollection` | `update_collection` | `ov-cp update <rid>` |
| `DeleteOpenVikingCollection` | `delete_collection` ⚠️ | `ov-cp delete <rid>` |
| `GetOpenVikingUsage` | `get_usage` | `ov-cp usage <rid>` |
| `GetOpenVikingCollectionUserAccess` | `get_collection_api_key` | `ov-cp api-key <rid>` |
| `AccessOpenVikingApiKey`(路径 `/GetOpenVikingCollectionUserAccess`) | `get_collection_api_key` | `ov-cp api-key <rid>` |
| `ListOpenVikingUser`(路径 `/ListOpenVikingCollectionUser`) | `list_collection_users` | `ov-cp user list <rid>` |
| `RegisterOpenVikingUser` | `register_collection_user` | `ov-cp user register <rid>` |
| `UpdateOpenVikingUser` | `update_collection_user` | `ov-cp user update <rid> <uid>` |
| `DeleteOpenVikingUser` | `delete_collection_user` ⚠️ | `ov-cp user delete <rid> <uid>` |

`user *` 系列管理企业版库的多用户,要求 AgentPlan key **与目标库已关联**。`user list`
返回的用户 `ApiKey` 是**掩码**,取指定用户的明文数据面 key 使用
`api-key <rid> --user-id <uid>`。新注册用户的角色固定为 `user`;`user update`
当前只支持重生 API Key。

## 端点

Expand Down Expand Up @@ -48,6 +58,11 @@ Action 在 **path** 里(不走 `?Action=&Version=` query)。请求体是该
| 控制面 endpoint(base URL) | `VIKING_ENDPOINT` | `--endpoint` / `-e` | `https://api.vikingdb.cn-beijing.volces.com/openviking` |
| AgentPlan ApiKey | `AGENTPLAN_API_KEY` | `--api-key` / `-k` | —(必填) |
| 默认 project | `OPENVIKING_PROJECT` | `--project` | `default` |
| 额外请求头 | `VIKING_EXTRA_HEADERS` | `--header` / `-H`(可重复) | — |

`VIKING_EXTRA_HEADERS` 是逗号分隔的 `Key: Value` 列表;`--header` 每次带一对、可重复
(CLI 优先于环境变量)。两者合并后加到每个请求上,常用于泳道路由,例如
`-H 'x-tt-env: lujiakun'`。`Authorization`、`Content-Type` 为受保护头,不可覆盖。

## CLI 用法

Expand All @@ -62,22 +77,94 @@ uv run ov-cp list
uv run ov-cp get <ResourceID>
uv run ov-cp usage <ResourceID>
uv run ov-cp api-key <ResourceID>
uv run ov-cp api-key <ResourceID> --user-id xiaohong

# 建库(消耗付费配额;source=agentplan 时只需 --name
# 模型名取默认、模型 ApiKey 回落到配置的 key
# 建库(消耗付费配额;固定使用 AgentPlan 模型路径和已配置的 AgentPlan key
# 不开放模型来源、模型参数、模型鉴权与 OpenViking 镜像版本
uv run ov-cp create --name my_kb

# 建企业版库(容量更高,按企业版费率计费)
uv run ov-cp create --name my_kb --version enterprise

# 计费方式(--pay-type):库由谁付钱——与 --version 正交(--version 只决定费率)。
# 不传时默认 agentplan_personal(用账号的个人版 AgentPlan 做 AFP 抵扣);
# volc_pay(火山官网按量,费用计入火山账号账单)必须显式指定。⚠️ 企业版席位 key
# 不要依赖默认值(账号没有个人版套餐时抵扣会失败、库被停用),请显式传
# agentplan_enterprise + --seat-id。
uv run ov-cp create --name my_kb # 等价于 --pay-type agentplan_personal
uv run ov-cp create --name my_kb --pay-type volc_pay
uv run ov-cp create --name my_kb --version enterprise \
--pay-type agentplan_enterprise --seat-id seat-2026xxxx
# --seat-id:付费的企业版席位,需自行从方舟控制台「席位管理」页复制——
# 服务端不校验席位是否存在,填错要到下一个小时抵扣时才暴露(届时库被停用)。

# 更新库可变字段(只改传入的字段);也用于切换计费方式 / 换绑席位
uv run ov-cp update <ResourceID> --description "新描述"
uv run ov-cp update <ResourceID> --pay-type volc_pay
# 覆盖该库的 AgentPlan 模型凭证(VLM 与 Embedding 共用同一把 key),
# 库里其它凭证按原样重放,不受影响
uv run ov-cp update <ResourceID> --model-api-key ark-xxxxxxxx
# 不带 --model-api-key 时不会发送任何模型配置。若控制面仍在每次更新时重建模型,
# `update` 会读回该库自身的凭证重放一次,并在响应 Note 中说明;除 AgentPlan 外,
# 没有 ApiKeyID 的凭证无法重放,此时更新会被拒绝而不是猜测。

# 管理企业版库的用户(key 需与该库已关联)
uv run ov-cp user list <ResourceID>
uv run ov-cp user list <ResourceID> --role user --page 1 --limit 20
uv run ov-cp user register <ResourceID> xiaohong
uv run ov-cp user update <ResourceID> xiaohong --regenerate-key
uv run ov-cp user delete <ResourceID> xiaohong --yes

# 删库(不可逆)
uv run ov-cp delete <ResourceID> --yes
```

默认的 `--output auto` 在 stdout 连接终端时使用 Rich 结构化视图:
库/用户列表显示为表格,`get`/`usage` 显示为分区详情卡片,写操作显示精简成功卡片,
明文 API Key 则显示敏感信息警告。管道和重定向会自动保持标准 JSON:

```bash
uv run ov-cp list # 终端内显示 Rich 表格
uv run ov-cp list | jq '.Collections' # 标准 JSON
uv run ov-cp --json list # 强制标准 JSON
uv run ov-cp --output json-compact list
uv run ov-cp --output pretty list # 强制终端视图
```

`usage` 保留后端原有的 `EstimatedCosts` 字段,同时新增 `EstimatedBilling`,
明确费用为每小时 CNY 估值。AgentPlan 支付的库还会返回对应的 AFP 抵扣量和
支付场景;`volc_pay` 只返回 CNY。

命令行参数优先于环境变量。端点默认指向公网网关;仅在测试时(如指向 port-forward)才用
`-e` / `VIKING_ENDPOINT` 覆盖:`uv run ov-cp -e http://localhost:18080 list`。
`ov-cp --help` 不需要任何配置即可运行。

## MCP 用法(stdio / uvx)
## MCP 用法(stdio / uvx / streamable HTTP)

Server 默认 **stdio** 传输,可被任意 MCP 客户端作为子进程拉起;也可以以无状态
streamable HTTP 的方式挂在网关后面。`.mcp.json` 配置:

### 从 PyPI 安装

```json
{
"mcpServers": {
"openviking-controlplane": {
"command": "uvx",
"args": [
"--from",
"mcp-server-openviking-controlplane>=0.2.0",
"mcp-server-openviking-controlplane"
],
"env": {
"AGENTPLAN_API_KEY": "ark-xxxxxxxx"
}
}
}
}
```

Server 默认 **stdio** 传输,可被任意 MCP 客户端作为子进程拉起。`.mcp.json` 配置:
### 从源码安装

```json
{
Expand Down Expand Up @@ -114,6 +201,43 @@ Server 默认 **stdio** 传输,可被任意 MCP 客户端作为子进程拉起
}
```

### Streamable HTTP(无状态)

```bash
mcp-server-openviking-controlplane --transport streamable-http
# -> http://0.0.0.0:8000/mcp
```

默认即无状态:每个请求自带完整上下文,不依赖上一次返回的 `Mcp-Session-Id`,
因此进程可以在网关后面水平扩缩。

Server 支持 MCP 协议修订版 **2026-07-28**——即"每请求信封"修订版,通过
`server/discover` 探测而非 `initialize` 握手协商——同时仍可与要求旧握手修订版
(最低 `2024-11-05`)的客户端协商。该能力需要 mcp SDK 2.x。

| 环境变量 | 含义 | 默认值 |
|---|---|---|
| `MCP_SERVER_HOST` | HTTP 监听地址 | `0.0.0.0` |
| `MCP_SERVER_PORT` | HTTP 端口(`PORT` 仍然有效) | `8000` |
| `STREAMABLE_HTTP_PATH` | streamable HTTP 挂载路径 | `/mcp` |
| `STATLESS_HTTP` | 是否启用无状态 HTTP(`STATELESS_HTTP` 亦可) | `true` |

> 监听 `127.0.0.1` 会让 MCP SDK 自动开启 DNS-rebinding 保护,只放行 localhost 的
> `Host` 头——网关转发过来的请求会被拒。挂在网关后面时请保持 `0.0.0.0` 默认值。

#### HTTP 下的凭证来源

HTTP 传输下 AgentPlan ApiKey **按请求解析**,因此单个进程可以服务多个调用方:

| 来源 | 优先级 |
|---|---|
| `X-AgentPlan-Api-Key` 请求头 | 1(最高) |
| `Authorization: Bearer <key>` 请求头 | 2 |
| `AGENTPLAN_API_KEY` 环境变量 | 3(兜底) |

`Authorization` 只读取 `Bearer` scheme,其他 scheme 一律忽略并回落到环境变量——
这样网关若在该头上终结自己的鉴权,其凭证不会被写进调用方的库。

需要 SSE 时:`mcp-server-openviking-controlplane --transport sse`。

> ⚠️ `create_collection` / `delete_collection` 会创建/销毁**付费**资源,且已暴露为 MCP
Expand Down
Loading
Loading