feat(mcp): OAB MCP Facade MVP — shared openab-mcp crate + broker loopback HTTP server#1448
feat(mcp): OAB MCP Facade MVP — shared openab-mcp crate + broker loopback HTTP server#1448chaodu-agent wants to merge 2 commits into
Conversation
…er enforcement Implements the OAB MCP Adapter ADR (#1446) MVP slice in openab-agent: - New 'openab-agent mcp-facade' subcommand: inbound stdio MCP server exposing exactly search_capabilities / execute_capability (ADR §6.1, §6.4), backed by the existing McpRuntimeManager + meta_tool dispatcher (one capability dispatcher, two frontends). - search_capabilities: lazy discovery across all configured servers with per-provider failure isolation, name-collision qualification (server:tool), risk labels from MCP tool annotations, and redacted provider errors in an 'unavailable' list. - execute_capability: exact-name resolution against current discovery, then delegation to the shared call path (jsonschema argument validation, timeouts, circuit breaker, redaction). - tool_filter enforcement (accepted MCP ADR §5.6 contract, previously parsed but unenforced): glob include/exclude applied at discovery (fetch_tools, pre-cache) and execution (call_tool, pre-connect) for both the meta-tool and the facade. - rmcp features: + server, transport-io. Broker-side ACP session/new mcpServers advertisement is PR-2.
|
LGTM ✅ - The OAB MCP Facade MVP is correctly scoped and enforces the documented least-privilege tool surface. Note No Critical or Important findings were identified for the reviewed commit. What This PR DoesThis PR adds the agent-facing OAB MCP Facade MVP as an How It WorksThe facade reuses the existing Findings
Finding Details🟢 F1: Shared dispatcher and runtimeThe facade delegates execution through 🟢 F2: Defense-in-depth filter enforcementFiltered tools are removed before the shared tools cache is populated, and guessed filtered names are rejected before 🟢 F3: Failure isolation and redactionDiscovery continues across configured providers when one fails and returns a concise redacted error under 🟢 F4: Narrow protocol surfaceThe stdio server advertises exactly 🟢 F5: Focused regression coverageThe patch covers empty catalogs, unavailable providers, unknown execution names, duplicate-name qualification, glob semantics, include/exclude precedence, and the Gmail least-privilege profile shape. Addressing External Reviewer FeedbackNo external GitHub reviewer comments or submitted reviews were present for this round, so there is no unresolved external feedback to address. Baseline Check
5. Three Reasons We Might Not Need This PR
These are documented scope and rollout trade-offs, not blockers for this MVP. Validation
|
There was a problem hiding this comment.
LGTM ✅ - No Critical or Important findings were identified.
Consolidated review: #1448 (comment)
GitHub event: APPROVE
…back HTTP server Implements the OAB MCP Adapter ADR (#1446) MVP: - New workspace crate crates/openab-mcp: the MCP runtime extracted from openab-agent (client runtime, OAuth/PKCE + auth.json store, layered mcp.json config, tool cache, circuit breaker, jsonschema validation, redaction) plus the new OAB MCP Facade — an inbound MCP server exposing exactly search_capabilities / execute_capability. - Facade transport: loopback-only Streamable HTTP (http://127.0.0.1: <port>/mcp). Non-loopback binds are refused. Any MCP-capable coding CLI on the host can connect. - Broker activation: presence of [mcp] in config.toml starts the listener in-process (absent = no listener, backward compatible). openab-agent re-exports the crate (crate::{mcp,auth} shims + llm type layer + HostBridge) — one runtime, two hosts, zero duplication. - tool_filter enforcement (accepted MCP ADR §5.6, previously parsed but unenforced): glob include/exclude applied at discovery (pre-cache) and execution (pre-connect), for the meta-tool and facade alike. - openab-agent mcp-facade --listen: standalone facade server. - acp.rs env-mutating tests moved to temp_env (single global lock domain; a private ENV_LOCK raced with temp_env-based tests once the moved tests changed binary scheduling); session_new_missing_key now sandboxes HOME instead of deleting the developer's real auth.json. - Dockerfile.unified + ci-openab-agent.yml updated for the new member.
4c12c65 to
26a8395
Compare
0. Discord Discussion URL
Design discussed in the OpenAB Discord; the governing design is ADR PR #1446 (OAB MCP Adapter, §6.2 as amended for the broker-hosted loopback HTTP transport). No public Discord URL is included in the repository context.
1. What problem does this solve?
Implements the MVP of the OAB MCP Adapter ADR (#1446): the OAB MCP Facade — a loopback-only Streamable HTTP MCP server exposing exactly two agent-facing tools,
search_capabilitiesandexecute_capability, so any MCP-capable coding CLI on the host (Kiro, Claude Code, Codex, …) connects tohttp://127.0.0.1:<port>/mcp. The broker serves it in-process, enabled by a[mcp]section inconfig.toml.It also closes a real gap the ADR's least-privilege profiles depend on: the accepted MCP ADR (§5.6) documents
tool_filterglob include/exclude lists and the config schema parses them, but nothing enforced them until now.2. How it works
crates/openab-mcp: the entire MCP runtime extracted fromopenab-agent— outbound client (Streamable HTTP + stdio), OAuth/PKCE +auth.jsoncredential store (moved wholesale: atomic writes, corruption quarantine, refresh locks), layeredmcp.jsonconfig, tool cache, circuit breaker, jsonschema argument validation, secret redaction — plus the new facade. The broker links it;openab-agentre-exports it (crate::{mcp,auth}shims, shared LLM type layer,HostBridge). One runtime, two hosts, zero duplication.meta_toolcall path the native agent uses.tool_filterenforcement at both boundaries: discovery drops filtered tools before caching; execution rejects filtered names before any connect.[mcp]section → no listener, no behavior change.mcp.jsonremains the only provider source of truth ([mcp]carries listener settings only — ADR Alternative E).openab-agent mcp-facade --listen 127.0.0.1:8848serves the same facade standalone.from_configno longer implicitly binds an LLM provider (the crate has no concrete providers); the agent injects it viaset_provideralongside the host bridge — sampling behavior unchanged for the agent, correctly absent for the broker.3. Scope
Root workspace (new crate + broker wiring + Dockerfile.unified dummy-stage + CI paths) and
openab-agent(re-export shims). ACPsession/newmcpServersadvertisement is a documented follow-up — the loopback URL is already reachable by any CLI the operator points at it.4. Validation
On macOS arm64:
openab-mcp: 198 tests pass (moved runtime/meta-tool/auth tests + facade tests + loopback-bind validation)openab-agent: fmt clean, clippy-D warnings= 0 findings, 62 tests pass--workspace --all-features -D warnings= 0 errors;openab-coretests pass incl. 3 new[mcp]parse tests (only failure is the documented pre-existingsecrets::resolve_exec_nonzero_exitmacOS-env issue, identical on main)initializehandshake returnsoab-mcp-facade;tools/listreturns exactly the two tools;--listen 0.0.0.0:…refused[mcp]in config.toml binds the facade at startup and answersinitializewithoab-mcp-facadeon127.0.0.1:18850/mcpacp.rsenv-mutating tests moved from a privateENV_LOCKtotemp_env(single global lock domain); full agent suite green in parallel runsReview Contract
Goal
Ship the OAB MCP Facade MVP per ADR #1446 §6.2 (as amended): a loopback-only Streamable HTTP MCP server exposing exactly
search_capabilities/execute_capability, hosted in-process by the broker when[mcp]is present inconfig.toml, backed by the MCP runtime extracted into the sharedcrates/openab-mcpcrate — with the ADR'stool_filterleast-privilege contract actually enforced.Non-goals
session/newmcpServersadvertisement of the facade URL (follow-up; not required for CLIs configured directly).auth.jsonormcp.jsonlocations; provider config stays inmcp.json(no second source of truth).Accepted Residual Risks
[mcp]is enabled. Mitigations: loopback-only bind enforced at startup, explicit warnings in ADR/config example/startup log, opt-in activation; deployments colocating untrusted processes must not enable it.auth.jsoncode wholesale; behavior is regression-tested (198 tests) but subtle path/permission differences on exotic platforms would surface only in the follow-up provider validation.search_capabilitieswith no query lists the full filtered catalog (token-heavy on very large downstream catalogs); mitigated by enforced include filters and the query parameter.Acceptance Criteria
origin/mainon the same toolchain.[mcp]absent → no listener (config parse test); present → listener on the configured loopback address (live smoke), default127.0.0.1:8848.listenrefused at startup (unit test + live smoke).tools/listreturns exactly the two documented tools;initializeidentifiesoab-mcp-facade.tool_filter-excluded tool is invisible to discovery and rejected pre-connect at execution (regression tests, incl. the Gmail default-profile send/delete block).unavailablewith a redacted error (regression test).openab-agentbehavior unchanged through the re-export shims (62 agent tests green, incl. ACP session, model-switch, sampling paths).Follow-ups
session/newmcpServersadvertisement of the facade URL (currently hardcoded[]in openab-core, unchanged here).tools/list_changed-driven cache invalidation surfaced through the facade; optional result cap/pagination for large catalogs.openab-agent's now-redundant direct deps further (kept minimal already).