fix(mcp): add auth interceptor with channel user_id and keep header propagation to mcp tools#3294
Conversation
Co-authored-by: Cursor <cursoragent@cursor.com>
|
@zhongli-sz thanks for your contribution. Please add a unit test to check the user_id injection. |
There was a problem hiding this comment.
Pull request overview
This PR improves identity and header propagation across non-web (channel) and MCP tool-call paths so interceptors and downstream MCP servers can consistently access caller context.
Changes:
- Inject
user_idinto channel-triggered run context (fromInboundMessage.user_id) to avoid default/fallback identity in channel flows. - Ensure
user_idis merged into the gateway runtime context (config["context"]) when applyingbody.contextoverrides. - Forward interceptor-injected headers through MCP stdio tool calls by passing them via
meta.headers, and avoid internal system-role requests overwriting user context.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
| backend/app/channels/manager.py | Adds user_id to channel run context so channel flows preserve caller identity. |
| backend/app/gateway/services.py | Propagates user_id into runtime context; skips stamping user_id for internal system-role users. |
| backend/packages/harness/deerflow/mcp/tools.py | Forwards interceptor-injected headers through MCP stdio tool calls using meta.headers. |
| { | ||
| "thread_id": thread_id, | ||
| "user_id": msg.user_id, | ||
| }, |
| if "user_id" in context and isinstance(runtime_context, dict): | ||
| runtime_context.setdefault("user_id", context["user_id"]) |
| if "user_id" in context and isinstance(runtime_context, dict): | ||
| runtime_context.setdefault("user_id", context["user_id"]) |
| if getattr(user, "system_role", None) == "internal": | ||
| return |
| # Preserve interceptor-injected headers for stdio MCP calls by | ||
| # forwarding them through MCP call meta. | ||
| call_kwargs: dict[str, Any] = {} | ||
| if request.headers: | ||
| call_kwargs["meta"] = {"headers": dict(request.headers)} | ||
| return await session.call_tool(request.name, request.args, **call_kwargs) |
|
@zhongli-sz, thanks for your contribution. Please check out the review comment of Copilot. |
…-id-interceptor-mcp
…n tests Normalize external channel user ids into filesystem-safe runtime context while preserving raw channel_user_id, and document gateway user_id propagation semantics. Add regression coverage for channel user_id context mapping, gateway user_id precedence/internal-role behavior, and MCP interceptor header forwarding via meta.headers. Co-authored-by: Cursor <cursoragent@cursor.com>
|
@WillemJiang Thanks for the detailed review. I’ve addressed the comments by adding regression tests for user_id propagation and MCP interceptor header forwarding, updated the docstring, and handled channel user_id safety for filesystem-scoped runtime context. Please take another look. |
|
@zhongli-sz, here are some suggestions for this PR.
|
背景 / Background
在非 Web 鉴权链路(如 飞书 channel 消息)中,
user_id未正确进入运行时上下文,导致 interceptor 侧拿到默认值(default);同时 interceptor 注入的 header 在 MCP 工具调用时未透传,导致下游工具无法读取到鉴权/上下文信息。In non-web auth flows (e.g., feishu channel messages),
user_idnot be correctly propagated into runtime context, causing interceptor-side fallback/default values. Also, headers injected by interceptors were not forwarded during MCP tool calls, so downstream tools could not consume auth/context headers.本次改动 / What Changed
在
ChannelManager构建上下文时注入user_id(来自msg.user_id),避免 channel 场景丢失用户身份。Inject
user_idfrommsg.user_idwhen building run context inChannelManager, ensuring channel flows keep caller identity.在网关上下文合并逻辑中,确保
user_id写入 runtime context(不仅限于 configurable 字段)。Ensure
user_idis merged into runtime context in gateway override merge logic (not only configurable fields).在
inject_authenticated_user_context中跳过internal系统角色,避免内部调用覆盖/污染用户上下文。Skip
internalsystem-role users ininject_authenticated_user_contextto avoid overriding/polluting user context for internal calls.在 MCP 工具调用时透传 interceptor header:将
request.headers放入meta.headers传给session.call_tool(...)。Forward interceptor headers in MCP tool calls by passing
request.headersviameta.headerstosession.call_tool(...).影响 / Impact
修复 interceptor 获取到默认
user_id的问题,用户身份在 channel 链路中可正确透传。修复 interceptor 注入 header 在 MCP 工具调用中被忽略的问题。
降低鉴权上下文丢失风险,提升跨链路行为一致性。
Fixes default/fallback
user_idissue in channel flows.Fixes dropped interceptor headers during MCP tool invocation.
Reduces auth-context loss and improves cross-path consistency.
变更文件 / Files Changed
backend/app/channels/manager.pybackend/app/gateway/services.pybackend/packages/harness/deerflow/mcp/tools.py测试建议 / Test Plan
[✅] Channel 消息触发一次 MCP 工具调用,确认工具侧可读取到正确
user_id(非default)。[✅] 验证 interceptor 注入 header 后,MCP 工具端可收到对应 header。
[✅ ] 验证 internal 系统角色请求不会覆盖普通用户上下文。
[✅] 回归检查常规 Web 鉴权链路不受影响。
[✅] Trigger an MCP tool call from a channel message and verify
user_idis notdefault.[✅] Verify interceptor-injected headers are visible in MCP tool side.
[✅] Verify internal system-role requests do not override user context.
[✅] Regression check normal web-auth flow remains unaffected.