Skip to content

MCP

The Model Context Protocol (MCP, modelcontextprotocol.io) lets an LLM agent enumerate available tools and resources, then call them by structured spec. Matrix's actor declarations map directly onto MCP primitives: accepts ops are tools, state records and the blackboard are resources. An MCP server adapter walks $introspect across the bus and surfaces the result as an MCP catalog.

Source: WORKSTREAMS/core-and-packaging/ACTOR-COMMUNICATION-CONTRACT.md § "How they map to protocol gateways". Quoted: "accepts → MCP Tools, state → MCP Resources, blackboard → MCP Resources."

The mapping

Matrix declarationMCP primitive
static acceptsTools (call by name with structured arguments)
static stateResources (read by URI, return last value)
static blackboardResources (shared facts, optionally paid)
static emits(no MCP primitive) — events are not yet first-class in MCP
static streams(no direct mapping)
static subscribes(informational; consumed for prompt context)

Each MCP tool's name is the actor's mount path joined with the op name: <authorityRoot>/<mount>/<op>. Examples:

COM.NIMBLETEC.RICHARD-SANTOMAURO/system.registry/registry.resolve
COM.NIMBLETEC.RICHARD-SANTOMAURO/chat.conversation/chat.send

MCP resources use a similar URI shape with state or blackboard as the discriminator.

Why this exists

The core protocol vision (MASTER-PLAN.md Part 2):

"Every actor is an MCP tool. Automatically. No registration."

"Claude: 'I need to check inventory at Acme'→ $introspect matrix://acme.com/inventory→ accepts: ['inventory.check', 'inventory.reserve']→ schema: { sku: string, warehouse?: string }→ invoke: inventory.check { sku: 'WIDGET-42' }→ response: { available: 147, warehouses: ['east', 'west'] }"

The MCP bridge makes this concrete: an LLM agent can target any Matrix actor via the same MCP discovery flow it uses for any other tool host. Discovery comes for free from $introspect; argument schemas come from the accepts.*.schema field; descriptions come from the description field on every op and field.

Why descriptions matter so much

ACTOR-COMMUNICATION-CONTRACT.md is emphatic on this:

"Rule: EVERY field has a description. If a field doesn't have a description, it's a bug. No exceptions."

LLM agents do not call ops they don't understand. The description field is what they read to decide whether and how to call. Sparse descriptions = unused tools.

Tool descriptions vs op descriptions

The MCP tool description for an op is the description field on the accepts declaration:

typescript
static accepts = {
  'market.quote': {
    description: 'Get the current price, volume, and bid/ask spread for a stock symbol. Returns real-time data during market hours, last close price after hours.',
    schema: { symbol: { type: 'string', description: 'Ticker symbol, e.g. "TSLA"' } },
    returns: { price: { type: 'number', description: 'Last trade price in USD' }, ... },
  },
};

The MCP server adapter takes:

  • Tool name from the mount + op
  • Tool description from description
  • Tool input schema from schema (per-field types and descriptions)
  • Tool output schema from returns

The agent sees a fully-typed tool and can construct calls without prior knowledge.

Arg validation

MCP requires JSON-Schema-style argument validation. The Matrix FieldDescriptor shape is convertible to JSON Schema:

FieldDescriptorJSON Schema
type: 'string'{ "type": "string" }
type: 'array', items: ...{ "type": "array", "items": ... }
type: 'object', properties: ...{ "type": "object", "properties": ... }
optional: truerequired: [...] excludes the field
default: <v>{ "default": <v> }
enum: [...]{ "enum": [...] }

The MCP adapter generates JSON Schema on demand from the actor's accepts block.

Status today

Status: target state, partial. A first-class MCP server adapter that walks the bus is workstream material. Today, the building blocks are present:

  • Every actor returns its full $introspect block.
  • The static skills field exposes higher-level skill bundles for agent discovery.
  • The system.catalog catalog.search op already supports filtering by accepts/emits/skill, which is the discovery primitive an MCP adapter would use.

What's missing: a process that runs an MCP server (stdio or SSE) and maps catalog entries to MCP tools/resources. Several agents in the ecosystem (Claude Code, Cursor) already consume MCP; the adapter wiring is the remaining work.

Resource model

MCP resources are addressed by URI. For Matrix state, the URI shape is:

matrix://<root>/<mount>/$state/<key>
matrix://<root>/<mount>/$blackboard/<factName>

The MCP server adapter resolves the URI, performs a getState or readBlackboard against the actor, and returns the value as an MCP resource read.

Reads happen on demand (no proactive subscription). Live agents that want updates can re-read; a future revision could expose $state watches as MCP server-sent updates.

See also