Skip to content

feat(mcp): tell agents which upstream servers they can reach - #1486

Merged
github-actions[bot] merged 1 commit into
mainfrom
claude/mcp-proxy-server-visibility-c5fed8
Oct 3, 2026
Merged

github-actions[bot] merged 1 commit into
mainfrom
claude/mcp-proxy-server-visibility-c5fed8

Conversation

@Dumbris

@Dumbris Dumbris commented Oct 3, 2026

Copy link
Copy Markdown
Member

Pull Request

Description

Users report that Claude Code and Codex connected through mcpproxy "don't see" upstream servers. The agents then fall back to gh, curl and similar instead of the proxied tools. This PR fixes the cause and tells each agent what it can reach.

Bug: /mcp sent no initialize instructions. /mcp, /mcp/call, /mcp/code and /mcp/p/<slug> are served by callToolServer and codeExecServer, not by p.server. Only p.server and directServer had WithInstructions, so a default client never learned that upstream tools sit behind retrieve_tools. Both servers now carry the instructions (mcp_routing.go).

Per-caller guidance (internal/server/mcp_agent_access.go)

  • Initialize and server/discover instructions: the default text is rebuilt from the built-in tools this caller actually sees. Its new clauses say upstream tools aren't listed individually, and that the agent should search retrieve_tools before using a shell CLI or raw API. A YOUR ACCESS block lists:
    • the active profile, never the operator-only anonymous_profile;
    • the reachable servers: enabled, approved, connected, and in the caller's profile ∩ agent-token scope;
    • the allowed operation tiers: the token's permission set limited by the profile's max_tier, noting tools.allow exemptions;
    • the built-ins the profile hides.
  • retrieve_tools description: gains a "reachable ONLY through this tool" note and a per-caller CONNECTED SERVERS searchable here: … suffix. Claude Code's tool search can then match "github" to retrieve_tools.
  • Re-listing: clients get tools/list_changed only when the reachable server set, or the advertise flag, changes.
  • Name safety: server names are spelled out only if they match ^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$. Other names count toward "+N more", as a prompt-injection guard.
  • Opt-out: new config advertise_upstream_servers, default true and hot-reloadable.
  • Scope checks: everything is derived through the same predicates the call-time gates use: serverInScope, ScopedView with anonymous confinement, the token's permissions, and CompiledPolicy.Decide.

CLI and docs

  • mcpproxy agent-instructions [--with-servers] [--client cursor] prints a CLAUDE.md / AGENTS.md snippet that follows the configured routing_mode.
  • New page docs/features/agent-instructions.md, linked from the connect-clients, routing-modes and quick-start pages and from the config reference.

What a reviewer should know

  • Spec 105 SC-005: the test now pins operator instructions as identical for every caller. The generated access block is per caller by design, and scope-filtered.
  • Goldens: the retrieve_tools description changed, so four toolslist_goldens files were regenerated. retrieve_tools was added to toolsListAllowedDelta, and the pre-105 comparison allows exactly retrieveToolsReachNote.
  • Profile probes don't record: composing instructions calls the profile filter with a no-record context (noResolutionRecordKey). Recording a session resolution at initialize changed which sessions a later profile edit notifies.
  • set_profile sends no tools/list_changed (known limitation): sending it mid-call turns the streamable-HTTP JSON response into an event stream, which breaks JSON-only clients. The stale text only names servers this same session could already reach. This is documented.
  • Not in this PR: the Connect wizard writing the snippet into the client's memory file. That needs its own spec.

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

  • New tests:

    • instructions present on every routing surface;
    • operator text reaching every mode;
    • per-caller sync for admin, a read-only profile, a scoped token, token × profile, and a deny-all token;
    • opt-out;
    • inventory change detection;
    • unsafe-name filtering;
    • CLI routing-mode variants;
    • hot-reload detection.
  • Live check, isolated instance: an admin saw both servers; a read-only token scoped to one server saw only that server, plus the refused tiers, in both initialize and tools/list.

  • Suites and builds: the race suite with -tags server passes for server, profile, config, runtime, storage, httpapi and cmd. Lint is clean, and the strict docs build passes.

  • Cross-model review: zcode produced no verdict. opencode (Sol) found 8 issues over four rounds; all were fixed except the set_profile limitation above, and the final round came back clean.

🤖 Generated with Claude Code

Agents connected through mcpproxy did not realise upstream tools sit
behind retrieve_tools and fell back to shell CLIs (gh, curl, ...).

- Fix: /mcp, /mcp/call, /mcp/code and /mcp/p/<slug> are served by
  callToolServer/codeExecServer, which carried no initialize
  instructions at all (only p.server did). They now share the default.
- Per-caller instructions (initialize and server/discover): the default
  is recomposed from the built-ins the caller actually sees, plus a
  YOUR ACCESS block listing the active profile (never the
  anonymous_profile), reachable servers (enabled, approved, connected,
  in profile and token scope) and allowed operation tiers (token
  permissions with the profile max_tier, noting tools.allow exemptions).
- retrieve_tools description gains a reach note and a per-caller
  "CONNECTED SERVERS searchable here" suffix, so client-side tool search
  (Claude Code) matches service names; tools/list_changed is sent when
  the reachable set changes. Only names matching a conservative charset
  are spelled out (prompt-injection guard).
- advertise_upstream_servers (default true, hot-reloadable) opts out of
  naming servers.
- mcpproxy agent-instructions prints a CLAUDE.md/AGENTS.md snippet that
  follows routing_mode, optionally with the connected servers.
- Docs: features/agent-instructions, config reference, links from
  connect-clients, routing-modes and quick-start.

Spec 105 SC-005 now pins operator instructions as identical for every
caller; the generated access block is per caller and scope-filtered.
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying mcpproxy-docs with  Cloudflare Pages  Cloudflare Pages

Latest commit: 0c38dac
Status: ✅  Deploy successful!
Preview URL: https://c614eb29.mcpproxy-docs.pages.dev
Branch Preview URL: https://claude-mcp-proxy-server-visi.mcpproxy-docs.pages.dev

View logs

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Approved (Model B): Paperclip review verdicts = ACCEPT and qa-gate green at this head SHA. Arming auto-merge; GitHub merges when all required checks pass.

@github-actions
github-actions Bot enabled auto-merge (squash) October 3, 2026 14:39
@github-actions
github-actions Bot merged commit dc851c8 into main Oct 3, 2026
45 checks passed
@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

❌ Patch coverage is 81.08974% with 59 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
cmd/mcpproxy/agent_instructions_cmd.go 48.43% 33 Missing ⚠️
internal/server/mcp_agent_access.go 90.78% 12 Missing and 9 partials ⚠️
internal/config/config.go 0.00% 2 Missing ⚠️
internal/profile/policy.go 0.00% 2 Missing ⚠️
cmd/mcpproxy/main.go 0.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

github-actions Bot pushed a commit that referenced this pull request Oct 3, 2026
…1487)

## Description

Fixes three LOW findings from the zcode (GLM-5.3) review of #1486:

- **Config load failure (`cmd/mcpproxy/agent_instructions_cmd.go`):**
`mcpproxy agent-instructions` used to discard a config load error and
print the default retrieve_tools workflow with exit 0. It now warns on
stderr, the same way `--with-servers` already does.
- **`upstream_servers` line:** the default snippet always said to use
`upstream_servers`. With `disable_management` or `read_only_mode` that
tool is absent (`internal/server/mcp.go` management guard), so the line
is now omitted in that case.
- **Godoc (`internal/profile/policy.go`):** `HasAllowRules` had been
inserted inside `Decide`'s doc comment, so `Decide` lost its godoc. The
method now sits below `Decide`. No behaviour change.

Not needed for v0.70.0-rc.3.

## 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

New test:
`TestBuildAgentInstructions_OmitsUpstreamServersWithoutManagement`. `go
test ./cmd/mcpproxy/ -run AgentInstructions` and `./internal/profile/`
pass.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
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.

2 participants