Public beta — not for production use. Data may be wiped at any time. Questions? Contact us.
Documentation menu

Use Axiom from any MCP client (hosted MCP server)

Add Axiom's hosted MCP server in any MCP client — sign in with OAuth or paste an API key — and search the catalog, inspect schemas, read a node's source, invoke nodes and flows, and pin favorites as typed tools, without installing the CLI.

View as Markdown

Axiom ships a hosted MCP server (Model Context Protocol) so an external agent or user can use Axiom without installing anything. Add the server in any MCP client, sign in with OAuth or an Axiom API key, and your agent can search the catalog, inspect a node or flow's schema, read a node's source code to verify what it does, invoke it synchronously, and pin favorites that show up as native typed tools — all over the gateway, scoped to your tenant.

New here? Start with Quickstart: connect your agent (MCP) — the five-minute path from a URL to a real tool call. This page is the full reference behind it.

This is a consume-only surface. It does not author: you cannot create, compose, push, or publish from the MCP server. The axiom CLI stays the fully-featured authoring tool (see Author with Claude Code); the MCP server is how an agent uses what has been published.

What it is

  • Hosted and zero-install. The server runs in the Axiom platform behind the gateway; your MCP client connects to a URL, not a local process. No CLI, no Docker, no SDK in your agent's code.
  • Consume-only. Search, inspect, read source, invoke, and pin. No authoring lifecycle.
  • Authenticated and tenant-scoped. Your credential — OAuth-issued or an API key you created — authenticates through the same gateway every other Axiom client uses (see API keys). Every action is scoped to your tenant: you see public catalog items, your own private items, and items you've been granted an entitlement to — never another tenant's private data.
  • Typed. Pinned nodes and flows surface as first-class MCP tools with a derived input schema, so your agent calls them with structured arguments instead of guessing JSON shapes.

Connect with OAuth (no API key)

If your MCP client supports remote servers with OAuth — Claude's connector directory, and a growing number of others — you do not need an API key at all. Give the client the server URL:

https://api.axiomide.com/mcp

The client discovers everything else and walks you through sign-in:

  1. It reads Axiom's OAuth metadata (advertised on the /mcp endpoint itself, per RFC 9728) to find the authorization server.
  2. It registers itself automatically (RFC 7591 dynamic client registration) — there is nothing to create in the Axiom console first.
  3. Your browser opens an Axiom consent screen naming the application and what it will be able to do. Sign in with GitHub, Google, or email and password, then approve.
  4. The client receives a token and connects. The whole exchange uses authorization code + PKCE (S256); no secret is ever pasted anywhere.

What you are granting: the same access the axiom CLI has — the connected client acts as you, within your tenant. It can search the catalog, read schemas and source, invoke nodes and flows (including with your secrets), and manage your pins. It cannot see another tenant's data.

The token appears in Console → API Keys like any other session, so revoking it is the same as revoking a CLI session — delete it there and the connection stops working immediately. Tokens renew automatically while you keep using the connection; if a connection sits unused for two weeks it expires and the client will ask you to approve again.

Axiom does not review or verify applications that register. The consent screen tells you which application is asking — only approve one you started yourself from a client you trust. How your data is handled is covered in the privacy policy.

Add the Axiom MCP server with an API key

For a client that does not do OAuth (or when you want a key you manage yourself), authenticate with an Axiom API key — create one in Console → API Keys (see Create and manage API keys).

Then add the server in a single step. The only edit you make is replacing YOUR_AXIOM_API_KEY with your key — everything else is ready to paste as-is.

Claude Code — run one command:

claude mcp add --transport http axiom https://api.axiomide.com/mcp \
  --header "Authorization: Bearer YOUR_AXIOM_API_KEY"

Claude Desktop, Cursor, or any client with an mcpServers config file — paste this block:

{
  "mcpServers": {
    "axiom": {
      "type": "http",
      "url": "https://api.axiomide.com/mcp",
      "headers": {
        "Authorization": "Bearer YOUR_AXIOM_API_KEY"
      }
    }
  }
}

That's it — reload your client and the Axiom tools below appear. Under the hood the server speaks the MCP Streamable HTTP transport at the /mcp path on the gateway; the gateway validates your credential (the same JWT/API-key authorizer the HTTP API uses) and stamps the authoritative tenant identity, so you only ever send the Authorization header — the server never trusts a tenant the client supplies. An OAuth connection ends up holding exactly the same kind of credential, which is why both routes behave identically from here on.

The tools

Once connected, tools/list advertises the consume surface:

  • axiom_search — search the catalog for nodes and flows, lexical and semantic, tenant-scoped (public + your own + what you're entitled to). Results carry the invocable id (node_id for nodes, graph_id for flows) so they pipe straight into inspect or invoke.
  • axiom_inspect — get the compiled input/output JSON Schema for a node or a flow (the schema a typed invoke payload must satisfy). A flow may be given by its graph id or by its name, a node by its id or by its ref — see Naming a flow and Naming a node below.
  • axiom_get_source — get a node's handler source code plus a pointer to its full repository (source_url, pinned to the exact published commit). axiom_inspect tells you a node's input/output shape; axiom_get_source tells you what it actually does, so you can verify a node before invoking it. The snippet is the handler body; source_url is the escape hatch for reading code the handler calls into. For an Instance node the source is the inherited generic implementation, and generic_package labels its origin. Takes the same node ref as axiom_inspect.
  • axiom_invoke — synchronously invoke a published node or a compiled flow, resolved by id, name/ref, or a pinned name. This wraps the existing invoke endpoints — it does not create a new invoke path.
  • axiom_pin / axiom_unpin — pin a node or a flow as a favorite (one pin targets one node or one flow). Pins live in a shared store: a pin you make from the MCP server is also visible to axiom list-my-tools in the CLI, and vice versa.
  • axiom_list_my_tools — list your pinned favorites. tool_kind filters to node or flow; omit it for both. An unrecognized value is an error.
  • axiom_propose — propose a package, node, or flow the catalog doesn't have yet. Reach for it the moment a task needs an artifact axiom_search can't find: your proposal is high-signal demand because you hit the gap doing real work. It is propose-only — an agent can request a missing artifact, but filing product bug reports and feature requests stays with the human (the CLI's axiom feedback and the editor's feedback dialog), because those need human repro judgment.

How to name a target, and what an invoke can answer besides a result, are covered in the sections below.

Naming a flow

axiom_invoke and axiom_inspect both accept a flow's name wherever they accept a graph id:

{"kind": "flow", "target": "christiangeorgelucas/cron-explainer-card"}

The name is the flow's fully-qualified identity — <handle>/<flow-name> — and it always resolves to that flow's current published graph. Add @<version> (…/cron-explainer-card@1.2.0) to pin an exact published version.

Prefer the name. A graph id identifies one published version, so an id you copied out of an earlier answer can go stale the moment the author republishes, and invoking a stale id fails at run time. A name cannot go stale.

Naming a node

axiom_invoke, axiom_inspect and axiom_get_source all accept a node's ref — the same <handle>/<package>/<Node> form axiom invoke, axiom inspect node and axiom source node take on the CLI:

{"kind": "node", "target": "christiangeorgelucas/geo-tools/Haversine"}

Omit the version and you get the newest published version of that package. Add @<version> (…/geo-tools/Haversine@0.3.1) to pin an exact one. Node ids (ULIDs) and the fully-versioned {owner}/{pkg}/{version}/{node} form still work unchanged.

Prefer the ref without a version, for the same reason you prefer a flow's name: a version you pinned keeps running forever, silently, after the author ships a fix.

You rarely need to pass kind: a target that names itself is self-identifying — a flow name has two slash-separated segments, a node ref has three — so pasting either straight out of axiom_search works.

Streaming targets are refused, not truncated

A flow compiled with pipeline_mode: true, or a pipeline node, emits a stream of frames rather than one result. This server is synchronous request/response and has nowhere to put frames 2..n, so invoking one returns a not-callable result with the reason pipeline_flow or pipeline_node — and names the surface that can receive them (axiom flow run --stream, axiom invoke --input-stream).

It refuses rather than returning the first frame, because a partial answer that looks complete is worse than no answer: the unary endpoint would return one aggregated result and discard the rest with nothing to say so.

Telling a paused run from a slow one

When an invoke comes back without a result, the reason says which of two different things happened:

  • hitl_target — the flow paused for human approval and cannot finish without it. Relay the ask, with the execution_id in the payload; a human resumes it from the console.
  • exceeds_sync_timeout — the flow is still running. This is not a failure and needs no human. The payload carries the execution_id; fetch the result with axiom executions get <execution_id>, or simply invoke again — a cold flow is slow on its first run while each node's sidecar scales from zero, and the next run of the same flow is typically much faster.

Every not-callable payload carries the execution_id (and graph_id / node_ref), so an agent can always name the run it is talking about.

Knowing a run was mocked

axiom_invoke returns the ingress response as-is, which includes mocked_nodes (ADR-176) when the flow's artifact serves one or more nodes from authoring fixtures. A response carrying it is not proof of live behaviour — treat a non-empty mocked_nodes exactly as the CLI's ▲ MOCKED RUN banner: the run exercised the graph, not the world.

Which tools change things

Every tool is annotated with the MCP spec's behavioural hints, so a client knows before it calls whether a tool merely reads or changes something:

  • Read-onlyaxiom_search, axiom_inspect, axiom_get_source, axiom_list_my_tools. Safe to call freely; they change nothing.
  • Writes, not destructiveaxiom_invoke, axiom_pin, axiom_propose. axiom_invoke is additionally marked open-world: it runs published code that may reach external systems, so a well-behaved client treats it as consequential. Pinned typed tools carry the same profile, because each one is an axiom_invoke with the target already bound.
  • Destructiveaxiom_unpin alone. It removes a stored pin (and with it the typed tool that pin was producing).

Pinned tools become typed tools

When you pin a node or a flow, it does not just appear in a list — it shows up on tools/list as a first-class MCP tool with a derived inputSchema compiled from the target's proto definition. Your agent can then call it directly with structured arguments, exactly like a native function. Unpin it and the tool disappears. (A pinned node's schema comes from its compiled input JSON Schema; a pinned flow's comes from its on-demand OpenAPI spec.)

Current limitations

The hosted MCP server is synchronous only today. Know these before you rely on it:

  • Sync-only, ~30s. axiom_invoke waits for a synchronous result and has a roughly 30-second budget. There is no async get_result, no HITL resume, no recommend, and no cancel yet — those are planned but not available today.
  • A target that can't finish synchronously tells you so — it never hangs or silently pauses. If you invoke something that pauses for human approval, that streams, or that outlasts the sync budget, axiom_invoke returns a clear, explicit not-callable result with a machine-readable reason (hitl_target, exceeds_sync_timeout, pipeline_flow, pipeline_node or grant_required) and the execution_id. It does not return a bare accepted ack and it does not hang your agent — that is a hard guarantee, not best-effort. The reasons are distinguished from one another, too: see Telling a paused run from a slow one above.
  • A missing secret grant is a relayable ask. An invoke refused for secret authorization returns reason grant_required carrying the ingress's machine-readable missing[] verbatim — every unsatisfied (node, slot) with its typed reason, the human action that fixes it, and a console deep link. Granting is human-only by design; relaying the exact ask is what you can do.
  • No authoring. You cannot create, compose, push, or publish from the MCP server. To author, use the axiom CLI (see Author with Claude Code).
  • Tenant-scoped. You see only public catalog items, your own, and what you are entitled to — the same boundary as the HTTP API (see Sandboxing and tenancy).

Next steps