mcp capability plugins

Updated · View the entry on sijie.xyz ↗

This page is the capability plane — StandMeet as MCP host (see confusables). The agent's external tools / skills (retrieval, booker, summarize, ask_visitor + owner-registered ext-mcp). Already plugin-shaped — one of the two plugin axes (the other is connector-plugins; the axes touch only at connector-deps).

  • Sandboxed: each runs in a bubblewrap namespace, --unshare-net (network-isolated), reaching the host only through one narrow unix socket per capability (/run/standmeet/<cap>.sock). ask_visitor has no socket at all — fully offline. (cmd/server/axiscap/register.go registers them; internal/capabilities/sandbox/ is the bwrap runner; the builtin manifests are data at backend/capabilities/<id>/manifest.yaml, loaded by backend/capabilities/loader.go.)
  • Builtin == third-party: builtins are static binaries loaded through the same sandbox_stdio path as third-party plugins; the contract is a manifest + the MCP protocol, not Go imports.
  • Manifest Requires: a capability declares its deps (calendar.book → requires: [calendar], mail.send → [smtp]); the host validates them at boot (axiscap/register.go:97) and gates exposure if a dep isn't connected — the Android-manifest model of prompt-injection-is-buffer-overflow. A second, per-tool layer VisitorToolRequires (tool → action-qualified dep such as calendar:events.insert, F-B-8) hides only the tool the connection cannot perform, instead of the whole capability.

Class view

classDiagram
  class Manifest {
    <<mcpplugin manifest>>
    ID, Version, Title string
    Shape Shape
    Requires []string
    VisitorTools []string
    VisitorToolRequires map[string][]string
    OwnerTools []OwnerTool
    Config, CodeConfig, RoleConfig []ConfigField
    Quota *QuotaDecl
    ClaimGate *ClaimGateDecl
    PromptFragmentID string
    ACL string
    Transport Transport
  }
  class Transport {
    Kind string
    Command string + Args []string
    URL string
    Env, Headers map[string]string
    InProcessServer *MCPServer
    Sandbox *Sandbox
  }
  class Sandbox {
    PluginDir string
    HostOps []string - op names, socket path derived by host
    AllowNet bool
    Workspace bool
  }
  class PluginSpec {
    <<agentcore PluginSpec - the Driver hand-off>>
    ID, Command string
    Args []string
    Env map[string]string
    HostOps []string
    RawToolNames bool
    ACLAlways bool
  }
  Manifest *-- Transport
  Transport *-- Sandbox
  Manifest ..> PluginSpec : flattened for the agent loop

Transport.Kind selects the wire (stdio child / http / in-process); Sandbox.HostOps names the host ops (fixed vocabulary in routes/hostdesk) that the capsocket back-channel serves — the socket path is derived by the host from the plugin id, never declared (renamed from HostSockets when hostdesk landed, 2026-08-01, 5cb8d8464); Requires feeds connector-deps; PromptFragmentID feeds the prompt-hash regression.

How a capability gets the session's trusted scope without the LLM forging it → trusted-identity-via-meta.

Related notes