Skip to content

Contract (@exp/emcp)

eMCP’s contract: how an Exponential app exposes MCP tools (exposes bindings, ${input.x} resolution, <app>__<tool> naming), who sees them (the grant model and the access rule), the mcp_calls audit row, app automations (trigger matching, the loop guard, the sponsor), the vault format and the upstream OAuth states. Each fixtures/*.json is the spec (its $comment) plus the cases; src/ holds the pure TypeScript references that replay them. The server half (apps/web/src/lib/emcp) and the Rust crate emcp replay the same fixtures.

import { validateExposes, qualifiedToolName } from "@exp/emcp/exposes"
import fixture from "@exp/emcp/fixtures/exposes.json" with { type: "json" }
fixture covers
exposes.json the binding format, validation, template resolution, tool naming + collisions, _meta provenance
access.json mcp_grants (kind, all_apps, app_ids, access), the member policy, effective access, visibility, revocation
audit.json the mcp_calls row, canonical JSON + the args hash, statuses, 90-day retention
vault.json VAULT_KEY keyrings, ev1.<kid>.<iv>.<body> AES-256-GCM, the row-binding AAD, rotation, exact vectors
upstream.json one member’s upstream connection: states, transitions, callable, the consent page’s missing upstreams; binding = upstream:<server>/<tool> read bindings (parse, declare, read-only, cache key, value, banners)
hub.json the device’s local hub (#EXP-1507): server names, a run’s tool list under its grant (a level per server), routing, the mcp_calls row per call

Licence: Apache-2.0