Skip to content

feat(mcp): OAB MCP Facade MVP — shared openab-mcp crate + broker loopback HTTP server#1448

Merged
thepagent merged 3 commits into
mainfrom
feat/oab-mcp-facade-mvp
Jul 24, 2026
Merged

feat(mcp): OAB MCP Facade MVP — shared openab-mcp crate + broker loopback HTTP server#1448
thepagent merged 3 commits into
mainfrom
feat/oab-mcp-facade-mvp

Conversation

@chaodu-agent

@chaodu-agent chaodu-agent commented Jul 24, 2026

Copy link
Copy Markdown
Collaborator

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_capabilities and execute_capability, so any MCP-capable coding CLI on the host (Kiro, Claude Code, Codex, …) connects to http://127.0.0.1:<port>/mcp. The broker serves it in-process, enabled by a [mcp] section in config.toml.

It also closes a real gap the ADR's least-privilege profiles depend on: the accepted MCP ADR (§5.6) documents tool_filter glob include/exclude lists and the config schema parses them, but nothing enforced them until now.

2. How it works

config.toml                       coding CLI (Kiro / Claude Code / …)
  [mcp]                              │  MCP over http://127.0.0.1:8848/mcp
  listen = "127.0.0.1:8848"          ▼
        │                     OAB MCP Facade  (in openab broker process)
        └── presence spawns ──►  search_capabilities / execute_capability
                                     │
                              crates/openab-mcp  (extracted shared runtime)
                                     │  lazy connect · tool_filter · OAuth
                                     ▼  jsonschema · breaker · redaction
                              downstream MCP servers (mcp.json)
  • New workspace crate crates/openab-mcp: the entire MCP runtime extracted from openab-agent — outbound client (Streamable HTTP + stdio), OAuth/PKCE + auth.json credential store (moved wholesale: atomic writes, corruption quarantine, refresh locks), layered mcp.json config, tool cache, circuit breaker, jsonschema argument validation, secret redaction — plus the new facade. The broker links it; openab-agent re-exports it (crate::{mcp,auth} shims, shared LLM type layer, HostBridge). One runtime, two hosts, zero duplication.
  • One dispatcher, two frontends (ADR §6.4): the facade delegates to the same meta_tool call path the native agent uses.
  • tool_filter enforcement at both boundaries: discovery drops filtered tools before caching; execution rejects filtered names before any connect.
  • Loopback-only, no auth: non-loopback binds are refused at startup; the endpoint carries no authentication layer, so the host/pod boundary is the trust boundary (documented in the ADR, the config example, and the startup log line).
  • Backward compatible: no [mcp] section → no listener, no behavior change. mcp.json remains the only provider source of truth ([mcp] carries listener settings only — ADR Alternative E).
  • openab-agent mcp-facade --listen 127.0.0.1:8848 serves the same facade standalone.
  • from_config no longer implicitly binds an LLM provider (the crate has no concrete providers); the agent injects it via set_provider alongside 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). ACP session/new mcpServers advertisement 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
  • Root workspace: clippy --workspace --all-features -D warnings = 0 errors; openab-core tests pass incl. 3 new [mcp] parse tests (only failure is the documented pre-existing secrets::resolve_exec_nonzero_exit macOS-env issue, identical on main)
  • Live smoke (standalone): initialize handshake returns oab-mcp-facade; tools/list returns exactly the two tools; --listen 0.0.0.0:… refused
  • Live smoke (broker): binary with [mcp] in config.toml binds the facade at startup and answers initialize with oab-mcp-facade on 127.0.0.1:18850/mcp
  • Test-race fix verified: 8 acp.rs env-mutating tests moved from a private ENV_LOCK to temp_env (single global lock domain); full agent suite green in parallel runs

Review 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 in config.toml, backed by the MCP runtime extracted into the shared crates/openab-mcp crate — with the ADR's tool_filter least-privilege contract actually enforced.

Non-goals

  • Authentication on the facade endpoint (loopback + host boundary is the MVP trust model; token/socket-permission scheme is a follow-up).
  • ACP session/new mcpServers advertisement of the facade URL (follow-up; not required for CLIs configured directly).
  • Notion/Gmail live OAuth validation (ADR rollout steps 2–3; requires interactive login).
  • Renaming/moving auth.json or mcp.json locations; provider config stays in mcp.json (no second source of truth).
  • Gateway crate changes (it does not depend on openab-mcp).

Accepted Residual Risks

  • Unauthenticated loopback endpoint: any process on the host can call authorized capabilities when [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.
  • Broker gains the runtime dependency set (rmcp, jsonschema, oauth2, axum) — accepted for one-runtime/zero-duplication; binary size impact is bounded and the gateway is unaffected.
  • The extraction moves auth.json code 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_capabilities with no query lists the full filtered catalog (token-heavy on very large downstream catalogs); mitigated by enforced include filters and the query parameter.
  • Facade sampling requests are declined (broker has no LLM provider) — correct for a non-LLM host, noted for provider docs.

Acceptance Criteria

  • Both workspaces: fmt/clippy/test green per §4 with zero introduced clippy findings vs origin/main on the same toolchain.
  • [mcp] absent → no listener (config parse test); present → listener on the configured loopback address (live smoke), default 127.0.0.1:8848.
  • Non-loopback listen refused at startup (unit test + live smoke).
  • Facade tools/list returns exactly the two documented tools; initialize identifies oab-mcp-facade.
  • A tool_filter-excluded tool is invisible to discovery and rejected pre-connect at execution (regression tests, incl. the Gmail default-profile send/delete block).
  • One failing provider does not fail discovery; it is reported in unavailable with a redacted error (regression test).
  • openab-agent behavior unchanged through the re-export shims (62 agent tests green, incl. ACP session, model-switch, sampling paths).

Follow-ups

  • ACP session/new mcpServers advertisement of the facade URL (currently hardcoded [] in openab-core, unchanged here).
  • Facade endpoint authentication (bearer token or unix-socket permissions).
  • Notion, then Gmail (preview) provider validation and checked-in profile examples (ADR rollout steps 2–3).
  • tools/list_changed-driven cache invalidation surfaced through the facade; optional result cap/pagination for large catalogs.
  • Consider trimming openab-agent's now-redundant direct deps further (kept minimal already).

…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.
@chaodu-obk

chaodu-obk Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

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 Does

This PR adds the agent-facing OAB MCP Facade MVP as an openab-agent mcp-facade stdio server. It exposes exactly two tools, search_capabilities and execute_capability, and makes the existing tool_filter configuration enforceable for discovery and execution.

How It Works

The facade reuses the existing meta_tool dispatcher and McpRuntimeManager, so schema validation, timeouts, circuit breaking, redaction, and policy enforcement remain single-sourced. Discovery connects lazily to configured providers, isolates provider failures in an unavailable list, filters tools before caching, and qualifies duplicate names as server:tool. Execution resolves an exact current discovery result and applies the pre-connect filter gate before dispatch.

Findings

# Severity Finding Location
1 🟢 Shared dispatcher and runtime preserve one enforcement path for the native meta-tool and the facade. openab-agent/src/mcp/facade.rs:167-190
2 🟢 tool_filter is applied before caching and is independently enforced before a guessed tool can connect. openab-agent/src/mcp/meta_tool.rs:156-166, openab-agent/src/mcp/meta_tool.rs:449-460
3 🟢 Provider failure isolation and secret-redacted unavailable responses are implemented and covered by tests. openab-agent/src/mcp/facade.rs:114-131, openab-agent/src/mcp/facade.rs:423-447
4 🟢 The public facade surface is constrained to the two documented tools with schemas and initialization metadata. openab-agent/src/mcp/facade.rs:208-247, openab-agent/src/mcp/facade.rs:267-283
5 🟢 Duplicate qualification and include/exclude filter semantics have focused regression tests. openab-agent/src/mcp/facade.rs:372-388, openab-agent/src/mcp/config.rs:559-615
Finding Details

🟢 F1: Shared dispatcher and runtime

The facade delegates execution through meta_tool::dispatch(Action::Call) instead of duplicating provider lifecycle or security behavior. This keeps argument validation, timeout handling, breaker behavior, and redaction aligned with the existing native path.

🟢 F2: Defense-in-depth filter enforcement

Filtered tools are removed before the shared tools cache is populated, and guessed filtered names are rejected before connect. This satisfies the least-privilege contract at both discovery and execution boundaries.

🟢 F3: Failure isolation and redaction

Discovery continues across configured providers when one fails and returns a concise redacted error under unavailable, preserving access to healthy providers without exposing provider secrets.

🟢 F4: Narrow protocol surface

The stdio server advertises exactly search_capabilities and execute_capability, identifies itself as oab-mcp-facade, keeps logs on stderr, and serializes provider results as MCP text content.

🟢 F5: Focused regression coverage

The 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 Feedback

No external GitHub reviewer comments or submitted reviews were present for this round, so there is no unresolved external feedback to address.

Baseline Check
  • PR opened: 2026-07-24
  • Base branch: main
  • Merge-base: 967270087ab74b32bcba9f6bc89a402e7abc3aca
  • Main already has: the existing downstream MCP runtime and native mcp meta-tool, but not the inbound facade, its two-tool protocol surface, or enforced tool_filter behavior.
  • Net-new value: a per-session stdio facade with progressive disclosure and least-privilege filtering, without changing broker advertisement or the native meta-tool contract.

5. Three Reasons We Might Not Need This PR

  1. The broker integration is deferred - Until the follow-up broker advertisement lands, the new facade command is not yet exposed through normal ACP session setup.
  2. Provider discovery remains potentially large - An unqualified search can return a large filtered catalog, so production profiles may still need result caps or pagination.
  3. The facade adds another protocol boundary - Maintaining a second MCP server frontend increases integration surface compared with using the native meta-tool alone.

These are documented scope and rollout trade-offs, not blockers for this MVP.

Validation

  • git diff --check: passed.
  • Local source review completed against origin/main...pr-1448 at the exact requested SHA.
  • Rust validation could not run in this environment because cargo, rustc, and rustup are not installed.

chaodu-obk[bot]
chaodu-obk Bot previously approved these changes Jul 24, 2026

@chaodu-obk chaodu-obk 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.

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.
@chaodu-agent
chaodu-agent force-pushed the feat/oab-mcp-facade-mvp branch from 4c12c65 to 26a8395 Compare July 24, 2026 11:40
@chaodu-agent chaodu-agent changed the title feat(agent): OAB MCP Facade MVP — mcp-facade stdio server + tool_filter enforcement feat(mcp): OAB MCP Facade MVP — shared openab-mcp crate + broker loopback HTTP server Jul 24, 2026
The workspace gained crates/openab-mcp but only Dockerfile.unified's
dummy stage was updated; the base and per-CLI Dockerfiles failed at
'cargo build' with 'failed to load manifest for workspace member'.
Apply the same COPY/mkdir/stub/rm/touch pattern everywhere (incl.
Dockerfile.gateway/builder/native/agentcore, which share the pattern
but are outside this PR's smoke matrix).
@chaodu-agent

Copy link
Copy Markdown
Collaborator Author

Gmail live validation report (ADR rollout step 3, run early per maintainer request)

Ran the Gmail hosted-MCP profile end-to-end against gmailmcp.googleapis.com/mcp/v1 using this PR's binaries (8f6ff4f, macOS arm64: broker + openab-agent). Summary: the facade side is fully green; execution is blocked upstream by Google's Developer Preview enrollment; and one real bug found in the OAuth flow (no refresh token for Google providers).

What passed ✅

Step Result
Custom-provider OAuth (PKCE S256 paste-back, provider: "google") ● logged in: gmail; credentials stored in auth.json (0600)
mcp doctor config / oauth / live connect all green
Standalone facade (mcp-facade, loopback) initializeoab-mcp-facade; tools/list = exactly the two tools
search_capabilities 6 capabilities returned (create_draft, list_drafts, get_thread, get_message, search_threads, list_labels) — the tool_filter include-list correctly hid label_*/create_label (§6.5 least-privilege enforced at discovery)
Lazy connect + schema passthrough Google's full input_schema (incl. $defs) surfaced intact through execute_capability discovery

Blocked upstream ⛔ — Google preview gating (not a PR bug)

Every tools/call returns isError: true / "The caller does not have permission". Isolation chain with the same access token:

  • Gmail REST (gmail.googleapis.com/gmail/v1/users/me/labels) → 200 OK
  • Gmail MCP initialize + tools/listOK
  • Gmail MCP tools/call (any tool) → denied
  • Bypassing the facade and calling gmailmcp.googleapis.com directly → identical denial (facade behavior is byte-equivalent to a direct client)
  • X-Goog-User-Project header, and a token re-minted after program signup → no change

Cause: the Gmail MCP server requires Google Workspace Developer Preview Program enrollment — per-account and per-GCP-project (the program registers your project; approval is manual, "about a week"). Suggest adding to the ADR §6.5 Gmail profile / §9 risks: execution (not discovery) is gated on DPP enrollment of both the authorizing account and the OAuth client's GCP project; REST access is not a valid preflight signal for MCP access.

Bug found 🐛 — no refresh token from Google (headless blocker)

The paste-back authorize URL contains no access_type=offline (Google's requirement for issuing refresh tokens; prompt=consent also relevant for re-grants). Result: auth.json's token_response has access_token / expires_in: 3600 / refresh_token_expires_in — but no refresh_token. Confirmed by a failed refresh grant (HTTP 400) and a second interactive login (same shape).

Impact: for any Google provider the session dies after ~1h and recovery requires an interactive re-login — unusable for the headless bot deployments (ECS) this is destined for. Suggested fix: when resolving a custom provider whose authorize endpoint is accounts.google.com, append access_type=offline (+prompt=consent) to the authorize URL, or add a general oauth.extra_authorize_params map to OAuthConfig so provider quirks don't need code changes.

Recommendation — today's evidence tips rollout step 5 toward the native adapter

The ADR already stages this decision (§10 step 5: hosted MCP vs native Gmail adapter) and documents OpenWorker's native Gmail REST connector as prior art (§5.3). Today's run adds three concrete data points in favor of the Capability Plugin / native Gmail adapter path (§6.1), at least until Gmail MCP reaches GA:

  1. The same OAuth token that MCP rejects works perfectly against Gmail REST (verified live: users/me/labels → 200). An OpenWorker-style connector (search_threads/get_message/create_draft over REST, exactly the six-capability §6.5 profile) needs no DPP enrollment, no per-project registration, no preview wait.
  2. DPP terms make hosted Gmail MCP unshippable pre-GA: program terms (ii)/(iv) prohibit including preview features in public applications and granting end users outside the developer's domain access to pre-GA integrations. For OAB as a product, the hosted path is a validation-only track until GA — the native adapter is the only Gmail path that can reach users today.
  3. The facade makes the swap invisible: §6.1 already normalizes native adapters behind search_capabilities/execute_capability, so shipping a native Gmail connector now and re-pointing to hosted MCP at GA changes zero agent-facing surface. Adopt OpenWorker's specific lessons per §5.3: approval-gated send (drafts-only in our profile), per-account token refresh (which also sidesteps the refresh-token bug above at the adapter level — the adapter owns its access_type=offline), and privacy filters before content reaches the agent.

Hosted-MCP validation continues in parallel (DPP application submitted) — it remains the right long-term default per the ADR; this only re-sequences what ships first.

State

  • DPP application submitted; will re-run the execution matrix (search/read/draft + draft-review-cleanup) on approval — token re-auth only, no other changes needed.
  • Preview image ghcr.io/openabdev/openab:pr1448 built green; b2 (ECS) e2e is staged behind the same approval — and behind the refresh-token fix, which is a hard prerequisite for headless.

@thepagent
thepagent merged commit 3149c1d into main Jul 24, 2026
47 of 48 checks passed
@chaodu-agent

Copy link
Copy Markdown
Collaborator Author

Follow-up: DPP application rejected — hosted Gmail MCP is Workspace-account-only during preview

Closing the loop on the validation report above. The Developer Preview Program application was rejected: "We could not verify your Google Workspace account" — the program requires a Google Workspace account; consumer @gmail.com accounts are not accepted.

Implications, stronger than the original finding:

  1. Consumer Gmail mailboxes cannot use the hosted Gmail MCP server at all during preview — this is not a waiting-period problem. Even a successful re-application with a Workspace account would authorize that Workspace mailbox, not a consumer one.
  2. The native adapter (feat(mcp): native Gmail adapter — §6.5 profile over GA REST as stdio MCP server #1449) is therefore the only Gmail path for consumer-account use cases until Google opens the hosted server to consumer accounts (presumably at GA) — upgrading it from "pre-GA ship vehicle" to "sole option for a whole account class".
  3. The hosted-MCP validation track (ADR rollout step 3) can only be completed against a Workspace mailbox; recommend noting in the ADR §6.5 Gmail profile that the hosted path's availability is account-type-dependent, and re-evaluating at GA.

Meanwhile #1449 has been validated end-to-end in production topology: b2 (ECS) → OAB MCP Facade (search_capabilities/execute_capability) → gmail-native adapter (loopback Streamable HTTP) → Gmail GA REST, with the facade audit trail confirming the dispatch (call_tool server="gmail" tool="search_threads" outcome="ok").

thepagent pushed a commit that referenced this pull request Jul 25, 2026
…MCP server (#1449)

* feat(mcp): native Gmail adapter — six-tool §6.5 profile over Gmail REST as stdio MCP server

Capability Plugin per ADR §5.3/§6.1 and rollout step 5, motivated by live
validation (PR #1448): hosted Gmail MCP gates tools/call behind Workspace
Developer Preview enrollment and its terms bar pre-GA shipping, while the
same OAuth token works against GA Gmail REST.

- crates/openab-mcp/src/native/gmail.rs: search_threads / get_thread /
  get_message / list_labels / list_drafts / create_draft with names and
  argument shapes mirroring the hosted server (GA cut-over = config-only);
  drafts-only write surface; header-injection-hardened MIME builder;
  Gmail-id validation before path use.
- Own OAuth login (PKCE S256 paste-back) sending access_type=offline +
  prompt=consent — stores a refresh token (the rmcp-driven flow cannot),
  single-flight refresh with rotation-tolerant persistence via the shared
  auth.json McpCredentialStore under the gmail-native key.
- Served over stdio (openab-agent gmail-native serve|login): registered in
  mcp.json as a normal stdio server, so tool_filter, schema validation,
  breaker, timeouts, and redaction apply unchanged.

* style: cargo fmt + fix injection-test assertion (address guard trips on control char first)

* style: final rustfmt

* refactor(gmail-native): serve over loopback Streamable HTTP instead of stdio

Same transport and trust model as the OAB MCP Facade (require_loopback
reused; non-loopback binds refused; no auth layer — host boundary is the
trust boundary). mcp.json registration becomes a "type": "http" entry at
http://127.0.0.1:8850/mcp. Drops the rmcp transport-io feature.

* docs: gmail-native operator guide (setup, registration, security notes)

* refactor: move gmail-native serve|login to the broker binary

openab-agent is the coding agent (ACP client + MCP client toolchain);
long-running serving surfaces belong to the broker binary, which every
bot image already ships — the b2 validation required smuggling the agent
binary into the container precisely because the adapter lived on the
wrong binary. openab-agent keeps the client-side mcp CLI only.

* style: fmt + doc reference fix

* refactor(cli): nest under `openab mcp <addon> <action>` namespace

Maintainer direction: MCP add-ons get one namespace (openab mcp
gmail-native serve|login); long-running serving remains config-driven
([mcp] + openab run). Standalone/dev serving and interactive logins are
the namespace's job.

* style: fmt

---------

Co-authored-by: chaodu-agent <chaodu-agent@users.noreply.github.com>
thepagent pushed a commit that referenced this pull request Jul 25, 2026
* docs: OAB MCP Facade operator guide

Documents the #1448 feature: enablement (broker [mcp] / standalone),
the two-tool surface, audit lines, client registration (incl. the kiro
--agent config gotcha), trust model, and the pod-vs-fleet positioning
relative to octobroker (ADR §4.1).

* docs: add mermaid architecture diagram to the facade guide

* docs: describe facade-only run mode (replaces removed openab-agent mcp-facade)

---------

Co-authored-by: chaodu-agent <chaodu-agent@users.noreply.github.com>
pahud pushed a commit to brettchien/openab that referenced this pull request Jul 25, 2026
…browser tunnel wiring alongside the [mcp] facade block; Cargo.lock regenerated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants