-
Notifications
You must be signed in to change notification settings - Fork 2.7k
fix(desktop): preserve MCP URL query suffixes #6587
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| /** | ||
| * @license | ||
| * Copyright 2026 Qwen Team | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| import { describe, expect, test } from 'bun:test'; | ||
| import { normalizeMcpUrl } from '../server-builder.ts'; | ||
|
|
||
| describe('normalizeMcpUrl', () => { | ||
| test('removes trailing slashes from the URL pathname', () => { | ||
| expect(normalizeMcpUrl('https://api.example.com/mcp/')).toBe( | ||
| 'https://api.example.com/mcp', | ||
| ); | ||
| expect(normalizeMcpUrl('https://api.example.com/mcp///')).toBe( | ||
| 'https://api.example.com/mcp', | ||
| ); | ||
| }); | ||
|
|
||
| test('preserves trailing slashes inside query parameters', () => { | ||
| expect(normalizeMcpUrl('https://api.example.com/mcp?key=/')).toBe( | ||
| 'https://api.example.com/mcp?key=/', | ||
| ); | ||
| expect(normalizeMcpUrl('https://api.example.com/mcp/?key=value/')).toBe( | ||
| 'https://api.example.com/mcp?key=value/', | ||
| ); | ||
| }); | ||
|
|
||
| test('preserves trailing slashes inside URL fragments', () => { | ||
| expect(normalizeMcpUrl('https://api.example.com/mcp#/')).toBe( | ||
| 'https://api.example.com/mcp#/', | ||
| ); | ||
| expect(normalizeMcpUrl('https://api.example.com/mcp/#/route/')).toBe( | ||
| 'https://api.example.com/mcp#/route/', | ||
| ); | ||
| }); | ||
|
|
||
| test('keeps legacy fallback behavior for non-parseable URLs', () => { | ||
| expect(normalizeMcpUrl('not a url///')).toBe('not a url'); | ||
| }); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -334,11 +334,19 @@ export class SourceServerBuilder { | |||||||
|
|
||||||||
| /** | ||||||||
| * Normalize MCP URL to standard format | ||||||||
| * - Removes trailing slashes | ||||||||
| * - Removes trailing slashes from the path portion only | ||||||||
| * - Preserves query strings and fragments unchanged | ||||||||
| * - Preserves the user-configured path as-is (no /mcp suffix appended) | ||||||||
| */ | ||||||||
| export function normalizeMcpUrl(url: string): string { | ||||||||
| return url.replace(/\/+$/, ''); | ||||||||
| const suffixIndex = url.search(/[?#]/); | ||||||||
| if (suffixIndex === -1) { | ||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion] Consider adding a brief comment explaining why the
Suggested change
— qwen3.7-max via Qwen Code /review |
||||||||
| return url.replace(/\/+$/, ''); | ||||||||
| } | ||||||||
|
|
||||||||
| return ( | ||||||||
| url.slice(0, suffixIndex).replace(/\/+$/, '') + url.slice(suffixIndex) | ||||||||
| ); | ||||||||
| } | ||||||||
|
|
||||||||
| // Singleton instance | ||||||||
|
|
||||||||
There was a problem hiding this comment.
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 thaturl.search(/[?#]/)correctly handles?before#per RFC 3986 ordering, and prevents a regression if someone rewrites the split logic.— qwen3.7-max via Qwen Code /review