Owner MCP facade = generic discovery + invoke over the two containers

Updated · View the entry on sijie.xyz ↗

Decision (2026-07-27, owner)

The owner's agent (owner's own MCP client / Claude Code) must reach capabilities the same generic way the visitor agent does: discover from the container, invoke by name. No explicit per-capability host code.

Give the owner agent, over MCP, two generic discover+invoke doors — one per container:

container discover invoke
capability (capreg) list registered capabilities invoke a capability's tool by name, in owner context
connector (Hub) list connected connectors connector.invoke(category, verb, args) (opaque door — already exists)

"这样就通用了" — instead of N explicit owner caps, TWO generic doors. Owner books = discover the booker capability → invoke it; owner sets policy = same; owner uses calendar = discover the calendar connector → invoke. The owner side is zero explicit cap code.

What this replaces / deletes

  • The entire internal/plugins/ownercore explicit cap_* surface (~29 hand-written owner caps: cap_calendar / cap_booking / cap_chat / cap_corpus_* / …). They become discovered from the registry, not hand-written. cap_calendar/cap_booking in particular are the anti-pattern the owner flagged: an externalized capability (booker sandbox) re-implemented as explicit host code. See external-means-fully-external.
  • This is the endgame of facade-from-registry (facade-parity): every facade generated-from / verified-against the one capability registry.

The one real mechanism to build (the crux)

capreg already had List() (discovery was done). The missing piece was owner-context invocation of a capability — especially a sandbox capability. Until then a sandbox cap was dialed only inside a visitor session (Capability.VisitorBinding(ctx, AssembleInput) → dialAndList per ConversationID; extMCPCapability/mcpAppCapability were ShapeVisitorOnly, OwnerMCPBindings() returned []). There was no owner-context dial: the owner has no visitor session, only an owner_id.

Built (7f02bccc1, 2026-07-29): the manifest declares owner tools as data (mcpplugin.Manifest.OwnerTools, internal/capabilities/mcpplugin/manifest.go), and routes/capload/capreg_mcp_app_owner.go turns each declared tool into an OwnerMCPBindings() entry whose handler dials the sandbox only when invoked, with owner context (owner_id, no visitor/code/conversation) and forwards the tools/call. The table is enumerated at assembly time so facade-parity reconciles against it without dialing anything at boot. The sandbox tool runs its logic via the same generic reach-back it already used (connector.invoke + capstore + owner.meta); booker declares its owner-facing tools (owner list_slots, calendar_cancel_booking, policy get/set, bookings-list) in mcp-servers/booker/main.go, and the host's duplicate policy/slots evaluator was deleted in the same commit. The same hole reopened once more for settings — a capability could declare owner tools but not owner-tunable fields, so booker's booking policy was hand-written on the host again — and was closed the same way: Manifest.Config / capconfig (cc5c1db47, 2026-07-31).

Consequence for internal/

Reaching the "plugins gone" target was gated on this build, and it went through: internal/plugins dissolved into owner/ (fea7ae93f, 2026-07-29), the domains declare their own ops and the dispatcher only aggregates (53733b661, 2026-08-01), and ownercore itself is gone (f35e82c04, 2026-08-02). As of 2026-09-07 internal/ holds 11 directories — the 8 core domains + capabilities + routes + infra — which is exactly the whitelist check-internal-dirs.sh enforces.

Scope / verification

Foundational, replaces ownercore, must be verified by real owner + visitor booking e2e (gcal + sandbox rebuild; make test-only SPEC=booking). Compile+lint green is NOT sufficient.

Status

Shipped. Owner-context invocation 2026-07-29 (7f02bccc1), plugins dissolved 2026-07-29 (fea7ae93f), ownercore deleted 2026-08-02 (f35e82c04). Proof files: internal/routes/capload/capreg_mcp_app_owner.go, internal/capabilities/mcpplugin/manifest.go; internal/plugins/ and internal/owner/ownercore/ no longer exist on main.

Related notes