Skip to content

feat(connectors): per-user identity templates in MCP connector url and headers - #1845

Open
cy1990 wants to merge 1 commit into
TencentCloud:developfrom
cy1990:feature/connector-dynamic-user-header
Open

cy1990 wants to merge 1 commit into
TencentCloud:developfrom
cy1990:feature/connector-dynamic-user-header

Conversation

@cy1990

@cy1990 cy1990 commented Oct 9, 2026 •

Copy link
Copy Markdown

Summary

一份共享连接器只有一套静态 url / header,多租户 MCP 服务器无从判断这次调用属于哪个最终用户——所有用户塌进同一个服务端会话/租户。本 PR 让自定义 MCP 服务器的 url 路径与 header 值可以引用当前请求用户:${octop.user_id}、${octop.username}。

模板原样入库:管理员仍然只配置一份连接器,dashboard 里看到的也是模板本身;替换只发生在 Octop 为某个具体用户物化连接配置的那一刻。于是同一份共享配置下,每个用户带着自己的身份访问下游。

Target branch

  • Base is develop (feature / fix — default)
  • Base is main (release/* or hotfix/* only)

Type of change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation
  • Refactor / chore
  • Release / hotfix

Test plan

质量门禁按 uv.lock 锁定的工具版本复现(ruff 0.15.20 / mypy 2.1.0,与 make lint / make typecheck 同命令),Python 3.12、工作区为 rebase 到 origin/develop 后的代码:

ruff check src tests                  # All checks passed!
ruff format --check src tests         # 1169 files already formatted
mypy src/octop                        # Success: no issues found in 565 source files
pytest tests/unit/connectors -q       # 234 passed in 415.18s
pytest tests/unit/agents -q -k "mcp or connector or shared"   # 16 passed, 685 deselected

即 make lint 与 make typecheck 两个 step 在本地已是干净的,没有任何遗留告警需要维护者甄别。

新增 tests/unit/connectors/test_user_scope_headers.py(16 例)覆盖:header 与 url 两种模板的存原文/取替换、用户名优先与数字 id 回落、CR/LF 与非法字符清洗、超预算不截断、host 段占位符拒绝、未知 token 拒绝、探测与真实调用同路、无法解析的服务器被跳过而不影响其余工具面。

除单测外还做了真实容器端到端(多用户 + 一份 shared: true 连接器 + 真实多租户 MCP 服务器):

  • 同一份共享配置下,两个用户各自解析出不同的租户键,下游审计日志分别记录两个键;
  • 两人分别从各自的登录入口完成认证后,同一工具调用返回的是各自账号的数据;
  • 路径形态的键与 header 形态的键互不污染(在一个形态下登录不会让另一个形态变成已登录)。

make all 未完整跑通:它额外要求前端构建(dashboard/node_modules,本机无该目录,无法构建与类型检查)。本次前端改动只有两个 locale 新增键与连接器卡片的一处提示节点,未做构建验证;后端部分(ruff / mypy / pytest)已按上述 CI 同版本命令全量跑过。

Checklist

  • Updated CHANGELOG.md (if user-facing)
  • README / docs updated (if needed) — docs/api.md 自定义 MCP 一节补充占位符与约束
  • make all passes locally(原因见 Test plan:前端依赖缺失)

Refs #1827

…d headers

A shared connector carries one static target and header set, so a multi-tenant
MCP server cannot tell which end user a call belongs to: every user collapses
onto the same tenant session. Connector url paths and header values may now
reference the requesting user through ${octop.user_id} or ${octop.username}.
Templates are stored verbatim (admins still configure one connector, the
dashboard keeps showing the template) and substituted only when Octop
materializes the connection spec for a given user, so each user presents their
own identity on the same shared configuration.

Safety properties, chosen because a wrong identity is worse than no identity:

- Substituted values are constrained to the charset and length the downstream
  tenant resolver accepts ([A-Za-z0-9._-], <= 64) and are CR/LF-free. A value
  outside that envelope is not rejected by the server, it is silently remapped
  to the default tenant, which would leak one user's data into another's.
- Over-budget or degenerate names fall back to the numeric user id instead of
  being truncated, so two long names can never collide on one key.
- A literal template that cannot fit at all raises, and the unresolvable server
  is dropped for that user rather than sent with an ambiguous identity.
- Placeholders in the url are only allowed below the host, so templating cannot
  be used to redirect a request to another server; the host is validated at
  save time against a token-filled preview while the template is kept stored.
- Unknown tokens are rejected at save and probe time; probes take the same
  substitution path as real calls, so what passes the test button is what runs.

When a username cannot be resolved the placeholders degrade to the numeric id
and a warning is logged, so the degradation is observable instead of silent.
The connector editor documents both placeholders next to the header field.

refs TencentCloud#1827
@cy1990
cy1990 force-pushed the feature/connector-dynamic-user-header branch from 783d0bc to b6bba9f Compare October 9, 2026 07:43
@cy1990

cy1990 commented Oct 9, 2026

Copy link
Copy Markdown
Author

本地已按 uv.lock 锁定的工具版本复现 CI 的两个前置 step(fork PR 的 workflow 需要维护者批准才会开跑,这里先附上等价结果,便于判断是否值得放行):

ruff 0.15.20  ruff check src tests               All checks passed!
ruff 0.15.20  ruff format --check src tests      1169 files already formatted
mypy 2.1.0    mypy src/octop                     Success: no issues found in 565 source files
pytest        tests/unit/connectors              234 passed
pytest        tests/unit/agents -k "mcp or connector or shared"   16 passed / 685 deselected

另:上一版提交里 src/octop/api/routers/connectors.py 有一处换行不符合当前 ruff 的 formatter 输出(早期版本工具查不出,按 lock 版本复跑才暴露),已在 b6bba9f 修正,现在 make lint 全量干净。

live-tests job 对 fork PR 本就会被条件跳过,本次改动不依赖任何 secrets。

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant