Skip to content

fix(desktop): preserve MCP URL query suffixes#6587

Merged
wenshao merged 2 commits into
QwenLM:mainfrom
VectorPeak:codex/mcp-url-normalize-pathname
Jul 11, 2026
Merged

fix(desktop): preserve MCP URL query suffixes#6587
wenshao merged 2 commits into
QwenLM:mainfrom
VectorPeak:codex/mcp-url-normalize-pathname

Conversation

@VectorPeak

@VectorPeak VectorPeak commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

What this PR does

This PR narrows MCP source URL normalization so trailing slash cleanup only applies before the query string or fragment. Remote MCP URLs still have trailing slashes removed from the path portion, but query values and hash fragments are preserved exactly as configured.

Why it's needed

What Problem This Solves

The previous implementation normalized MCP URLs with a string-level trailing slash trim:

url.replace(/\/+$, '')

That treats the entire URL as one flat string. URLs are structured values: the path, query string, and fragment each carry different routing or authentication semantics. For a URL such as https://api.example.com/mcp?key=/, the trailing slash belongs to the query parameter value, not the path. Removing it changes the configured endpoint semantics from ?key=/ to ?key=.

That distinction matters for remote MCP sources because the configured mcp.url is passed into the runtime connection path. Query values can carry tokens, tenant identifiers, signed request material, compatibility flags, or provider-specific routing parameters. Fragments can also be used by some integrations as client-side routing or endpoint metadata. A normalizer that trims the whole string can therefore corrupt a valid remote MCP URL before the client attempts to connect.

Changes

This PR keeps the existing path cleanup behavior, but narrows where it applies. Instead of trimming trailing slashes from the entire URL string, normalizeMcpUrl() now finds the first query or fragment boundary and trims only the path prefix before that boundary.

 export function normalizeMcpUrl(url: string): string {
-  return url.replace(/\/+$, '');
+  const suffixIndex = url.search(/[?#]/);
+  if (suffixIndex === -1) {
+    return url.replace(/\/+$/, '');
+  }
+
+  return (
+    url.slice(0, suffixIndex).replace(/\/+$, '') + url.slice(suffixIndex)
+  );
 }

The intentionally unchanged behavior is the existing path suffix normalization: https://api.example.com/mcp/ still becomes https://api.example.com/mcp. The changed behavior is limited to preserving search and hash content exactly as configured.

Evidence

The failure can be reproduced with the old string-level normalizer:

https://api.example.com/mcp/       -> https://api.example.com/mcp
https://api.example.com/mcp?key=/  -> https://api.example.com/mcp?key=
https://api.example.com/mcp#/      -> https://api.example.com/mcp#

After this change, only the path portion is trimmed:

https://api.example.com/mcp/       -> https://api.example.com/mcp
https://api.example.com/mcp?key=/  -> https://api.example.com/mcp?key=/
https://api.example.com/mcp#/      -> https://api.example.com/mcp#/

The added focused test covers the key URL shapes:

cd packages/desktop
bun test packages/shared/src/sources/__tests__/server-builder-url.test.ts

It verifies path trailing slash cleanup, query values ending in /, fragments ending in /, and the legacy fallback behavior for non-parseable strings.

Possible call chain / impact

source config mcp.url
  -> SourceServerBuilder.buildMcpServer(...)
  -> normalizeMcpUrl(mcp.url)
  -> McpServerConfig.url
  -> remote MCP client connection

The same helper is also reused by MCP connection validation, so preserving URL structure benefits both runtime source setup and validation-time connection checks.

This PR does not change OAuth discovery, credential headers, stdio MCP sources, API sources, or the behavior that removes trailing slashes from the path itself. It only prevents query strings and fragments from being treated as part of the path cleanup target.

Reviewer Test Plan

How to verify

Run the focused Bun test for MCP URL normalization:

cd packages/desktop
bun test packages/shared/src/sources/__tests__/server-builder-url.test.ts

The test covers path trailing slash cleanup plus query and fragment values that legitimately end with /.

Evidence (Before & After)

N/A for screenshots; this is non-UI URL normalization behavior.

Before this change, string-level normalization produced:

https://api.example.com/mcp/       -> https://api.example.com/mcp
https://api.example.com/mcp?key=/  -> https://api.example.com/mcp?key=
https://api.example.com/mcp#/      -> https://api.example.com/mcp#

After this change, only the path portion is trimmed:

https://api.example.com/mcp/       -> https://api.example.com/mcp
https://api.example.com/mcp?key=/  -> https://api.example.com/mcp?key=/
https://api.example.com/mcp#/      -> https://api.example.com/mcp#/

Tested on

OS Status
🍎 macOS ⚪ not tested
🪟 Windows ✅ tested
🐧 Linux ✅ tested

Environment (optional)

Windows: Bun 1.3.14 installed at D:\ZXY\Dev\bun\bin. Linux: WSL Ubuntu 22.04 with Bun 1.3.14 installed at /mnt/d/ZXY/Dev/bun-linux/bin.

Validation run:

cd packages/desktop
bun test packages/shared/src/sources/__tests__/server-builder-url.test.ts
bun run typecheck:shared

Risk & Scope

  • Main risk or tradeoff: very low; this changes URL normalization for remote MCP source URLs only and keeps the existing path trailing-slash cleanup behavior.
  • Not validated / out of scope: macOS local validation, live remote MCP server calls, OAuth discovery, credential headers, stdio MCP sources, and API sources.
  • Breaking changes / migration notes: none expected.

Linked Issues

N/A

中文说明

这个 PR 做了什么

这个 PR 收窄 MCP source URL 的归一化逻辑:只在 query string 或 fragment 之前清理 path 的尾部斜杠。远程 MCP URL 的 path 仍会去掉尾斜杠,但 query 参数和 hash fragment 会按用户配置原样保留。

为什么需要它

之前的实现用整串字符串做尾部斜杠删除:

url.replace(/\/+$, '')

这会把 URL 当成一整段普通字符串处理。对于 https://api.example.com/mcp?key=/ 这样的 URL,最后的 / 属于 query 参数值,而不是 path。删除它会把配置语义从 ?key=/ 改成 ?key=,可能破坏依赖 query token、签名参数或 fragment routing 的远程 MCP server。

这是一个很小的 URL 结构保真修复:只规范化 path 后缀,不改写 searchhash 组件。

Reviewer 测试计划

运行 focused Bun test:

cd packages/desktop
bun test packages/shared/src/sources/__tests__/server-builder-url.test.ts

该测试覆盖 path 尾斜杠清理,以及 query / fragment 里合法保留尾部 / 的情况。

证据(Before & After)

非 UI 改动,无截图。

修复前,字符串级归一化会产生:

https://api.example.com/mcp/       -> https://api.example.com/mcp
https://api.example.com/mcp?key=/  -> https://api.example.com/mcp?key=
https://api.example.com/mcp#/      -> https://api.example.com/mcp#

修复后,只清理 path 部分:

https://api.example.com/mcp/       -> https://api.example.com/mcp
https://api.example.com/mcp?key=/  -> https://api.example.com/mcp?key=/
https://api.example.com/mcp#/      -> https://api.example.com/mcp#/

测试环境

Windows:Bun 1.3.14,安装在 D:\ZXY\Dev\bun\bin。Linux:WSL Ubuntu 22.04,Bun 1.3.14,安装在 /mnt/d/ZXY/Dev/bun-linux/bin

执行过的验证:

cd packages/desktop
bun test packages/shared/src/sources/__tests__/server-builder-url.test.ts
bun run typecheck:shared

风险与范围

主要风险很低;本 PR 只影响远程 MCP source URL 的归一化,并保留原有 path 尾斜杠清理行为。不包含 macOS 本地验证、真实远程 MCP server 调用、OAuth discovery、credential headers、stdio MCP sources 或 API sources。预计无 breaking changes。

关联 Issue

N/A

Normalize MCP source URLs by trimming trailing slashes only from the path portion before query or fragment components. This keeps query parameters and fragments intact when they legitimately end with a slash.

Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
@qwen-code-ci-bot

qwen-code-ci-bot commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

Thanks for the PR, @VectorPeak! (re-run)

Template looks good ✓

Problem: The normalizeMcpUrl function uses url.replace(/\/+$/, '') which treats the entire URL as a flat string, stripping trailing slashes from query parameter values or fragments — not just the path. This is a correctness bug in the normalization logic. The behavior is deterministically reproducible: any MCP URL with a query parameter or fragment ending in / will have that character silently removed. No linked issue, but the bug is provable from the code itself.

Direction: Fixing URL normalization to respect URL structure (path vs. query vs. fragment) is clearly correct and aligned with qwen-code's MCP support. MCP servers that use query tokens, signed parameters, or fragment-based routing would be affected by the current behavior.

Size: 10 production lines + 41 test lines. Minimal and focused. Not applicable for core module gate (changes are in packages/desktop/packages/shared, not core paths).

Approach: The fix is well-targeted — split at the first ? or #, trim only the path portion, recombine. Preserves the legacy fallback for non-parseable URLs. Scope is minimal: just the normalization function and a focused test file. Correction from prior run: the test file's bun:test import is correct — packages/desktop uses bun:test throughout (296 existing test files in the same package). No change needed.

Moving on to code review. 🔍

中文说明

感谢贡献!(重新运行)

模板完整 ✓

问题:normalizeMcpUrl 函数使用 url.replace(/\/+$/, '') 把 URL 当普通字符串处理,会错误地删除 query 参数或 fragment 中的尾部斜杠。这是归一化逻辑的正确性 bug,行为可确定性复现:任何 query 参数或 fragment 以 / 结尾的 MCP URL 都会被静默修改。虽无关联 issue,但 bug 可从代码本身证明。

方向:修复 URL 归一化以区分 URL 结构(path / query / fragment)是完全正确的,与 qwen-code 的 MCP 支持一致。使用 query token、签名参数或 fragment routing 的 MCP server 会受到当前行为的影响。

规模:10 行生产代码 + 41 行测试。精简聚焦。不触及核心模块(改动在 packages/desktop/packages/shared,非 core 路径)。

方案:修复方案精准——在第一个 ?# 处分割,只清理 path 部分,再拼接回去。保留了对不可解析 URL 的降级处理。范围最小化:只改了归一化函数和对应的测试文件。更正:测试文件的 bun:test 导入是正确的——packages/desktop 整个包都使用 bun:test(该包中有 296 个测试文件使用相同导入)。无需修改。

进入代码审查 🔍

Qwen Code · qwen3.7-max

@qwen-code-ci-bot

qwen-code-ci-bot commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

Code Review

The fix is correct and minimal. The approach — finding the first ? or # with url.search(/[?#]/), trimming trailing slashes only from the path prefix, then reattaching the suffix — correctly preserves query parameter values and fragments that happen to end with /.

The logic handles all edge cases properly:

  • No query/fragment → falls through to the original regex (preserves existing behavior)
  • Non-parseable URLs → also fall through correctly (no ? or # in not a url///)
  • Combined path slash + query (e.g., https://api.example.com/mcp/?key=value/) → both the path slash is removed AND the query value is preserved
  • Combined query + fragment (e.g., https://api.example.com/mcp/?key=val#frag/) → url.search(/[?#]/) finds the ? first, so the path is trimmed and everything from ? onward is preserved verbatim

The function is called in two places (server-builder.ts:112 for building MCP server configs, validation.ts:125 for connection validation). Both callers pass user-configured MCP URLs, so the fix applies consistently.

Correction from prior run: the test file's bun:test import is correct for this package. packages/desktop uses bun:test as its test runner — package.json defines "test": "bun test && ..." and 296 test files across the desktop package use the same import. No change needed.

Verification

Verified the normalization logic with a standalone node script comparing before/after behavior across 10 URL shapes:

PR #6587 - normalizeMcpUrl verification
============================================================
Input:  https://api.example.com/mcp/
Before: https://api.example.com/mcp
After:  https://api.example.com/mcp unchanged

Input:  https://api.example.com/mcp///
Before: https://api.example.com/mcp
After:  https://api.example.com/mcp unchanged

Input:  https://api.example.com/mcp?key=/
Before: https://api.example.com/mcp?key=
After:  https://api.example.com/mcp?key=/ FIXED

Input:  https://api.example.com/mcp/?key=value/
Before: https://api.example.com/mcp/?key=value
After:  https://api.example.com/mcp?key=value/ FIXED

Input:  https://api.example.com/mcp#/
Before: https://api.example.com/mcp#
After:  https://api.example.com/mcp#/ FIXED

Input:  https://api.example.com/mcp/#/route/
Before: https://api.example.com/mcp/#/route
After:  https://api.example.com/mcp#/route/ FIXED

Input:  https://api.example.com/mcp/?key=val#frag/
Before: https://api.example.com/mcp/?key=val#frag
After:  https://api.example.com/mcp?key=val#frag/ FIXED

Input:  not a url///
Before: not a url
After:  not a url unchanged

Input:  https://api.example.com/mcp
Before: https://api.example.com/mcp
After:  https://api.example.com/mcp unchanged

Input:  https://api.example.com
Before: https://api.example.com
After:  https://api.example.com unchanged

Behavioral fixes: 5 | Backward compatible: 5

All 10 cases produce correct results: path trailing slashes still removed (backward compatible), query/fragment trailing slashes preserved (fixed), combined query+fragment handled correctly (fixed), non-parseable URLs still fall back gracefully (backward compatible).

Maintainer @wenshao also verified locally on macOS with the full bun test suite (163 pass / 0 fail across 15 files) and an extended 22-URL adversarial A/B matrix (69 assertions, all pass).

中文说明

代码审查

修复正确且最小化。方案——用 url.search(/[?#]/) 找到第一个 ?#,只对 path 前缀去尾部斜杠,再拼接后缀——正确保留了 query 参数值和 fragment 中碰巧以 / 结尾的部分。

逻辑处理所有边界情况都正确:无 query/fragment 走原有正则、不可解析 URL 同样正确降级、path 斜杠 + query 组合正确处理、query + fragment 组合也正确处理(url.search(/[?#]/) 先找到 ?,path 被清理,? 之后原样保留)。

函数在两个地方被调用(server-builder.ts:112validation.ts:125),两处都传入用户配置的 MCP URL,修复一致适用。

更正:测试文件的 bun:test 导入对该包是正确的。packages/desktop 使用 bun:test 作为测试运行器——package.json 中定义了 "test": "bun test && ...",且该包中有 296 个测试文件使用相同导入。无需修改。

验证

使用独立 node 脚本对比修复前后行为,覆盖 10 种 URL 形态,所有场景均产生正确结果。维护者 @wenshao 也在 macOS 上完整验证了 bun 测试套件(15 个文件 163 通过 / 0 失败)和 22 种 URL 扩展对抗矩阵(69 处断言全通过)。

Qwen Code · qwen3.7-max

@qwen-code-ci-bot

qwen-code-ci-bot commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

This is a clean, focused correctness fix. The normalizeMcpUrl function had a string-level regex that didn't respect URL structure — it would silently corrupt query parameter values and fragments ending with /. The fix is 10 lines of production code that split at the first ? or #, trim only the path, and recombine. Verification confirms all cases behave correctly across 10 URL shapes, with 5 behavioral fixes and 5 backward-compatible unchanged results.

The scope is minimal — one function, one test file, no drive-by changes. The test file correctly uses bun:test, matching the packages/desktop test runner. Maintainer @wenshao has independently verified on macOS with the full test suite (163 pass) and an extended adversarial matrix, closing the macOS testing gap and recommending merge.

Approving. ✅

中文说明

这是一个干净、聚焦的正确性修复。normalizeMcpUrl 函数的字符串级正则不区分 URL 结构——会静默破坏以 / 结尾的 query 参数值和 fragment。修复用 10 行生产代码在第一个 ?# 处分割,只清理 path,再拼接回去。验证确认 10 种 URL 形态全部正确:5 处行为修复 + 5 处向后兼容不变。

范围最小——一个函数、一个测试文件、无顺手改动。测试文件正确使用 bun:test,与 packages/desktop 的测试运行器一致。维护者 @wenshao 已在 macOS 上独立验证(完整测试套件 163 通过)并扩展了对抗矩阵,补全了 macOS 测试缺口,建议合并。

通过。✅

Qwen Code · qwen3.7-max

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, looks ready to ship. ✅

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed — no blockers. Suggestion-level recommendations are in the Suggestion summary comment below.

@qwen-code-ci-bot

qwen-code-ci-bot commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

Suggestions — commit 62a5bc01c

  • packages/desktop/packages/shared/src/sources/__tests__/server-builder-url.test.ts:20Missing test: URL with both query string and fragment. The combination https://example.com/mcp/?key=val#frag is a common real-world URL shape. The implementation handles it correctly (the regex finds the first ? or #), but no test asserts this. An explicit test guards against future refactors that might handle ? and # separately.
  • packages/desktop/packages/shared/src/sources/__tests__/server-builder-url.test.ts:38Misleading test name. "keeps legacy fallback behavior for non-parseable URLs" — the input 'not a url///' has no ? or #, so it takes the same suffixIndex === -1 branch as any URL without query/fragment. There is no distinct "legacy fallback" code path. Suggested rename: "removes trailing slashes from plain strings".

— qwen3.7-max via Qwen Code /review

Clarify that MCP URL normalization removes trailing slashes only from the path portion while preserving query strings and fragments unchanged.

Co-authored-by: qwen-code-ci-bot <253268222+qwen-code-ci-bot@users.noreply.github.com>

Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
@VectorPeak

Copy link
Copy Markdown
Contributor Author

Thanks for the careful review. I updated the JSDoc to say that trailing slashes are removed from the path portion only, and that query strings and fragments are preserved unchanged. I also reran the focused Windows test: \cd packages/desktop && bun test packages/shared/src/sources/tests/server-builder-url.test.ts.

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed — no blockers. Suggestion-level recommendations are in the Suggestion summary comment below.

@wenshao wenshao left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed — no blockers. Suggestions are inline.

export function normalizeMcpUrl(url: string): string {
return url.replace(/\/+$/, '');
const suffixIndex = url.search(/[?#]/);
if (suffixIndex === -1) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] Consider adding a brief comment explaining why the URL constructor is intentionally avoided here. A future maintainer might see the manual search(/[?#]/) + slice() parsing and refactor to new URL(), which would subtly change behavior (host lowercasing, percent-encoding normalization, throwing on unparseable inputs like 'not a url///').

Suggested change
if (suffixIndex === -1) {
// Intentionally avoids URL constructor: preserves original URL verbatim and handles non-parseable strings without throwing.
const suffixIndex = url.search(/[?#]/);

— qwen3.7-max via Qwen Code /review

'https://api.example.com/mcp?key=value/',
);
});

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] The tests cover ? and # independently but not together. Adding a combined case locks in the invariant that url.search(/[?#]/) correctly handles ? before # per RFC 3986 ordering, and prevents a regression if someone rewrites the split logic.

Suggested change
expect(normalizeMcpUrl('https://api.example.com/mcp/?key=value/')).toBe(
'https://api.example.com/mcp?key=value/',
);
});
test('preserves trailing slashes when both query and fragment are present', () => {
expect(normalizeMcpUrl('https://api.example.com/mcp/?key=val#/route/')).toBe(
'https://api.example.com/mcp?key=val#/route/',
);

— qwen3.7-max via Qwen Code /review

@wenshao

wenshao commented Jul 11, 2026

Copy link
Copy Markdown
Collaborator

✅ Local verification (maintainer) — recommend merge

Built and ran this PR end-to-end on macOS, which also closes the PR's 🍎 macOS ⚪ not tested gap.

Environment: macOS 15 (Darwin 24.6, arm64) · Bun 1.3.14 (matches the author's env) · tsc 5.9.3 · Node 22. Verified against PR head 62a5bc0 in an isolated worktree, so no other local changes interfere.

What I ran

# Check Result
1 bun test …/sources/__tests__/server-builder-url.test.ts — the test shipped in this PR 4 pass / 0 fail (7 assertions)
2 bun test …/sources/__tests__/ — full suite, regression guard 163 pass / 0 fail (15 files, 372 assertions)
3 cd packages/shared && bun run tsc --noEmit — the author's typecheck 0 diagnostics (353 files)
4 Extended adversarial A/B matrix (OLD vs NEW normalizer, 22 URL shapes) 4 pass / 0 fail (69 assertions)

verification session

Does it actually fix the bug?

Yes. I inlined the pre-PR normalizer (url.replace(/\/+$/, '')) and diffed its output against the new path-only normalizer across 22 URL shapes. Every query/fragment trailing slash the old code silently corrupted is now preserved, while the path trailing-slash cleanup is unchanged — and now correctly reaches the path even when a ?/# suffix follows (which the old whole-string trim could not).

OLD vs NEW behavioral diff

Impact chain confirmed

The normalized URL flows straight into the live connection URL in two call sites, so the old corruption was reaching real endpoints — this is a genuine fix, not cosmetic:

  • SourceServerBuilder.buildMcpServer()normalizeMcpUrl()McpServerConfig.url → remote MCP client
  • validateMcpConnection()normalizeMcpUrl()CraftMcpClient({ url })

One minor, non-blocking observation

The new implementation also trims the authority-terminating slash on host-only URLs that carry a suffix, e.g. https://api.example.com/?key=valuehttps://api.example.com?key=value (and .../#frag…#frag). This is benign: WHATWG URL parsing re-adds the default / path, so new URL() treats both forms as identical, and no configured MCP endpoint depends on the difference. It isn't a merge blocker — flagging only for awareness; if byte-exact host handling is ever desired, a one-line test could pin it.

Verdict

Minimal, correct, well-scoped. Green on macOS here plus the author's Windows/Linux runs, no regressions, typecheck clean. LGTM — safe to merge. 🟢

Note: the "extended A/B matrix" in row 4 is my own throwaway verification harness (mcp-url-verify.extra.test.ts), not part of this PR — it exists only to prove the diff.

🇨🇳 中文版本

✅ 维护者本地验证 —— 建议合并

macOS 上完整构建并运行了本 PR,同时补上了 PR 中 🍎 macOS ⚪ not tested 的缺口。

环境: macOS 15 (Darwin 24.6, arm64) · Bun 1.3.14(与作者环境一致)· tsc 5.9.3 · Node 22。在独立 worktree 中基于 PR head 62a5bc0 验证,不受本地其他改动干扰。

执行的检查

# 检查项 结果
1 bun test …/server-builder-url.test.ts(PR 自带测试) 4 通过 / 0 失败(7 处断言)
2 bun test …/sources/__tests__/(完整套件,回归保护) 163 通过 / 0 失败(15 个文件,372 处断言)
3 cd packages/shared && bun run tsc --noEmit(作者的类型检查) 0 报错(353 个文件)
4 扩展对抗式 A/B 矩阵(旧 vs 新归一化,22 种 URL 形态) 4 通过 / 0 失败(69 处断言)

(截图见上方英文部分)

是否真的修复了问题?

是。我把 改动前 的归一化实现(url.replace(/\/+$/, ''))内联进来,和新的 path-only 实现在 22 种 URL 形态上逐一对比。旧实现会静默破坏的 query / fragment 尾斜杠,现在全部原样保留;同时 path 尾斜杠清理行为保持不变,并且现在即使后面跟着 ?/# 也能正确清理 path(旧的整串裁剪做不到)。

影响链已确认

归一化后的 URL 会直接进入两处真实连接 URL,所以旧的破坏是会实际影响到远程端点的 —— 这是真实修复,而非表面改动:

  • SourceServerBuilder.buildMcpServer()normalizeMcpUrl()McpServerConfig.url → 远程 MCP 客户端
  • validateMcpConnection()normalizeMcpUrl()CraftMcpClient({ url })

一点小的、不阻塞合并的观察

新实现对带 suffix 的 纯 host URL 也会裁掉那个结尾斜杠,例如 https://api.example.com/?key=valuehttps://api.example.com?key=value(以及 .../#frag…#frag)。这是 无害 的:WHATWG URL 解析会自动补回默认的 / path,new URL() 视二者完全等价,也没有任何 MCP 端点依赖这个差异。它不阻塞合并,仅作提醒;若将来希望 host 部分做到逐字节保真,可以补一条一行的测试来固定行为。

结论

改动最小、正确、范围清晰。macOS(本次)叠加作者的 Windows/Linux 运行结果全绿,无回归,类型检查干净。LGTM —— 可以安全合并。 🟢

说明:上表第 4 行的"扩展 A/B 矩阵"是我自己的一次性验证脚手架(mcp-url-verify.extra.test.ts),不属于 本 PR,仅用于证明改动的正确性。

@wenshao

wenshao commented Jul 11, 2026

Copy link
Copy Markdown
Collaborator

@qwen-code /triage

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, looks ready to ship. ✅

@wenshao
wenshao added this pull request to the merge queue Jul 11, 2026
Merged via the queue into QwenLM:main with commit 28bfadf Jul 11, 2026
78 checks passed
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.

3 participants