Skip to content

Commit 425a618

Browse files
committed
fix(cliclient): percent-encode server name in daemon logs URL
Third read site for the namespaced-name log fix (Codex review round 2 on PR #604): the daemon-backed CLI logs client built /servers/%s/logs with the raw name, so 'mcpproxy upstream logs io.github.x/name' missed the chi route. PathEscape the name (matching the registry client) + regression test. Related #598
1 parent 86295b9 commit 425a618

2 files changed

Lines changed: 46 additions & 2 deletions

File tree

internal/cliclient/client.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -415,8 +415,11 @@ func (c *Client) GetServers(ctx context.Context) ([]map[string]interface{}, erro
415415

416416
// GetServerLogs retrieves logs for a specific server.
417417
func (c *Client) GetServerLogs(ctx context.Context, serverName string, tail int) ([]contracts.LogEntry, error) {
418-
url := fmt.Sprintf("%s/api/v1/servers/%s/logs?tail=%d", c.baseURL, serverName, tail)
419-
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
418+
// PathEscape the server name: official-registry names are namespace/name and
419+
// contain "/", which would otherwise inject extra path segments and miss the
420+
// chi /servers/{id}/logs route (MCP-1111 / #598).
421+
reqURL := fmt.Sprintf("%s/api/v1/servers/%s/logs?tail=%d", c.baseURL, url.PathEscape(serverName), tail)
422+
req, err := http.NewRequestWithContext(ctx, http.MethodGet, reqURL, nil)
420423
if err != nil {
421424
return nil, fmt.Errorf("failed to create request: %w", err)
422425
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package cliclient
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"net/http"
7+
"net/http/httptest"
8+
"testing"
9+
"time"
10+
11+
"github.com/stretchr/testify/assert"
12+
"github.com/stretchr/testify/require"
13+
"go.uber.org/zap"
14+
)
15+
16+
// Regression for MCP-1111 / #598: the daemon logs client must percent-encode a
17+
// namespaced (slash-bearing) official-registry server name so the request matches
18+
// the chi /servers/{id}/logs route instead of injecting extra path segments.
19+
func TestClient_GetServerLogs_EscapesSlashName(t *testing.T) {
20+
const serverName = "io.github.evidai/polymarket-guard"
21+
var gotEscaped string
22+
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
23+
gotEscaped = r.URL.EscapedPath()
24+
w.Header().Set("Content-Type", "application/json")
25+
w.WriteHeader(http.StatusOK)
26+
_ = json.NewEncoder(w).Encode(map[string]interface{}{
27+
"success": true,
28+
"data": map[string]interface{}{"logs": []interface{}{}},
29+
})
30+
}))
31+
defer ts.Close()
32+
33+
client := NewClient(ts.URL, zap.NewNop().Sugar())
34+
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
35+
defer cancel()
36+
37+
_, err := client.GetServerLogs(ctx, serverName, 50)
38+
require.NoError(t, err)
39+
assert.Equal(t, "/api/v1/servers/io.github.evidai%2Fpolymarket-guard/logs", gotEscaped,
40+
"server name must be percent-encoded in the daemon logs URL")
41+
}

0 commit comments

Comments
 (0)