feat(mcp): add --scope option for OAuth and fix upstream auth flow issues#1625
feat(mcp): add --scope option for OAuth and fix upstream auth flow issues#16254riel wants to merge 2 commits intoMoonshotAI:mainfrom
Conversation
…sues Add --scope/-s repeatable option to `kimi mcp add` for OAuth servers that require specific scopes. Validated to require --auth oauth and http transport, following the existing --header/--auth guard pattern. Refactor `kimi mcp auth` to use manual transport+OAuth construction so scopes are forwarded. Add create_oauth() helper with _PatchedOAuthClient that works around three upstream fastmcp/MCP SDK issues: URL path stripping breaking RFC 8707 resource matching, redirect_handler pre-flight GET misinterpreting 400 responses, and token exchange rejecting HTTP 201. Show configured scopes in `kimi mcp list` output and pass scopes through to OAuth during runtime MCP tool loading in toolset.py.
There was a problem hiding this comment.
Pull request overview
Adds OAuth scope support to MCP server configuration and authorization, and applies upstream OAuth compatibility workarounds so OAuth flows work with providers that require scopes and/or have non-standard behaviors.
Changes:
- Add repeatable
--scope / -stokimi mcp add, persist scopes in config, and display them inmcp list. - Refactor
kimi mcp authto construct transports manually so configured scopes are forwarded into OAuth. - Update runtime MCP loading to use the same OAuth construction path for scoped OAuth servers; add CLI-focused tests covering validation, persistence, and OAuth construction.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
src/kimi_cli/cli/mcp.py |
Adds --scope, persists/displays scopes, and introduces create_oauth() with upstream OAuth workarounds; updates mcp_auth() to use manual transport construction. |
src/kimi_cli/soul/toolset.py |
Uses create_oauth() + manual transport when loading scoped OAuth MCP servers at runtime. |
tests/core/test_mcp_cli.py |
New tests for --scope validation/persistence and for OAuth/transport construction behavior in mcp_auth(). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| from fastmcp.client.transports import SSETransport, StreamableHttpTransport | ||
|
|
||
| from kimi_cli.cli.mcp import create_oauth | ||
|
|
There was a problem hiding this comment.
toolset now imports create_oauth from kimi_cli.cli.mcp. This introduces a runtime dependency on the CLI module (and its Typer app construction) from the core soul runtime. Consider moving create_oauth into a non-CLI/shared module (e.g. kimi_cli/mcp/oauth.py) and importing it from both CLI and runtime to keep layering clean and avoid accidental CLI-side imports/side effects in non-CLI contexts.
src/kimi_cli/cli/mcp.py
Outdated
| cli = typer.Typer(help="Manage MCP server configurations.") | ||
|
|
||
|
|
||
| def create_oauth(mcp_url: str, scopes: list[str] | None = None) -> Any: |
There was a problem hiding this comment.
create_oauth() is annotated as returning Any, but it always returns an OAuth instance (a _PatchedOAuthClient subclass). Returning a concrete type (e.g. OAuth) would improve type-checking and make call sites (like transport construction) safer/clearer.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f8fb87b0f9
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| transport_type = server.get("transport", "http") | ||
| transport_cls = SSETransport if transport_type == "sse" else StreamableHttpTransport | ||
| auth = create_oauth(url, scopes=scopes) |
There was a problem hiding this comment.
Preserve transport inference when
transport is unset
This new auth path hardcodes http when the config omits transport, but FastMCP's canonical config supports leaving transport empty and inferring it from the URL (notably /sse endpoints). For OAuth servers loaded from external/canonical MCP configs that rely on URL-based inference, kimi mcp auth will now choose StreamableHttpTransport instead of SSETransport, causing authorization/connect failures that did not happen in the previous Client({"mcpServers": ...}) flow.
Useful? React with 👍 / 👎.
| transport_cls = ( | ||
| SSETransport | ||
| if server_config.transport == "sse" | ||
| else StreamableHttpTransport |
There was a problem hiding this comment.
Keep URL-based transport inference in scoped OAuth loading
In the scoped OAuth branch, transport selection is reduced to transport == "sse" else HTTP, which skips FastMCP's normal inference for configs where transport is omitted. That means OAuth servers with scopes and URLs that imply SSE can be loaded with the wrong transport at runtime, while the old MCPConfig(...)->Client(...) path would infer correctly.
Useful? React with 👍 / 👎.
…tAI#1625) - Extract create_oauth() and _PatchedOAuthClient to kimi_cli/oauth.py - Fix layering violation: soul/toolset.py imports from kimi_cli.oauth instead of cli.mcp - Remove scopes gate: OAuth workarounds now apply to ALL OAuth servers, not just those with explicit scopes - Fix return type: create_oauth() now returns OAuth instead of Any - Add E2E tests for OAuth scopes support
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: af9cf48b19
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| auth = create_oauth(url, scopes=scopes) | ||
| transport = transport_cls(url, headers=headers, auth=auth) | ||
|
|
||
| client = fastmcp.Client(transport) |
There was a problem hiding this comment.
Route
mcp test through patched OAuth client
mcp_auth() now builds OAuth with create_oauth() (full-path RFC8707/resource behavior), but mcp_test() still uses the legacy fastmcp.Client({"mcpServers": ...}) path. When a server has no cached token yet (or needs refresh), mcp test can drop back to the unpatched OAuth flow and fail on providers that need path-aware resource/scope handling, even though mcp auth succeeded for the same server. Reusing the same patched transport/auth construction for mcp_test would keep these commands consistent.
Useful? React with 👍 / 👎.
Related Issue
N/A — discovered when connecting to OAuth MCP servers that require specific scopes
(e.g. Supabase requires
organizations:read,projects:read, etc.). The existingkimi mcp add/kimi mcp authcommands had no way to specify or forward scopes.Description
Problem
kimi mcp addhas no--scopeoption — OAuth servers requiring specific scopescannot be configured correctly
kimi mcp authuses the dict-config path (fastmcp.Client({"mcpServers": ...}))which silently ignores any
scopesfield in the configOAuth.__init__strips the URL path, breaking RFC 8707 resource matchingredirect_handlerpre-flight GET misinterprets 400 responses as "client not found"_handle_token_responserejects HTTP 201 on token exchangeFix
CLI (
src/kimi_cli/cli/mcp.py):--scope / -srepeatable option tomcp_add(), validated to require--auth oauthand http transport (following the existing
--header/--authguard pattern)create_oauth()helper with_PatchedOAuthClientsubclass that works aroundthe three upstream issues
mcp_auth()to usecreate_oauth()with manual transport construction,so scopes are properly forwarded to the OAuth flow
mcp_list()outputRuntime (
src/kimi_cli/soul/toolset.py):create_oauth()when loading MCP tools at runtime for OAuth servers with scopesProvider compatibility
The implementation is fully provider-agnostic. Verified against:
server_urlfix impactorganizations:read)org:read,project:write)openid)Before / After
Validation
tests/core/test_mcp_cli.py:--scoperejected for stdio, rejected without--auth,rejected with non-oauth auth, guard ordering preserved
--auth oauth, no-scope still worksselection, headers forwarded, RFC 8707 server_url fix verified
ruff checkandruff formatpassChecklist
make gen-changelogto update the changelog.make gen-docsto update the user documentation.