Add upstream MCP proxy backend for guarded passthrough tools #18

Closed
opened 2026-07-10 03:24:29 +00:00 by coilyco-ops · 2 comments
Member

Context

Kai wants a Grubhub MCP implemented as a guarded passthrough to Playwright MCP. The current ward-mcp inline grammar is a good deploy/runtime fit for MCPs, but it projects HTTP grants today. This needs an MCP-upstream backend once cli-guard exposes the core KDL/specgen support.

Desired ward-mcp work

  • Add an upstream MCP backend that can connect to a streamable-HTTP MCP server.
  • Reuse cli-guard-generated proxy grants to expose only selected upstream tools.
  • Forward calls to the upstream MCP after applying generated pre-call guards.
  • Preserve upstream tool schemas for the outward MCP contract where possible.
  • Fail closed on unknown upstream tools and upstream schema drift.
  • Surface policy errors as normal MCP tool errors.

Grubhub-driving example

The target consumer is a future coilyco-bridge/deploy Grubhub service:

  • public/client MCP endpoint is grubhub-mcp
  • private upstream is a dedicated Playwright MCP in the same namespace
  • upstream owns a PVC-backed browser profile for sticky Grubhub auth
  • wrapper exposes only a small Playwright tool subset
  • browser_evaluate, run_code_unsafe, file_upload, drop, drag, fill_form, and raw network body tools are denied by absence

Non-goals for v1

  • Do not make ward-mcp a full browser policy engine.
  • Do not add a Grubhub-specific semantic layer.
  • Do not expose the private Playwright upstream directly.

Acceptance sketch

ward-mcp can serve an MCP whose tools are generated from allowlisted upstream MCP tools, backed by a private streamable-HTTP upstream, with simple argument guard failures returning MCP errors.

Dependency

Depends on coilyco-flight-deck/cli-guard#214 for the core KDL/specgen surface.

## Context Kai wants a Grubhub MCP implemented as a guarded passthrough to Playwright MCP. The current ward-mcp inline grammar is a good deploy/runtime fit for MCPs, but it projects HTTP grants today. This needs an MCP-upstream backend once cli-guard exposes the core KDL/specgen support. ## Desired ward-mcp work * Add an upstream MCP backend that can connect to a streamable-HTTP MCP server. * Reuse cli-guard-generated proxy grants to expose only selected upstream tools. * Forward calls to the upstream MCP after applying generated pre-call guards. * Preserve upstream tool schemas for the outward MCP contract where possible. * Fail closed on unknown upstream tools and upstream schema drift. * Surface policy errors as normal MCP tool errors. ## Grubhub-driving example The target consumer is a future coilyco-bridge/deploy Grubhub service: * public/client MCP endpoint is grubhub-mcp * private upstream is a dedicated Playwright MCP in the same namespace * upstream owns a PVC-backed browser profile for sticky Grubhub auth * wrapper exposes only a small Playwright tool subset * browser_evaluate, run_code_unsafe, file_upload, drop, drag, fill_form, and raw network body tools are denied by absence ## Non-goals for v1 * Do not make ward-mcp a full browser policy engine. * Do not add a Grubhub-specific semantic layer. * Do not expose the private Playwright upstream directly. ## Acceptance sketch ward-mcp can serve an MCP whose tools are generated from allowlisted upstream MCP tools, backed by a private streamable-HTTP upstream, with simple argument guard failures returning MCP errors. ## Dependency Depends on https://forgejo.coilysiren.me/coilyco-flight-deck/cli-guard/issues/214 for the core KDL/specgen surface.
Author
Member

WARDED_WORKFLOW: reservation-held

reservation details

Holder: launch intent for container engineer-codex-ward-mcp-18 on host kais-macbook-pro-2.local.

Accepted by ward agent --harness codex (reserved 2026-07-15T03:39:01Z). Concurrent ward agent runs are blocked until this intent becomes visible or the intent is released. The stale-intent fallback is still TTL-bounded (3h TTL). --override-reservation overrides.

Do not comment on or edit this issue to steer the run while it is reserved. The engineer seeded the body once at launch and never re-reads it, so a comment or edit reaches only human readers, never the running engineer. A correction goes to a new issue, dispatched fresh. That is the only channel that reaches a run in flight. Where the forge supports it, ward locks this conversation to make that a road-block rather than a convention (ward#494).

run seed context — what this run is carrying (ward#609)
  • Resolved: coilyco-flight-deck/ward-mcp#18 · branch issue-18 · harness codex · workflow pull-request-and-merge
  • Run: engineer-codex-ward-mcp-18 · ward v0.696.0 · dispatched 2026-07-15T03:38:54Z
  • Comment thread: 0 included in the pre-flight read, 0 stripped (ward's own automated comments).

Static container doctrine and seed boilerplate are identical every run and omitted here (they ride ward v0.696.0).

— Codex, via ward agent

<!-- ward-agent-reservation --> WARDED_WORKFLOW: reservation-held <details><summary>reservation details</summary> Holder: launch intent for container `engineer-codex-ward-mcp-18` on host `kais-macbook-pro-2.local`. Accepted by `ward agent --harness codex` (reserved 2026-07-15T03:39:01Z). Concurrent `ward agent` runs are blocked until this intent becomes visible or the intent is released. The stale-intent fallback is still TTL-bounded (3h TTL). `--override-reservation` overrides. **Do not comment on or edit this issue to steer the run while it is reserved.** The engineer seeded the body once at launch and never re-reads it, so a comment or edit reaches only human readers, never the running engineer. A correction goes to a **new issue, dispatched fresh**. That is the only channel that reaches a run in flight. Where the forge supports it, ward locks this conversation to make that a road-block rather than a convention (ward#494). <details><summary>run seed context — what this run is carrying (ward#609)</summary> - **Resolved:** `coilyco-flight-deck/ward-mcp#18` · branch `issue-18` · harness `codex` · workflow `pull-request-and-merge` - **Run:** `engineer-codex-ward-mcp-18` · ward `v0.696.0` · dispatched `2026-07-15T03:38:54Z` - **Comment thread:** 0 included in the pre-flight read, 0 stripped (ward's own automated comments). Static container doctrine and seed boilerplate are identical every run and omitted here (they ride ward v0.696.0). </details> </details> <!-- ward-agent-signature --> — Codex, via `ward agent`
Author
Member

WARDED_WORKFLOW: #24

details workflow: pull-request-and-merge; review summary: skipped (in-container review gate intentionally skipped because the temporary ward default pending brokered QA)

The implementation felt straightforward once the MCP SDK client cache behavior was accounted for.

confidence: high
surprises: the upstream session did not refresh tool schema, so drift checks had to probe a fresh connection
follow-ups: none

WARDED_WORKFLOW: https://forgejo.coilysiren.me/coilyco-flight-deck/ward-mcp/pulls/24 <details><summary>details</summary> workflow: pull-request-and-merge; review summary: skipped (in-container review gate intentionally skipped because the temporary ward default pending brokered QA) The implementation felt straightforward once the MCP SDK client cache behavior was accounted for. confidence: high surprises: the upstream session did not refresh tool schema, so drift checks had to probe a fresh connection follow-ups: none </details>
Sign in to join this conversation.
No description provided.