You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 83ccbc2
Browse filesBrowse the repository at this point in the historyBrowse files
fix(cliclient): path-escape server and tool names in per-server URLs (#1751)
# 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
- [x] I have tested these changes locally
- [x] I have added/updated tests that prove my fix is effective or my
feature works
- [x] 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>
0 commit comments