New:Introducing Socket Scanning for VS Code Marketplace Extensions.Learn more β†’
Get Started

@ultimat3/mcp

Package Overview
Dependencies
Maintainers
1
Versions
98
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@ultimat3/mcp

MCP server, dev tools, and the action-to-tool projection β€” one authz system, two surfaces

Source
npmnpm
Version
27.2.0
Version published
Weekly downloads
2K
-63.46%
Maintainers
1
Weekly downloads
Β 
Created
Source

@ultimat3/mcp πŸ€–

The MCP surface. An agent that can reach this package needs no framework documentation β€” it asks instead of guessing.

The dev server: x mcp serve

ToolScopeAnswers
routes.listdev:readroute table β€” url, render mode, offline strategy, hydrate, budget
schema.describedev:readentities with columns, types, invariants
policies.listdev:readevery policy: permission, subject, enforcement points
actions.describedev:readactions + queries: input/output schema, policy, cache tags, MCP exposure
jobs.inspectdev:readjob definitions, retry policy, steps (omit name for all)
queue.depthdev:readpending / running / failed per queue
manifest.readdev:readx.manifest.json verbatim
errors.explaindev:readstable X_* code β†’ cause + exact fix command + docs
db.querydb:readread-only, enforced four ways β€” SELECT-only role, BEGIN READ ONLY, one-statement parse, 5s/1000-row/256 KiB caps
db.migratedb:migratebranch DB only β€” refuses production and any non-branch target
tests.rundev:testruns the suite (executes project code)
verify.rundev:testx verify β€” the shippable contract
logs.taildev:logslast N lines, optionally per runtime role

db.query and db.migrate are gated and say so in their own description, so a model that reads only the catalog still knows what it is holding.

db.query's four layers

LayerMechanismWhere
1. Roleultimate_readonly β€” NOLOGIN, SELECT on every table present and future, nothing on sequences; assumed with SET LOCAL ROLE inside the transaction, never via a second connection string@ultimat3/db
2. TransactionBEGIN READ ONLY … ROLLBACK on one reserved connection β€” Postgres refuses the write even if a grant is wrong@ultimat3/db
3. Parseone statement, a read leader, no mutating keyword at statement level, no lock β€” clause or pg_advisory_* call β€” and no call into a banned function family, matched by prefix of the called name β€” quoted and schema-qualified spellings included β€” so a new spelling is refused by default and a column sharing a prefix is not; on a form with literals and comments blankedreadonly-sql.ts
4. LimitsSET LOCAL statement_timeout, a hard 1000-row ceiling (limit clamps into it, never past it) and a 256 KiB byte capquery-limits.ts

Banned function families β€” each is a ban already made elsewhere in another spelling: pg_notify (= NOTIFY, a write keyword); server control (pg_cancel_backend, pg_terminate_backend, pg_reload_*, pg_rotate_*, pg_switch_*, pg_promote, pg_wal_replay_*); replication (pg_logical_*, pg_create_*, pg_drop_*, pg_replication_* β€” advancing a slot is a write no ROLLBACK undoes); pg_file_*; txid_current / pg_current_xact_id; pg_import_*; SQL run from a string (*_to_xml*, ts_stat, ts_rewrite). U&"…" is refused, never decoded; a keyword is a whole identifier (set2 is a column). Catalog VIEWS (pg_replication_slots) are read from, never called.

Banned SQL functions are matched as a prefix of a CALLED function name β€” add a family, never a name (pg_sleep_for passed an exact pg_sleep ban). The unit is the call (name(), so a column pg_sleep_for_seconds is fine; the call scan keeps quoted-identifier content ("pg_advisory_lock"(1) is a call), the keyword scan blanks it. pg_advisory_* (a session lock survives layer 2's ROLLBACK; packages/testing/src/db-integration.test.ts), pg_sleep* (the one ban that holds on PGlite), nextval/setval (a consumed id is not rolled back).

The answer carries guards β€” the layers that actually engaged β€” plus truncatedBy and bytes. A layer that could not engage (a managed Postgres that refuses CREATE ROLE) is absent from the list, never assumed. Truncation is never silent.

One authz system, two surfaces

Every action with mcp: { expose: true } becomes a tool for free, and the tool's handle reaches the same invoke the HTTP route reaches β€” the projection's run is that call with surface: 'mcp', nothing more. Policy evaluation lives inside invoke. (An action has no .run member; run is the projection seam, and a query's half of it is sourceFor.)

HTTP  POST /api/posts/publish ─┐
                               β”œβ”€β†’ invoke(action, input, { surface, actor }) ─→ policy ─→ handler
MCP   tools/call publishPost  β”€β”˜

mcp: { visibleTo: [...] } on the action or query travels with the projection too β€” the only declaration surface outcome 1 has for a projected tool. Catalog audience, never authz.

The projection itself declares no scope β€” a projection cannot know what a token means. defineAppMcp's scopes: map (below) may attach one afterward, as a capability of the CONNECTION rather than a second gate: it decides before the policy runs and never reads the input, so the two cannot disagree. There is no MCP-specific authorization code to review beyond it.

Security posture: three outcomes, hidden β‰  forbidden

Refused byDeclared byAnswerWire
rolevisibleToomitted from tools/list, ToolNotFound on call, no data-32601
scopescopeForbidden, naming the missing scope + a runnable fix-32600, X_MCP_SCOPE_DENIED
policythe primitive's own policyisError result carrying code/cause/fixX_FORBIDDEN

Forbidden confirms a tool exists, which turns an authz boundary into a catalog an agent can enumerate by probing. So a role-hidden tool is indistinguishable from an absent one β€” even for a caller holding every scope in the system. A scope refusal is the opposite case: the caller was already shown the tool and can legitimately fix this, so hiding it would only strand a well-behaved client.

RuleDetail
A role list is fail-closeda visibleTo role list admits only the roles it names, so a caller carrying no role matches none of them
A predicate audience sees the caller and nothing elseit is handed McpCaller β€” never the call arguments, so two calls with different inputs cannot answer differently. Must return the literal true; if it throws, the tool is hidden
tools/list is answered per callerfiltered on every call against the caller the transport resolved β€” one per HTTP request, one per stdio connection β€” never a static catalog
Gate ordervisibility β†’ scope β†’ arguments β†’ policy; the scope gate never waits on a policy run against attacker-supplied input
Every outcome is auditedone line per tools/call; hidden/scope/policy at warn, ok, invalid-args and unconfirmed at info β€” see audit.ts; the same decision reaches onAudit after the line. A tool that renders its OWN isError result may name the code it refused with (McpToolResult.code, audit-only, never on the wire) and is then classified by the same outcomeForCode a thrown error is β€” otherwise every self-rendered refusal lands in the policy-denied bucket a prober's name walk is alerted from
Audit lines carry no payloadtool, outcome, actor, code. Never arguments, never rows
No trusted-tool modethere is no flag that skips policy evaluation

Executable contract: security.test.ts. Rationale: docs/architecture/11-ai-surface.md.

Their apps are AI-first too

A generated app exposes its own MCP surface with one call, so the user's agents can drive the user's app:

// apps/admin/app/admin/mcp.ts β€” under app/, so the app scan imports it
import { defineAppMcp, t } from '@ultimat3/mcp';

export const mcp = defineAppMcp({
  name: 'acme-admin',
  include: 'exposed',                    // every action/query with mcp: { expose: true }
  resources: [orgExport],
  prompts: ['apps/web/app/posts/prompts/summarize.v3.md'],   // prompts/get reads the file
  tools: {
    seatReport: {                        // the key IS the tool name
      description: 'Seats used, remaining and the plan limit. Read-only.',
      input: t.object({}),               // any Standard Schema
      policy: 'org:administer',          // an existing permission, never a new rule
      destructive: false,
      async handle({ ctx }) {
        return seats(await ctx.orgs.byId(ctx.actor.orgId));
      },
    },
  },
  scopes: {                              // scope name β†’ tool NAMES; given, it must cover EVERY tool
    'admin:seats': ['seatReport'],
    'posts:write': ['publishPost'],      // …including each one include: 'exposed' projected
  },
  instructions: 'Admin console. seatReport({}) answers seat questions; writes are audited.',
  resolveToken: (token) => sessions.resolveAgentToken(token),
});

The web role mounts it, As of 2026-09-05 β€” the contract is the file's location. app.config.ts has no routes: key and never had one; this page said routes: [mcp.route] while nothing read it, and POST /mcp answered X_ROUTE_NOT_FOUND in every app ever scaffolded. Now: apps/<app>/mcp.ts exports mcp (the value defineAppMcp returns), and both boots β€” x dev and runRole β€” mount POST <defineAppMcp path> (default /mcp) β†’ mcp.route.handle(request) when config.ai.mcp.expose is true, which is the default, and say app mcp mounted in the boot log (x dev prints mcp POST /mcp in its summary). The http pipeline does not pre-judge the route (auth: 'public', enforcedBy: 'handler'): mcp.route.handle reads Authorization: Bearer through resolveToken and decides per tool through the policy every other surface evaluates. expose: true with nothing to mount β€” no file exports mcp, or the export was built without resolveToken and so carries no route β€” logs X_MCP_APP_UNMOUNTED once, with the file to write; expose: false mounts nothing and says nothing.

Several endpoints, one per population (Unreleased). mcp may be a non-empty array of defineAppMcp values: every endpoint mounts at its own defineAppMcp({ path }); endpoint #0 owns the root well-known document, and every other serves only its path-inserted metadata document. Each is its own server β€” catalog, instructions, groups, scopes, prompts. Two on one route throws X_MCP_PATH_DUPLICATE at boot. McpPrompt.visibleTo hides a prompt from prompts/list and prompts/get with the tool/resource McpVisibility semantics.

include: 'exposed' reads the action and query registries instead of asking for actions: [...] / queries: [...] β€” the registries already know who opted in, and a second hand-maintained list is a thing that goes stale silently. The explicit arrays still work and win over the registry's copy of the same name.

The two lists are read differently, on purpose. include sweeps: it holds every primitive the app registered, so one that never opted in is passed over. actions:/queries: are written out: naming a primitive there is the request to expose it, so one that never declared mcp: { expose: true } is X_MCP_TOOL_UNDECLARED at boot β€” a listed tool is never silently missing from the catalog, and exposure stays declared next to the policy. Two primitives reaching one tool name is X_MCP_TOOL_DUPLICATE, also at boot.

Both lists take the primitives themselves, exactly as the app declared them:

import { publishPost } from '../api/posts';

defineAppMcp({ name: 'postly', actions: [publishPost] });
// X_MCP_TOOL_UNDECLARED unless publishPost declared mcp: { expose: true }

One adapter serves both routes, so a written-out primitive runs through the same invoke (or sourceFor) the swept one does β€” the list changes which tools are NAMED, never how one runs. An action that was never handed to defineApi has no export name, and is X_ACTION_UNREGISTERED rather than a tool called '' that nothing could call.

The tool name is the export name, verbatim β€” publishPost, never publish_post. This server answers tools/call for that name and no other, so every surface that PUBLISHES a name has to publish the same one: x-ultimate.mcpTool in openapi.json and ActionDescriptor.mcp.tool. The projection reads primitive.name and nothing else β€” a hand-built ProjectablePrimitive names its tool with its own name; the mcp.name override, which no action() or query() could set, is gone (As of 25.0.0). The action publishers snake_cased the name As of 2026-08, so an agent reading the spec called a tool the catalog never contained and got ToolNotFound.

One projection: toolFrom (As of 25.0.0). It takes a real action or query (or a ProjectablePrimitive) and answers the tool this server serves β€” toolListEntry(toolFrom(publishPost)) is, field for field, its tools/list entry. Reach for it where publishPost.tool() / liveFeed.tool() were: those were a second projection, built a tier below this one, and they disagreed with the served tool (description fallback, the idempotencyKey argument, annotations). toolFromQuery was this function under another name and is gone; ask isMcpExposed(primitive.mcp) from @ultimat3/core where isExposed was. src/cross-surface.test.ts is what makes a fourth spelling a failing test rather than a note.

A hand-written tool's policy is a permission, evaluated through the same guardAction() an HTTP request goes through, so a tool cannot acquire a second authz path. A tool without one is X_MCP_TOOL_UNSAFE at boot, and an unmarked tool is metered as a write.

scopes: (type McpScopes, applied through the exported withScopes) is outcome 2's declaration surface: a scope name β†’ the TOOL NAMES it covers, however each one reached the catalog β€” a projected action, a projected query, or a key in tools. It lives here, not beside the action, because a scope is a capability of the CONNECTION's token β€” what x token grant <scope> names β€” not a fact about the operation; the policy beside the action stays the only rule that reads the input. A name this server does not project is X_MCP_SCOPE_UNKNOWN at boot; one tool claimed by two scopes is X_MCP_SCOPE_CONFLICT.

Given at all, scopes: must cover every tool β€” X_MCP_SCOPE_UNCOVERED at boot names each one no scope lists, As of 2026-10. The registry gates a tool only when it carries a scope, so an unlisted tool answered every token, while bearerMount over the same map serves an unlisted primitive to nobody; one map now exposes one set on both doors. A ready McpTool that arrives with its own scope is covered by it. With include: 'exposed', every primitive that opts in later needs a line here too, or the next boot says which.

A constant surface: surface: 'meta'

A large or sparsely used catalog blows an agent's context one tool per primitive. surface: 'meta' serves three tools instead, however many primitives sit behind them; tools named in no group (docs, whoami) stay flat beside them. The default is 'flat' and changes nothing.

import { defineAppMcp } from '@ultimat3/mcp';

defineAppMcp({
  include: 'exposed',
  surface: (caller) => (caller.role?.startsWith('staff') ? 'meta' : 'flat'), // or 'meta'
  groups: {
    payouts: {
      description: 'Affiliate payout batches',
      tools: ['previewPayoutBatch', 'listPayoutBatches'],
    },
  },
});
ToolAnswers
list_resourcesPLAIN TEXT (As of 22.10; JSON until then): one line per resource, and under it one line per action β€” previewPayoutBatch (action; confirms) {batchId: string, note?: string} β€” <description>. Compact by rule, never by a character cut (As of 2026-10, #590): a scope every action of a resource shares β€” or every action of one kind, query scope payouts:read; action scope payouts:write β€” is said once on the resource line, and stays on each action otherwise; only (action) is tagged, untagged is a read-only query (from destructive, never the name); oneLineParams names every required field, then optional ones up to 4 fields / 100 characters, then …, and cuts an enum after 8 literals with |…. The same catalog as data: server.catalog(caller) (MetaResource[], per-action scope intact, undefined for a flat caller); the renderer is renderCatalog
describe_resource{ resources: string[] } (batched) β†’ each action's full inputSchema
manage_resource{ resource, action, params } β†’ the flat tool's answer, byte for byte

manage_resource is the flat tools/call through another door: visibility (a hidden or misaddressed pair answers what an absent one does), then the shared resolver's scope and argument gates, then the tool's own policy, audited under the tool's name and metered in its bucket. A meta caller cannot call a grouped tool by name, and a flat caller never sees the meta tools. confirms: true on an McpTool marks a write a human finishes. Boot refuses X_MCP_GROUP_UNKNOWN, X_MCP_GROUP_CONFLICT, X_MCP_SURFACE_INVALID (meta without groups, groups nobody is served) and a meta tool name taken by an app tool (X_MCP_TOOL_DUPLICATE).

List params. A list query declares its whitelist, mcp: { expose: true, listParams: { filters: { status: ['_eq', '_in'], createdAt: ['_gt', '_lt'] }, sort: ['createdAt'], fields: ['id', 'status'], maxLimit: 100 } }. Keys are FLAT β€” status_eq, createdAt_gt, sort: '-createdAt', fields, cursor (opaque keyset), limit β€” because a query's input travels as a query string; the query's own input declares them and implements them. describe_resource publishes the whitelist and manage_resource admits the whitelist plus the keys the input requireds and refuses anything else, an optional input key the whitelist left out included (an isError result carrying X_INPUT_INVALID), before the query runs; a whitelisted key the input does not declare is X_MCP_LIST_PARAMS_INVALID at boot.

What a client is told beyond the schema

As of 22.10, MCP 2025-06-18. The projection derives each from the declaration; the primitive's mcp block overrides.

FieldSourceDefault
initialize.instructionsdefineAppMcp({ instructions }) / mcpServer({ instructions }) β€” a string, or (caller) => string | undefinednone; a function that throws or answers blank sends none
titlemcp: { title } Β· hand-written titlenone
annotationsderiveAnnotations(primitive), then mcp: { annotations } key by keyquery { readOnlyHint: true }; action { readOnlyHint: false, destructiveHint: true, idempotentHint: <idempotent> }; openWorldHint only when declared
outputSchemaan action's output with an object root; a query's rows as { rows: [<row>] }, or as the row itself for a single: true read (which answers one row or X_NOT_FOUND, as its route does) β€” toWireOutputSchema (from @ultimat3/schema) / toRowsOutputSchema, structure only (no bounds, patterns or additionalProperties: a client refuses a result that misses its schema)none
structuredContentstructuredResult(value, wrap?): the serialized answer read back, beside the text block β€” a list query's rows under rows, a single: true query's row unwrappedabsent

A tool's text is compact JSON (jsonResult; the 2-space form until 22.10). The meta tools carry their own hints: list_resources and describe_resource read-only, manage_resource a write.

Budget test. measureMcpSurface(server, caller) answers { toolsList, listResources, instructions } in characters, as serialized on the wire; assertMcpSurfaceBudget(server, caller, { toolsList, listResources, instructions }) throws X_MCP_SURFACE_OVER_BUDGET naming every surface over its ceiling. Put the derivation beside the number.

Whose fix. errorAudience: 'caller' (defineAppMcp's default) renders an error's callerFix β€” X_FORBIDDEN: ask the account owner for the permission; X_MCP_SCOPE_DENIED: ask for a token with the scope; X_INPUT_INVALID: correct the named fields β€” where fix names something only the app's developer can run. 'developer' (mcpServer's default, the dev server's) keeps fix. An error whose docs is not the framework's one Error-Codes page β€” an app's docs://recipes/... β€” renders a fourth docs: line. Byte-identical to UltimateError.format({ audience, docs }).

Transports

TransportEntryAuth
HTTPmcpHttpRoute({ server, resolveToken }) β†’ POST /mcp β€” resolveToken(token, facts)Authorization: Bearer <token> β†’ Actor { kind: 'agent' }
stdioserveStdio({ server, caller })none β€” the peer already owns the shell

The HTTP transport exports a route descriptor, not a mounted handler: a host owns the lifecycle, and the descriptor stays drivable from a bare Request in a test. It carries rateLimitClass(body) because all MCP traffic is one URL β€” a per-route bucket would charge initialize to the write bucket and throttle an agent on its handshake.

Reads: 120/min per caller. Writes: 20/min. Unresolvable calls bill the write bucket (fail-closed).

handle enforces those numbers itself, As of 2026-08-24 β€” they were published on the descriptor and applied by no mount point before that, so the ceiling was really Bun's accept rate. It cannot be done from outside: rateLimitClass(body) takes an already-parsed body and handle is the only thing that parses one. The bucket is @ultimat3/http's, keyed per actor per class; over the limit is 429 + Retry-After + X_MCP_RATE_LIMITED.

The body is capped WHILE it is read, and over the cap is 413 on a JSON-RPC -32600 (id: null) carrying data: { code: 'X_MCP_BODY_TOO_LARGE', cause, fix, limit } β€” the stdio transport answers an over-long line with the same code. A JSON-RPC batch (an array) is refused -32600 by name with data: { code: 'X_MCP_PROTOCOL', fix }: one request per POST, never walked. Every refusal on this surface carries its instruction, As of 2026-09-07 β€” the 413 and the batch were the two that did not.

KnobWhereDefault
the body capmcpHttpRoute({ bodyLimitBytes }) Β· defineAppMcp({ bodyLimitBytes })DEFAULT_MCP_BODY_LIMIT_BYTES, 1 MiB
the numbersmcpHttpRoute({ rateLimits }) Β· defineAppMcp({ rateLimits })MCP_RATE_LIMITS
failed authentications per addressrateLimits: { unauthenticated }, keyed on handle(request, { address })MCP_UNAUTHENTICATED_LIMIT, 20 / minute; past it the address is 429 before resolveToken runs. No address, not metered here
where they are countedmcpHttpRoute({ rateLimitStore }) Β· defineAppMcp({ rateLimitStore })a per-process memory store β€” N replicas behind one URL each enforce the full allowance, so a fleet passes postgresRateLimitStore({ executor })
OAuth discoverymcpHttpRoute({ oauth }) Β· defineAppMcp({ oauth })absent: the 401 is Bearer realm="ultimate-mcp"

What resolveToken is told: McpRequestFacts

resolveToken(token, facts) (As of 2026-10) β€” a second argument a one-argument resolver ignores. Frozen, typed, never the raw Headers, so a token can be bound to a network or refused from a browser origin without the resolver parsing a header it cannot trust.

FactSourceAbsent
addresshandle(request, { address }) β€” what the HOST resolved (ctx.ip: a declared proxy's x-forwarded-for per trustedProxyHops, else the socket). Never read from a header herenull
userAgentUser-Agent, verbatim β€” a hint, never an identitynull
originthe Origin request header; 'null' (the string) is an opaque origin, not an absent onenull
paththe URL path, no query stringβ€”
import type { McpRequestFacts, ResolvedToken } from '@ultimat3/mcp';
import { defineAppMcp } from '@ultimat3/mcp';

declare const tokens: {
  resolve(token: string, where: { network: string | null }): Promise<ResolvedToken | null>;
};

export const mcp = defineAppMcp({
  include: 'exposed',
  resolveToken: async (token: string, facts: McpRequestFacts) => {
    if (facts.origin !== null) return null; // agents are not browsers β€” the same 401 as a bad token
    return tokens.resolve(token, { network: facts.address });
  },
});

onAudit: the gate's decisions, as data

mcpServer({ onAudit }) Β· defineAppMcp({ onAudit }) β€” the route reads its server's. One audit PATH with a second destination: the mcp.* log line is written, then the same decision is handed on. What an action or query DID still reaches core's setAuditSink (surface: 'mcp'); onAudit carries what no AuditRecord can β€” the refusals before anything ran.

event.kindWhenCarries
tool-callevery tools/call outcome (ok, hidden, scope-denied, policy-denied, invalid-args, unconfirmed, failed)tool, outcome, caller, scope?, code?
resource-readevery resources/read outcomeuri, outcome, caller, scope?, code?
auth-refuseda missing or rejected token, a non-agent actor, an address past its failure allowance β€” also logged mcp.auth.<reason> at warnreason, status (401/403/429), facts

Every event has at from the server's clock. Never the token, never the arguments. The hook is not awaited and cannot change an answer: one that throws or rejects is logged once as mcp.audit-hook.failed at error (its code when it has one, never its message). tools/list and resources/list stay silent, as in the log.

OAuth discovery (RFC 9728)

defineAppMcp({ oauth: { authorizationServers: ['https://www.example.com'] } }) (As of 22.6.0; MCP authorization spec 2025-06-18, unchanged in 2025-11-25): every 401 carries WWW-Authenticate: Bearer realm="ultimate-mcp", resource_metadata="<origin>/.well-known/oauth-protected-resource<path>" (error="invalid_token" when a token was sent), and route.protectedResource carries the document and the two paths it is served at β€” path-inserted per RFC 9728 Β§3.1 and the root β€” which @ultimat3/cli mounts as public GETs beside POST /mcp. resource defaults to the request's PUBLIC origin (handle(request, { origin }), which the boot fills from ctx.https) + the mount path; scopes_supported to the scopes map's keys. Issuers must be https (http on loopback): X_MCP_OAUTH_INVALID. The authorization server is the app's own routes.

Human confirmation: mcpConfirmations

A factory over action, never a ninth primitive: what it returns IS the decision action ({ id, decision: 'approve' | 'reject' }), registered like any other β€” route, OpenAPI, typed client, audit: true if asked β€” and defineAppMcp({ confirmations }) gates the named tools with it.

import type { PgExecutor } from '@ultimat3/core';
import { defineAppMcp, mcpConfirmations, postgresConfirmationStore } from '@ultimat3/mcp';

declare const executor: PgExecutor;

// apps/web/app/orders/actions/confirm-refunds.ts β€” register it like any action
export const confirmRefunds = mcpConfirmations({
  tools: ['refundOrder'],
  store: postgresConfirmationStore({ executor }), // memoryConfirmationStore() in a test
  permission: 'order:refund',
  ttlMs: 600_000, // DEFAULT_MCP_CONFIRMATION_TTL_MS
});

// apps/web/app/mcp.ts
export const mcp = defineAppMcp({ include: 'exposed', confirmations: confirmRefunds });
StepWhat happens
agent calls refundOrderafter visibility β†’ scope β†’ args β†’ the tool's caller-only admit, a pending row opens β€” actor, org, tool, keyedFingerprint (HMAC-SHA-256 under the signing secret) of the validated arguments, expiry β€” and the call answers X_MCP_CONFIRMATION_PENDING naming the id. Nothing ran. Repeating it answers the same id
a person views itconfirmRefunds({ id, decision: 'view' }) answers the request exactly as the agent sent it β€” { tool, status, expiresAt, arguments }, the arguments opened from their seal. Changes nothing
a person decidesconfirmRefunds({ id, decision: 'approve', arguments }) with the arguments the view returned, or decision: 'reject', over any surface the action projects to. An approval whose arguments' keyed digest is not the row's β€” none, different, or a row keyed before a secret rotation β€” is X_MCP_CONFIRMATION_ARGUMENTS_MISMATCH, so a swap between the view and the approval decides nothing. Decided once (X_MCP_CONFIRMATION_DECIDED), never after expiry (X_MCP_CONFIRMATION_EXPIRED)
who may view and decidepolicy can(permission, check). An agent never may, whatever it holds (X_FORBIDDEN). The default check is the asking org: row !== null && row.orgId === (actor.orgId ?? null) β€” another tenant's row and an unknown id are one X_FORBIDDEN. Crossing tenants is an explicit check; with one that admits a missing row, an unknown id is X_MCP_CONFIRMATION_UNKNOWN
agent repeats the SAME callapproved: it runs once, through the tool's own handle β€” the action's full policy still decides it. Rejected: X_MCP_CONFIRMATION_REJECTED, audited as a denial. Past expiry: X_MCP_CONFIRMATION_EXPIRED. Each outcome is delivered once; the next identical call asks again

Bound to the asking actor, the tool and the input digest: other arguments, or another agent, ride no one's approval. The digest is keyed because the row stores it β€” an unkeyed hash of { accountNumber } is brute-forced from a database read. The open row is found by equality on it, key id included, so after a signing-secret rotation the same call opens a FRESH confirmation: never a mismatch error, never the old approval. The arguments are stored SEALED (seal(), AES-256-GCM under the app's one master key, purpose MCP_CONFIRMATION_ARGUMENTS_PURPOSE), never plaintext at rest β€” the person deciding must see what they approve, or a prompt-injected agent describes "$5 to order 17" and sends another. sealKeys points at the key in a test. SQL_MCP_CONFIRMATIONS_TABLE is the store's DDL, imported from @ultimat3/mcp/schema (its one path β€” 25.0.0 took it off the barrel); store.purge(before) from a task bounds the table. A gated name the server does not project β€” or tools: [] β€” is X_MCP_CONFIRMATION_TOOL_UNKNOWN at boot. The gated tool carries confirms: true, which the meta catalog shows as (confirms).

Resources

URIContents
ultimate://manifestx.manifest.json β€” the generated facts
ultimate://openapi.jsonOpenAPI 3.1 projected from actions and queries
ultimate://routesroute table
ultimate://schemaentities, columns, invariants

Providers are injected thunks: @ultimat3/manifest and @ultimat3/render sit in this same tier, so the CLI wires them and this package owns only the shape and the URIs.

Argument validation

tools/list hands the agent a JSON Schema, so that document is the thing enforced β€” there is no second private validator a tool could be judged against instead. validate-args.ts implements the emitted subset (objects, arrays, enums, required, additionalProperties, bounds, default) and applies declared defaults. Actions still re-parse authoritatively inside their own handler.

A call that fails it is a tool result with isError: true β€” X_INPUT_INVALID, the code HTTP answers for the same input, each issue addressed by path (input for tool "publishPost" failed validation: postId: required) β€” As of 22.10. It was a JSON-RPC -32602 until then, and clients surface a protocol error to the human and hide it from the model, which then retried blind. -32602 remains for a call that is not one: no params, a non-string name.

Idempotency over MCP

An idempotent: true action's tool carries one reserved, optional argument, MCP_IDEMPOTENCY_KEY_ARG ('idempotencyKey', string, 1–255), advertised in tools/list and held to that schema here. tools/call takes it out of the arguments and hands it to invoke as the idempotency key β€” what the Idempotency-Key header is over HTTP, filed under action, caller and key β€” so an agent retrying a timed-out call with the same key gets the first result back:

{ "method": "tools/call", "params": { "name": "sendNotification",
  "arguments": { "caseId": "c1", "idempotencyKey": "7f0c…" } } }

The action's own input never sees the key. A non-idempotent tool does not advertise it and refuses it (X_INPUT_INVALID, as an isError result). An argument, not params._meta: _meta is for host metadata and a model-driven client cannot set it. An idempotent action whose input already names idempotencyKey is X_MCP_IDEMPOTENCY_KEY_SHADOWED at boot.

Errors

CodeMeaning
X_MCP_TOOL_UNKNOWNno visible tool by that name β€” absent and role-hidden are one answer
X_MCP_SCOPE_DENIEDvisible, but the connection's token lacks the scope
X_MCP_SCOPE_UNKNOWNdefineAppMcp's scopes: names a tool this server does not project
X_MCP_SCOPE_CONFLICTtwo scopes in defineAppMcp's scopes: claim one tool
X_MCP_SCOPE_UNCOVEREDdefineAppMcp was given scopes: and a projected tool is under none of them
X_INPUT_INVALIDarguments failed the published inputSchema β€” an isError result since 22.10
X_MCP_ARGS_INVALIDno longer raised on the wire (22.10); McpArgsInvalidError stays exported
X_MCP_SURFACE_OVER_BUDGETassertMcpSurfaceBudget measured a surface over its declared ceiling
X_MCP_IDEMPOTENCY_KEY_SHADOWEDan idempotent action's input declares idempotencyKey, the reserved retry-key argument
X_MCP_PROTOCOLmalformed envelope, unknown method, bad auth header
X_MCP_QUERY_REJECTEDdb.query given anything but one read-only statement
X_MCP_NOT_BRANCH_DBdb.migrate aimed at a production or otherwise non-branch database
X_MCP_RESOURCE_DUPLICATEtwo resources claim one ultimate:// URI β€” refused at registration, as a duplicate tool name is
X_MCP_GROUP_UNKNOWN Β· X_MCP_GROUP_CONFLICTgroups: names a tool not projected Β· lists one tool twice
X_MCP_SURFACE_INVALIDsurface and groups disagree
X_MCP_LIST_PARAMS_INVALIDlistParams whitelists a key the tool's input does not declare
X_MCP_CONFIRMATION_PENDINGa gated call waits for a person: the cause names the confirmation id
X_MCP_CONFIRMATION_EXPIREDthe confirmation lapsed before the call came back, or before a person decided it
X_MCP_CONFIRMATION_REJECTEDa person rejected this exact call
X_MCP_CONFIRMATION_DECIDEDapprove/reject on a confirmation already decided
X_MCP_CONFIRMATION_UNKNOWNno confirmation has that id
X_MCP_CONFIRMATION_TOOL_UNKNOWNmcpConfirmations({ tools }) names a tool this server does not project, or none
X_MCP_CONFIRMATION_CONTESTEDidentical concurrent calls kept racing one confirmation; retry
X_MCP_CONFIRMATION_ARGUMENTS_MISMATCHan approval did not carry the arguments the agent sent (missing, different, or keyed before a rotation)
X_MCP_RATE_LIMITEDthe caller spent its per-minute allowance for this request's class. Its own code rather than @ultimat3/http's X_RATE_LIMITED because the KNOB differs β€” rateLimits on the route, never rateLimit.buckets in app.config.ts

Error classes

Every error class src/index.ts exports, for instanceof inside one process. Across a wire or a job boundary the class is gone and the code is what survives β€” match on that.

ClassCodeDeclared in
McpAppUnmountedErrorX_MCP_APP_UNMOUNTEDsrc/errors.ts
McpArgsInvalidErrorX_MCP_ARGS_INVALIDsrc/errors.ts
McpBodyTooLargeErrorX_MCP_BODY_TOO_LARGEsrc/errors.ts
McpIdempotencyKeyShadowedErrorX_MCP_IDEMPOTENCY_KEY_SHADOWEDsrc/errors.ts
McpConfirmationArgumentsMismatchErrorX_MCP_CONFIRMATION_ARGUMENTS_MISMATCHsrc/confirmation-errors.ts
McpConfirmationContestedErrorX_MCP_CONFIRMATION_CONTESTEDsrc/confirmation-errors.ts
McpConfirmationDecidedErrorX_MCP_CONFIRMATION_DECIDEDsrc/confirmation-errors.ts
McpConfirmationExpiredErrorX_MCP_CONFIRMATION_EXPIREDsrc/confirmation-errors.ts
McpConfirmationPendingErrorX_MCP_CONFIRMATION_PENDINGsrc/confirmation-errors.ts
McpConfirmationRejectedErrorX_MCP_CONFIRMATION_REJECTEDsrc/confirmation-errors.ts
McpConfirmationToolUnknownErrorX_MCP_CONFIRMATION_TOOL_UNKNOWNsrc/confirmation-errors.ts
McpConfirmationUnknownErrorX_MCP_CONFIRMATION_UNKNOWNsrc/confirmation-errors.ts
McpGroupConflictErrorX_MCP_GROUP_CONFLICTsrc/meta-errors.ts
McpGroupUnknownErrorX_MCP_GROUP_UNKNOWNsrc/meta-errors.ts
McpListParamsInvalidErrorX_MCP_LIST_PARAMS_INVALIDsrc/meta-errors.ts
McpNotBranchDbErrorX_MCP_NOT_BRANCH_DBsrc/errors.ts
McpProtocolErrorX_MCP_PROTOCOLsrc/errors.ts
McpSurfaceInvalidErrorX_MCP_SURFACE_INVALIDsrc/meta-errors.ts
McpQueryRejectedErrorX_MCP_QUERY_REJECTEDsrc/errors.ts
McpRateLimitedErrorX_MCP_RATE_LIMITEDsrc/errors.ts
McpResourceDuplicateErrorX_MCP_RESOURCE_DUPLICATEsrc/errors.ts
McpScopeConflictErrorX_MCP_SCOPE_CONFLICTsrc/errors.ts
McpScopeDeniedErrorX_MCP_SCOPE_DENIEDsrc/errors.ts
McpScopeUncoveredErrorX_MCP_SCOPE_UNCOVEREDsrc/errors.ts
McpScopeUnknownErrorX_MCP_SCOPE_UNKNOWNsrc/errors.ts
McpSurfaceOverBudgetErrorX_MCP_SURFACE_OVER_BUDGETsrc/errors.ts
McpToolDuplicateErrorX_MCP_TOOL_DUPLICATEsrc/errors.ts
McpToolUndeclaredErrorX_MCP_TOOL_UNDECLAREDsrc/errors.ts
McpToolUnknownErrorX_MCP_TOOL_UNKNOWNsrc/errors.ts
McpToolUnsafeErrorX_MCP_TOOL_UNSAFEsrc/errors.ts

FAQs

Package last updated on 09 Oct 2026

Related posts