MCP Connect MCP servers as Ax tools for generation, agents, prompts, resources, and runtime workflows. typescript concepts concepts/mcp website/content-src/templates/concept-mcp.md concepts MCP

MCP

MCP is a native Ax execution surface. Attach a live AxMCPClient with mcp; AxGen, AxAgent, and AxFlow retain the owning protocol state, qualified tool identity, structured content, tasks, cancellation, and tracing. The compatibility-only function adapter is lossy and is not used by native execution.

MCP bridge
flowchart LR
  Server["MCP server"] --> Client["AxMCPClient"]
  Client --> Program["AxGen, AxAgent, or AxFlow"]
  Client --> Events["AxMCPEventSource"]
  Events --> Inbox["AxEventRuntime inbox"]
  Inbox --> Program

Stateful And Stateless Eras

The TypeScript client supports both stateful MCP 2025-11-25 and stateless MCP 2026-07-28. Automatic era classification is the default. Legacy servers initialize a session; modern servers answer server/discover and receive the protocol version, method, operation name, client metadata, and tracing context on every request.

The generated Python, Java, C++, Go, and Rust packages use the same automatic dual-era classification and modern discovery boundary. Their language-specific snippet below shows the native spelling.

VerifiedNeeds credentialsSource
TypeScript
const mcp = new AxMCPClient(
  new AxMCPStreamableHTTPTransport(endpoint, {
    ssrfProtection: { allowHTTP: true, allowLoopback: true },
  }),
  {
    era: 'auto',
    readCache: true,
    elicitation: async () => ({
      action: 'accept',
      content: { confirmed: true },
    }),
  }
);
const discovery = await mcp.discover();
if (mcp.getEra() !== 'modern') throw new Error('Expected modern MCP');
const mrtr = await mcp.callTool('mrtr_one_round');

discover() performs classification but returns only for a modern endpoint. Use inspectCatalog() for code that must work in either era.

Native Tools

The client negotiates capabilities and Ax maps native tool definitions at each model step. Modern catalog results carry TTL and cache scope. A tool may map schema properties to request headers with x-mcp-header; Ax validates those annotations and keeps the body and headers synchronized.

TypeScript
const mcp = new AxMCPClient(
  new AxMCPStreamableHTTPTransport(endpoint),
  { namespace: 'inventory' }
);
const program = ax(
  'request:string -> answer:string "Use the inventory MCP tool."',
  { mcp }
);
try {
  const catalog = await mcp.inspectCatalog();
  console.log({
    tools: catalog.tools.map(({ name }) => name),
    resources: catalog.resources.map(({ name, uri }) => ({ name, uri })),
    resourceTemplates: catalog.resourceTemplates,
  });
  await program.forward(llm, { request: 'Reindex inventory.' });
} finally {
  await mcp.close();
}

Multi Round-Trip Input

Modern operations may pause with input_required. TypeScript can fulfill roots, sampling, and elicitation through host handlers. The generated Python, Java, C++, Go, and Rust clients fulfill roots automatically and elicitation through a host callback; they deliberately do not advertise sampling and reject a truthy sampling option during initialization.

VerifiedNeeds credentialsSource
TypeScript
const client = new AxMCPClient(transport, {
  era: 'modern',
  roots: [{ uri: 'file:///workspace', name: 'workspace' }],
  elicitation: async (params, context) => ({
    action: 'accept',
    content: { confirmed: true },
  }),
});
const result = await client.callTool('mrtr_one_round');

Subscriptions Can Wake Programs

AxMCPEventSource converts protocol notifications into normal event ingress. A notification is durable before acknowledgement when the configured store supports it. Nothing wakes a model until an explicit authenticated route selects wake.

The endpoint is only the address. inspectCatalog() discovers server-owned resource names and URIs, while an explicit none/all/URI/selector policy decides what the source maintains. Legacy clients use subscription requests and resumable GET/SSE; modern clients put their interests in a long-running subscriptions/listen POST. See MCP Subscriptions for catalog selection, URI templates, ownership, reconnect, and troubleshooting.

TypeScript
const source = new AxMCPEventSource({
  client: mcp,
  resourceSubscriptions: 'all',
  identity: { tenantId: 'demo' },
  trust: 'authenticated',
});
const target = eventTarget('inventory-agent')
  .program(program)
  .ai(llm)
  .input((input) => input.field('uri', eventPath.data('uri')))
  .forwardOptions({ mcp })
  .build();
const runtime = eventRuntime({
  allowVolatile: true,
  sources: [source],
  routes: [
    eventRoute('resource-wake')
      .types('mcp.resource.updated')
      .authenticated()
      .instanceKey(eventPath.subject())
      .wake(target)
      .build(),
  ],
});
await runtime.start();

MCP sessions do not establish application tenant identity. Supply identity from the OAuth-token or account mapping. Unmapped notifications remain anonymous and cannot match routes requiring authentication.

Tasks Resume Continuations

Modern Tasks v2 work is server-directed. callTool() auto-awaits an unsolicited task by default; applications can expose it, submit requested input with provideTaskInput(), or cancel it. Task progress and logs default to observe. An input_required or terminal task event correlates as namespace:taskId and can atomically consume the continuation owned by a prior AxFlow or Agent run. Polling remains available because MCP task notifications are optional.

TypeScript
const mcpSource = new AxMCPEventSource({
  client,
  identity: { tenantId: 'demo' },
  trust: 'authenticated',
});
const runtime = eventRuntime({
  allowVolatile: true,
  sources: [push, mcpSource],
  routes: [
    eventRoute('start-reindex-flow')
      .types('job.reindex.requested')
      .wake(target)
      .build(),
    ...axMCPEventRoutes({ client }),
  ],
});
await runtime.start();
await push.publish({
  event: {
    specversion: '1.0',
    id: 'reindex-request-1',
    source: 'app://inventory',
    type: 'job.reindex.requested',
  },
  identity: { tenantId: 'demo' },
  trust: 'authenticated',
});

Transports, Authentication, And Server Requests

Ax supports stdio, Streamable HTTP, legacy HTTP/SSE, and custom WebSocket transports. Native clients also expose prompts, resources, templates, subscriptions, completions, roots, elicitation, multi round-trip requests (MRTR), progress, cancellation, Tasks v2, OAuth, MCP Apps, client credentials, and enterprise-managed authorization. Sampling and legacy inbound elicitation remain TypeScript-only.

Transport listeners are supervised and nonblocking. Legacy reconnect resumes with Last-Event-ID when available and restores logical subscriptions. Modern reconnect issues a fresh subscriptions/listen request without session or resume headers. Caller-owned clients remain caller-owned and must be closed.

Safety

  • Treat prompt and resource content as attributed, untrusted context.
  • Require application identity for tenant routes; never derive it from an MCP session id.
  • Authorize side-effecting tools from annotations, arguments, task context, and caller identity.
  • Treat schema-derived request headers as routing data, not authorization.
  • Do not blindly replay an uncertain post-side-effect failure.
  • Use recording/replay or a sandbox for optimization and evaluation.

See MCP Subscriptions, Event Runtime, Tools, ax() generation, and agent() agents.

Docs