Skip to content

Upstream tool proxy

What it does: TapPass becomes the MCP endpoint the agent connects to, then forwards to the real upstream tool server after policy checks.

Agents need to call tools — Collibra catalog APIs, Salesforce, internal MCP servers. If the agent connects to those upstreams directly, TapPass sees nothing. If we put TapPass on the path as a proxy, every call passes through the pipeline before reaching the upstream.

The gateway already serves MCP (the tappass/gateway/mcp_server.py accepts inbound MCP traffic). What's new in this concept is the forwarding mode — TapPass is not just an MCP endpoint, it's an MCP-to-MCP proxy. The agent's connection terminates at TapPass; a separate connection from TapPass to the upstream completes the call.

This is the architectural primitive that makes "every tool call governed" actually true. See architecture §3 (interactions) and §7 (catalog) for full context.

Inputs: an MCP session from an enrolled agent (authenticated via mcp_session_token); an outbound tool call within that session.

Behavior:

  1. Validate the session's keyring is current (not expired).
  2. Look up the requested tool name in the keyring's layer-4 capabilities. Refuse if not in tools_allowed.
  3. Run the pipeline (schema_acl, loop_guard, PII / secret / exfil detectors). Refuse if any step rejects.
  4. Look up which upstream MCP server provides this tool (via approved-tool-server-list).
  5. Open / reuse a connection to that upstream, injecting the upstream's credentials from vault.
  6. Forward the call; record the response; emit audit; return to the agent.
  7. On keyring change (layer-4 slice updated), emit tools/list_changed to the agent's session.

Non-goals:

  • Choosing which upstreams are allowed (approved-tool-server-list).
  • Per-call resource ACL logic (resource-access-checker pipeline step).
  • Loop detection (runaway-agent-stopper pipeline step).

Where it lives: tappass/gateway/mcp_forward/ (sibling to existing mcp_server.py).

Reuses: the existing 32-step pipeline; the existing audit hash-chain; the existing capability-token system.

New: the forwarding state machine (per-session connection pool to upstreams); the tools/list_changed notification path on keyring change.

  • All acceptance_criteria pass.
  • End-to-end test: agent → TapPass → mock Collibra MCP; call succeeds; audit shows full path.
  • End-to-end test: keyring updated; tools/list_changed received by agent; agent's tool list reflects new caps.
  • Failure test: upstream MCP unreachable; agent receives upstream_unreachable with retry hint; audit logged.
  • Spec section in concepts/tappass-mcp-proxy.md written.

With policy-to-sandbox-config-builder: consumes the layer-4 slice; expects mcp_proxy_url, mcp_session_token, pipeline, capabilities.

With resource-access-checker, runaway-agent-stopper: pipeline steps we invoke. Their interface is the existing pipeline-step contract.

Open questions:

  • (Q) Connection pooling per upstream — long-lived (efficiency) vs. per-call (isolation)? Lean: long-lived per (org_id, upstream_id) pair, recycled on credential rotation.
  • The agent-side MCP client — that's agent-client-sdk.
  • Discovery of new upstream tools — that's runtime-tool-discovery (concept).
  • Inbound MCP (external systems calling the agent's tools) — that's the existing gateway/mcp_server.py.