Skip to content

Commit 6884742

Browse files
committed
docs(planning): sync meta-gateway invoke status with QA sign-off
Align planning doc with Ship result, surfaced one-hop exception, and completed Phase A–C checklist; refresh QA runbook red-flag wording. Signed-off-by: crimsonsunset <jsangio1@gmail.com>
1 parent 5508c5e commit 6884742

2 files changed

Lines changed: 32 additions & 26 deletions

File tree

docs/planning/meta-gateway-invoke-qa.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ After any Surface change: **Cursor → MCP → Reload tools**.
2929

3030
**Tester:** Cursor agent (Composer)
3131
**Date:** May 25, 2026
32-
**McpMux version / commit:** `feat/meta-gateway-invoke` @ `433e7bd` (PR [#155](https://github.com/mcpmux/mcp-mux/pull/155))
32+
**McpMux version / commit:** `feat/meta-gateway-invoke` @ `5508c5e` (PR [#155](https://github.com/mcpmux/mcp-mux/pull/155))
3333

3434
---
3535

@@ -291,7 +291,7 @@ Rules: McpMux meta tools only, read schemas before invoke, note truncation if an
291291
## Red flags (stop and file a bug)
292292

293293
- [ ] Backend tools (`github_*`, etc.) appear in `tools/list` without surfacing
294-
- [ ] Agent can call backend tools directly (bypassing `mcpmux_invoke_tool`)
294+
- [ ] Non-surfaced backend tools callable directly (bypassing `mcpmux_invoke_tool`) — surfaced one-hop is expected
295295
- [ ] Enable server expands `tools/list` beyond meta + surfaced
296296
- [ ] Search returns tools from inactive or unbound servers
297297
- [ ] Invoke succeeds for tools outside FeatureSet ACL

docs/planning/meta-gateway-invoke.md

Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Meta-Gateway Invoke (Search → Schema → Invoke)
22

33
**Last Updated:** May 25, 2026
4-
**Status:** Implemented on branch — manual QA in progress ([`meta-gateway-invoke-qa.md`](./meta-gateway-invoke-qa.md))
4+
**Status:** ✅ Phases A–C implemented and manually QA complete — ready to merge ([`meta-gateway-invoke-qa.md`](./meta-gateway-invoke-qa.md) **Ship**)
55
**Branch:** `feat/meta-gateway-invoke`
66
**Base branch:** `main`
77
**Issue:** [#155](https://github.com/mcpmux/mcp-mux/pull/155)
@@ -51,7 +51,7 @@ This doc defines that model for McpMux while preserving its product strengths: O
5151
| 7 | Invoke authorization | **Fail closed**`invoke_tool` rejects when target server/tool is outside effective permission set | Same composition as today: `(binding_servers ∪ session_enabled) − session_disabled`, then FeatureSet member filter. Empty effective set → invoke denied with actionable error, not silent proxy. |
5252
| 8 | Session enable/disable | **Keep existing `mcpmux_enable_server` / `mcpmux_disable_server`** — they gate invoke/search eligibility, not `tools/list` size | Mental model unchanged: "turn on github" expands what search/invoke can reach. `tools/list` size stays ~constant. |
5353
| 9 | Error messages | **Actionable, bounded errors** — no dumping full available-tool lists | e.g. `"github inactive → mcpmux_enable_server('github')"`, `"unknown tool → did you mean github_list_issues?"`. Optional Levenshtein suggestions (Phase D). |
54-
| 10 | Rollout | **Hard cut — no legacy opt-out** | Backend tools never appear in `tools/list`. Direct `call_tool` on backend qualified names is rejected with an actionable redirect to `mcpmux_invoke_tool`. No `expose_backend_tools_in_list` setting. Ship in one release; document migration in CHANGELOG. |
54+
| 10 | Rollout | **Hard cut — no legacy opt-out** | Non-surfaced backend tools never appear in `tools/list` and direct `call_tool` is rejected with a redirect to `mcpmux_invoke_tool`. **Exception:** FeatureSet members marked `surfaced: true` are promoted into `tools/list` and callable in one hop. No `expose_backend_tools_in_list` setting. Ship in one release; document migration in CHANGELOG. |
5555
| 11 | `mcpmux_list_all_tools` | **Keep as operator/diagnostic tool** — not the primary agent discovery path | Still useful for FeatureSet authoring and UI. Doc + descriptions steer agents to `search_tools`. Consider server_id filter arg in Phase A to avoid 855 KB dumps. |
5656
| 12 | Result shaping scope | **Phase B only on `invoke_tool`** — opt-in via explicit `filter`: `max_rows`, `max_bytes`, `fields`, `format: summary`. Omit filter → backend response as-is. | Agents pass `filter` when they know a tool returns large payloads. No default truncation. |
5757
| 13 | REST / OpenAPI capabilities | **Out of scope here** — Phase E / separate planning doc | [`web-admin-remote-access.md`](./web-admin-remote-access.md) covers admin REST, not REST→MCP capability YAML. No conflict; different layer. |
@@ -87,8 +87,8 @@ tools/list (fixed ~10–15 tools)
8787
4. mcpmux_get_tool_schema({ tools: ["github_list_issues"] })
8888
5. mcpmux_invoke_tool({
8989
server_id: "github",
90-
tool: "github_list_issues",
91-
args: { owner: "mcpmux", repo: "mcp-mux" }
90+
tool: "list_issues",
91+
args: { owner: "mcpmux", repo: "mcp-mux", state: "OPEN" }
9292
})
9393
```
9494

@@ -166,7 +166,7 @@ Prompts and resources: unchanged — still materialized per grants. Invoke model
166166
| `crates/mcpmux-gateway/src/services/meta_tools/invoke.rs` | `InvokeToolTool` impl — permission check, routing, error mapping, result shaping | ✅ Done |
167167
| `crates/mcpmux-gateway/src/services/meta_tools/invoke_backend.rs` | `InvokeToolBackend` trait + `RoutingService` adapter for testable invoke routing | ✅ Done |
168168
| `tests/rust/src/canned_invoke_backend.rs` | Canned backend for filter e2e integration tests | ✅ Done |
169-
| `tests/rust/tests/integration/meta_gateway_invoke.rs` | Search, schema, invoke, permission deny, surfaced tools, filter shaping, e2e filter via canned backend | ✅ Done (17 tests) |
169+
| `tests/rust/tests/integration/meta_gateway_invoke.rs` | Search, schema, invoke, permission deny, surfaced tools, filter shaping, e2e filter via canned backend | ✅ Done (16 tests) |
170170
| `docs/planning/meta-gateway-invoke-qa.md` | Manual QA runbook for Phases A–C | ✅ Done |
171171
| `docs/planning/meta-gateway-invoke.md` | This doc | ✅ Done |
172172

@@ -181,11 +181,12 @@ Prompts and resources: unchanged — still materialized per grants. Invoke model
181181
| [`crates/mcpmux-gateway/src/pool/features/facade.rs`](../../crates/mcpmux-gateway/src/pool/features/facade.rs) | Split into `get_advertised_tools_for_grants` vs `get_invokable_tools_for_grants` | ✅ Done |
182182
| [`crates/mcpmux-gateway/src/pool/features/resolution.rs`](../../crates/mcpmux-gateway/src/pool/features/resolution.rs) | `resolve_surfaced_feature_ids` for surfaced promotion | ✅ Done |
183183
| [`crates/mcpmux-gateway/src/pool/routing.rs`](../../crates/mcpmux-gateway/src/pool/routing.rs) | `format_direct_call_redirect`; actionable invoke errors | ✅ Done |
184-
| [`crates/mcpmux-gateway/src/mcp/handler.rs`](../../crates/mcpmux-gateway/src/mcp/handler.rs) | `tools/list` uses advertised set only; direct backend `call_tool` rejected with invoke redirect | ✅ Done |
184+
| [`crates/mcpmux-gateway/src/mcp/handler.rs`](../../crates/mcpmux-gateway/src/mcp/handler.rs) | `tools/list` uses advertised set only; non-surfaced direct `call_tool` rejected with invoke redirect; surfaced tools allowed one-hop; `ensure_roots_probed` before routing in `call_tool` | ✅ Done |
185185
| [`crates/mcpmux-core/src/domain/feature_set.rs`](../../crates/mcpmux-core/src/domain/feature_set.rs) | `surfaced: bool` on `FeatureSetMember` | ✅ Done |
186-
| [`apps/desktop/src/features/featuresets/FeatureSetPanel.tsx`](../../apps/desktop/src/features/featuresets/FeatureSetPanel.tsx) | Per-tool "Surface in client" toggle | ✅ Done |
187-
| [`apps/desktop/src/features/settings/SettingsPage.tsx`](../../apps/desktop/src/features/settings/SettingsPage.tsx) | Update meta-tools copy for search → schema → invoke workflow | ⬜ Pending |
188-
| [`README.md`](../../README.md) | Replace "every tool available right now" agent-facing claim; document search → schema → invoke flow | ⬜ Pending |
186+
| [`apps/desktop/src/features/featuresets/FeatureSetPanel.tsx`](../../apps/desktop/src/features/featuresets/FeatureSetPanel.tsx) | Per-tool "Surface in client" toggle + explainer tooltip | ✅ Done |
187+
| [`apps/desktop/src/features/settings/SettingsPage.tsx`](../../apps/desktop/src/features/settings/SettingsPage.tsx) | Meta-tools copy for search → schema → invoke workflow | ✅ Done |
188+
| [`README.md`](../../README.md) | Agent-facing search → schema → invoke flow; checkbox vs Surface in Feature Sets | ✅ Done |
189+
| [`docs/guide/feature-sets.mdx`](../guide/feature-sets.mdx) | Included vs Surface editor explainer; invoke ACL semantics | ✅ Done |
189190

190191
---
191192

@@ -194,17 +195,17 @@ Prompts and resources: unchanged — still materialized per grants. Invoke model
194195
### Phase A — Meta invoke core
195196

196197
**Effort:** ~3–4 days
197-
**Status:** ✅ Implemented — manual QA sections 0–4 pass; section 5 pass; section 6 pending ([`meta-gateway-invoke-qa.md`](./meta-gateway-invoke-qa.md))
198+
**Status:** ✅ Implemented — manual QA sections 0–4 pass ([`meta-gateway-invoke-qa.md`](./meta-gateway-invoke-qa.md))
198199

199200
- [x] `ToolDiscoveryService` — build index from Space features; search by query + optional `server_id`; return matches at `detail_level`
200201
- [x] `mcpmux_search_tools` meta tool — pagination (`limit`, `cursor`), `detail_level` enum
201202
- [x] `mcpmux_get_tool_schema` — single + batch; `compact` strips descriptions/examples
202203
- [x] `mcpmux_invoke_tool``{ server_id, tool, args }`; delegates to `RoutingService::call_tool`; fail closed on permission miss
203204
- [x] `FeatureService` split: **advertised** = meta tools + surfaced only (hard cut — no backend tools in list)
204-
- [x] Handler rejects direct backend `call_tool`return actionable error pointing to `mcpmux_invoke_tool`
205+
- [x] Handler rejects **non-surfaced** direct backend `call_tool`redirect to `mcpmux_invoke_tool`; surfaced tools pass through
205206
- [x] Actionable error mapping: inactive server, unknown tool, permission denied, param validation passthrough from backend
206207
- [x] Optional `server_id` filter on `mcpmux_list_all_tools`
207-
- [x] Integration tests: GitHub read path (enable → search → schema → invoke); deny when server inactive; direct `github_*` call rejected
208+
- [x] Integration tests: GitHub read path (enable → search → schema → invoke); deny when server inactive; non-surfaced direct call rejected
208209

209210
**Outcome:** Cursor session shows **10** `mcpmux_*` tools (verified May 25, 2026). Agent completes `github_list_issues` on `mcpmux/mcp-mux` via search → schema → invoke with zero param guessing.
210211

@@ -241,16 +242,16 @@ Prompts and resources: unchanged — still materialized per grants. Invoke model
241242
### Phase C — FeatureSet as invoke ACL + surfaced tools
242243

243244
**Effort:** ~3 days
244-
**Status:** ✅ Implemented — manual QA sections 8–9 pending
245+
**Status:** ✅ Implemented — manual QA sections 8–9 pass ([`meta-gateway-invoke-qa.md`](./meta-gateway-invoke-qa.md))
245246

246247
- [x] FeatureSet member model: tools invokable by default when server in set; optional `surfaced: true` promotes into `tools/list`
247248
- [x] Search + invoke respect FeatureSet member filter (not just server-all)
248249
- [x] Workspaces UI: per-tool "Surface in client" toggle in FeatureSet editor (`FeatureSetPanel.tsx`)
249250
- **Checkbox** = invoke ACL member (search + `mcpmux_invoke_tool`)
250251
- **Surface button** = promote that included tool into client `tools/list` for direct one-hop calls
251252
- User-facing explainer: [`docs/guide/feature-sets.mdx`](../guide/feature-sets.mdx#included-vs-surface-featureset-editor)
252-
- [ ] Update `mcpmux_create_feature_set` to accept optional `surfaced_tools[]` (UI path done; meta-tool arg deferred)
253-
- [x] Integration tests: binding with partial tool set → search only finds allowed tools; surfaced tool appears in `tools/list`
253+
- [x] `mcpmux_create_feature_set` accepts optional `surfaced_tools[]` (subset of `tool_qualified_names`; UI path also available)
254+
- [x] Integration tests: partial FeatureSet binding limits search; surfaced vs invokable gate; advertised set promotion
254255

255256
### Phase D — Advanced optimizations (defer)
256257

@@ -290,7 +291,7 @@ Prompts and resources: unchanged — still materialized per grants. Invoke model
290291
| Full validate | `pnpm validate` | fmt, clippy, check, eslint, typecheck |
291292
| Rust tests | `pnpm test:rust` | unit + `meta_gateway_invoke.rs` integration |
292293
| TS tests | `pnpm test:ts` | vitest |
293-
| Manual smoke | Cursor against live gateway: GitHub read, GWorkspace invoke, permission deny | Agent UX verification |
294+
| Manual smoke | Cursor against live gateway — full runbook sections 0–11 | Agent UX verification — ✅ complete May 25 |
294295

295296
---
296297

@@ -316,7 +317,7 @@ Prompts and resources: unchanged — still materialized per grants. Invoke model
316317
| [`crates/mcpmux-gateway/src/services/meta_tools/invoke.rs`](../../crates/mcpmux-gateway/src/services/meta_tools/invoke.rs) | Invoke meta tool + result shaping |
317318
| [`crates/mcpmux-gateway/src/services/meta_tools/invoke_backend.rs`](../../crates/mcpmux-gateway/src/services/meta_tools/invoke_backend.rs) | Pluggable invoke routing trait |
318319
| [`tests/rust/src/canned_invoke_backend.rs`](../../tests/rust/src/canned_invoke_backend.rs) | Test double for filter e2e |
319-
| [`crates/mcpmux-gateway/src/mcp/handler.rs`](../../crates/mcpmux-gateway/src/mcp/handler.rs) | `tools/list` + `call_tool` handler — legacy direct call blocking |
320+
| [`crates/mcpmux-gateway/src/mcp/handler.rs`](../../crates/mcpmux-gateway/src/mcp/handler.rs) | `tools/list` + `call_tool` — advertised set, surfaced one-hop, invoke redirect for non-surfaced |
320321
| [`docs/planning/dynamic-mcp-toggle-meta-tools.md`](./dynamic-mcp-toggle-meta-tools.md) | Session enable/disable — kept, semantics updated |
321322
| [`docs/planning/tool-level-session-pin.md`](./tool-level-session-pin.md) | Superseded for token budget; Phase F very optional rework |
322323

@@ -335,17 +336,19 @@ Prompts and resources: unchanged — still materialized per grants. Invoke model
335336

336337
## Reconciliation
337338

338-
This doc is the source of truth for the meta-gateway invoke model. Phases A–C are implemented on `feat/meta-gateway-invoke`; manual QA tracked in [`meta-gateway-invoke-qa.md`](./meta-gateway-invoke-qa.md). Mark [`tool-level-session-pin.md`](./tool-level-session-pin.md) **Status** as *Superseded* once Phase A ships to main.
339+
This doc is the source of truth for the meta-gateway invoke model. Phases A–C are implemented on `feat/meta-gateway-invoke` and manually QA complete ([`meta-gateway-invoke-qa.md`](./meta-gateway-invoke-qa.md)**Ship**). Mark [`tool-level-session-pin.md`](./tool-level-session-pin.md) **Status** as *Superseded* when this branch merges to main.
339340

340-
**Decision record (May 25, 2026):** Hard cut to invoke-only — no legacy direct backend exposure. Surfaced tools default zero everywhere (bundles included). FeatureSets redefine as invoke ACL + optional surfaced promotion. Session pin deferred to Phase F (very optional, last). Competitor analysis (MikkoParkkola + abdullah1854) informed Phase A–B scope; REST capabilities in Phase E / separate doc.
341+
**Decision record (May 25, 2026):** Hard cut to invoke-only for non-surfaced backend tools — no legacy full-catalog `tools/list`. Surfaced tools default zero everywhere (bundles included); opt-in per FeatureSet member for one-hop hot paths. FeatureSets redefine as invoke ACL + optional surfaced promotion. Session pin deferred to Phase F (very optional, last). Competitor analysis (MikkoParkkola + abdullah1854) informed Phase A–B scope; REST capabilities in Phase E / separate doc.
342+
343+
**Handler fix (May 25, 2026):** `call_tool` probes workspace roots before routing (matches `list_tools`) and allows direct calls when the tool is in `get_advertised_tools_for_grants` (surfaced). Non-surfaced backend names still get `use_invoke_tool` redirect.
341344

342345
**Design revision (May 25, 2026):** Removed default smart truncation — `filter` is opt-in only. Rationale: plain-text MCP backends (GWorkspace) don't map cleanly to JSON row truncation; agents should explicitly bound payloads when needed.
343346

344-
**QA ergonomics (May 25, 2026):** Bind FeatureSets in Workspaces UI before agent QA — session enable alone is insufficient without binding ACL. Do **not** call `mcpmux_bind_current_workspace` during routine QA (triggers Space-wide approval modal). Reload MCP tools after UI binding changes.
347+
**QA ergonomics (May 25, 2026):** Bind FeatureSets in Workspaces UI before agent QA — session enable alone is insufficient without binding ACL. Do **not** call `mcpmux_bind_current_workspace` during routine QA (triggers Space-wide approval modal). Reload MCP tools after UI binding or Surface changes.
345348

346-
**Test coverage (May 25, 2026):** Phase B filter shaping — 13 unit tests in `invoke.rs`, 17 integration tests in `meta_gateway_invoke.rs`, manual QA section 6 pass on live `github_list_issues`.
349+
**Test coverage (May 25, 2026):** Phase B filter shaping — 13 unit tests in `invoke.rs`, 16 integration tests in `meta_gateway_invoke.rs`, manual QA sections 0–11 pass on live gateway.
347350

348-
**Manual QA progress (May 25, 2026):**
351+
**Manual QA progress (May 25, 2026):** Overall **Ship**. Full section results in [`meta-gateway-invoke-qa.md`](./meta-gateway-invoke-qa.md). Highlights:
349352

350353
| QA section | Result | Notes |
351354
| ---------- | ------ | ----- |
@@ -354,7 +357,10 @@ This doc is the source of truth for the meta-gateway invoke model. Phases A–C
354357
| 2 — Fail-closed + recovery | ✅ Pass | Session disable → actionable error → enable → retry |
355358
| 3 — Search detail levels + compact schema | ✅ Pass | compact omits top-level description only |
356359
| 4 — Session toggle (list size unchanged) | ✅ Pass | search empty when disabled; 10 meta tools stable |
357-
| 5 — Pass-through without filter (Phase B) | ✅ Pass | GWorkspace `list_drive_items` @ `433e7bd`: 100 items, ~45k chars, no metadata |
358-
| 6 — Explicit filter (Phase B) | ✅ Pass | Plain-text `max_bytes` + live `github_list_issues` JSON filter (`max_rows`, `fields`, `summary`) |
360+
| 5 — Pass-through without filter (Phase B) | ✅ Pass | GWorkspace `list_drive_items`: 100 items, no metadata envelope |
361+
| 6 — Explicit filter (Phase B) | ✅ Pass | Plain-text `max_bytes` + live `github_list_issues` JSON filter |
359362
| 7 — Clone disambiguation | ✅ Pass | Personal vs S2H clone scoped correctly |
363+
| 8 — FeatureSet ACL (Phase C) | ✅ Pass | Partial GitHub tool set; invoke deny outside ACL |
364+
| 9 — Surfaced promotion (Phase C) | ✅ Pass | `github_list_issues` in tools/list + direct one-hop; `get_me` invoke-only |
360365
| 10 — Diagnostic list vs search | ✅ Pass | 120 tools both paths for GWorkspace Personal |
366+
| 11 — End-to-end agent task | ✅ Pass | Meta-only workflow; schema-first; filter truncation metadata |

0 commit comments

Comments
 (0)