fix(mcp): 对齐 source 字段的大小写归一化并补齐回归测试 - #3164
Merged
Merged
Conversation
The visual editor lowercases `type`, `transport`, and `source` before
matching, but the core validator and parser still matched `source`
case-sensitively. A hand-written `{"source": "Remote", "url": ...}` was
therefore rendered happily by the form and then rejected on save with
"Server 'x' has unsupported 'source' value: 'Remote'", while
`parse_cursor_format` dropped the server with a warning.
Route `source` through the same token normalizer as `type`/`transport`,
and rename the shared helper to `normalized_token` since it now also
covers the `local`/`remote` source vocabulary. Accepted spellings still
normalize to the canonical persisted values.
Also adds the regression coverage missing from the previous change:
mixed-case `transport`/`source` validation, parsing, and canonical write
assertions in mcp_contracts, plus camelCase `type` and mixed-case
`source` fixtures in the visual editor round-trip table.
Co-authored-by: OpenBitFun <318544290+bitfun-ai@users.noreply.github.com>
wgqqqqq
force-pushed
the
wgq/fix-mcp-source-token-case
branch
from
September 21, 2026 02:12
a0335bd to
561bdc0
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Follow-up to #3156 (
fix(mcp): 兼容驼峰式 streamableHttp 传输类型), which normalized thetype/transporttokens but leftsourcebehind.sourcetoken through the same trim + lowercase normalizer astype/transport, in both core paths:json_config::normalize_sourceandcursor_format::parse_source.normalized_transport_tokentonormalized_token, since it now covers thetype/transport/sourcevocabularies (includinglocal/remote).source/transportvalidation, parsing, and canonical-write assertions inmcp_contracts.rs, plus camelCasetypeand mixed-casesourcefixtures in the visual editor round-trip table.mcp_contracts.rs(array_width).Root cause: the visual editor lowercases
type,transport, andsourcebefore matching (transportFromConfiginmcpConfigForm.ts), but the core validator and parser still matchedsourcecase-sensitively. So{ "mcpServers": { "docs": { "source": "Remote", "url": "https://example.com/mcp" } } }rendered fine in the form, then failed on save with
Server 'docs' has unsupported 'source' value: 'Remote', andparse_cursor_formatlogged a warning and dropped the server on reload. Before #3156 the form rejected the same input (advancedRequired), so this cross-surface divergence was introduced by that change.Type and Areas
Type: bug fix (follow-up to a merged fix)
Areas: Rust core (
services-integrationsMCP config), Web UI testsMotivation / Impact
A config the visual editor accepts must survive a save round trip. After #3156 that held for
typeandtransportbut not forsource: a hand-written or third-party-emitted"source": "Remote"was accepted in the UI and then rejected by the core, or silently dropped on reload. Both surfaces now share one normalization rule.The canonical persisted tokens are unchanged (
stdio/sse/streamable-http), so no stored configuration changes meaning and there is no migration. No user-facing strings changed.Verification
cargo test -p openbitfun-services-integrations --no-default-features --features mcp --test mcp_contracts— passed, 49 passed / 0 failed (48 before this PR)cargo test -p openbitfun-services-integrations --no-default-features --features mcp --test mcp_streamable_http_contracts— passed, 3 passedvitest run src/infrastructure/config/components/mcpConfigForm.test.ts(fromsrc/web-ui) — passed, 29 passed (27 before this PR)normalize_source/parse_sourceto case-sensitive matching makes the new test fail with'{"source":"REMOTE","transport":"SSE",...}' must validate: Server 'case' has unsupported 'source' value: 'REMOTE', so the test pins the behavior rather than just passing.pnpm run fmt:rs— applied; the three changed Rust files are rustfmt-clean.Build Impact,Repository Object Sizes, andShell Deploy Scriptspassed;Rust Build Check,Frontend Build, andCLI Testswere still pending when this description was written.Reviewer Notes
source) instead of removing the form's.toLowerCase().sourceis OpenBitFun's own read-side vocabulary —config_to_cursor_formatwritestypeand neversource— so widening it cannot regress a user-visible behavior, and it matches the fix(mcp): 兼容驼峰式 streamableHttp 传输类型 #3156 direction. If maintainers prefer strictsourcecasing, the alternative is a one-line removal inmcpConfigForm.ts; I can switch on request.normalized_tokenispub(super)inside the privatejson_configmodule and is not re-exported frommcp::config, so the rename has no compatibility impact.claude-code/opencode/codex/dsh), which match their own upstream formats and are out of scope per fix(mcp): 兼容驼峰式 streamableHttp 传输类型 #3156; the CLIopenbitfun mcp add --typevocabulary (local | remote, whose TUI path only emits wizard-generated canonical values, so there is no reproducible camelCase input); andMcpServerMutation'sMcpTransport, a separate serde kebab-case wire contract used by app-server / peer.Checklist
Generated with OpenBitFun