Skip to content

fix(cliclient): path-escape server and tool names in per-server URLs - #1751

Merged
Dumbris merged 1 commit into
smart-mcp-proxy:mainfrom
rudycelekli:fix/cliclient-escape-20261023-01
Oct 26, 2026
Merged

Dumbris merged 1 commit into
smart-mcp-proxy:mainfrom
rudycelekli:fix/cliclient-escape-20261023-01

Conversation

@rudycelekli

Copy link
Copy Markdown
Contributor

Pull Request

Description

Official-registry server ids are namespace/name (io.github.owner/repo, com.pulsemcp/google-flights). The daemon handles them on /api/v1/servers/{id}/* by decoding a %2F-encoded {id} (decodeServerIDParam, MCP-1118), and GetServerLogs already sends url.PathEscape(serverName). Every other per-server method in internal/cliclient splices the raw name into the URL with fmt.Sprintf, so the slash splits the name across path segments and the request lands on a different route (usually a 404):

mcpproxy upstream enable io.github.owner/repo
  -> POST /api/v1/servers/io.github.owner/repo/enable    (route: {id}=io.github.owner, then /repo/enable)

Affected: ServerAction (enable/disable/restart), GetServerTools, SetToolEnabled, SetAllToolsEnabled, TriggerOAuthLoginWithResult, TriggerOAuthLogout, PatchServer, RemoveServer, GetToolApprovals, GetToolDiff, ApproveTools, BlockTools. Most of these declare a local url variable that shadows net/url, which is probably how the escape was missed. ServerAction's var url string is renamed to reqURL; the url := fmt.Sprintf(..., url.PathEscape(...)) sites are fine as written because the new variable's scope starts after the statement.

Tool names in SetToolEnabled and GetToolDiff are escaped too. For a / in a tool name, the daemon also has to decode {tool}; that is #1750.

Testing

  • I have tested these changes locally
  • I have added/updated tests that prove my fix is effective or my feature works
  • All existing tests pass

TestPerServerRequests_EscapeServerAndToolNames (internal/cliclient/server_path_escape_test.go) calls 12 methods against an httptest server and checks r.URL.EscapedPath().

CGO_ENABLED=0 go vet ./internal/cliclient                                       # clean
CGO_ENABLED=0 go test ./internal/cliclient -count=1                             # ok
# with client.go reverted:
CGO_ENABLED=0 go test ./internal/cliclient -run TestPerServerRequests -count=1  # FAIL, 12 of 12 cases
gofmt -l internal/cliclient                                                     # clean

Co-authored-by: Cursor <cursoragent@cursor.com>
@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@Dumbris Dumbris left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Verified: the PR test fails on main for the reason described and passes on this branch merged with main; race tests for the touched packages and CI lint (bare and server tags) are clean. Also built and tested together with the rest of this batch, independently reviewed, and cross-reviewed with a second model.

@Dumbris
Dumbris merged commit 83ccbc2 into smart-mcp-proxy:main Oct 26, 2026
46 checks passed
@Dumbris

Dumbris commented Oct 26, 2026

Copy link
Copy Markdown
Member

Merged. Thank you, @rudycelekli! mcpproxy upstream … now reaches the right route for official-registry ids like io.github.owner/repo; together with #1750 the tool-name escape resolves on the daemon side too.

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