---
title: "Use Axiom from any MCP client (hosted MCP server)"
description: "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."
category: guide
surfaces: [cli, http-api]
related: [getting-started/invoke-via-api, guides/api-keys, guides/author-with-claude-code, guides/send-feedback-and-propose, reference/http-api, concepts/sandboxing-and-tenancy]
last_reviewed: 2026-08-05
---

# Use Axiom from any MCP client (hosted MCP server)

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)](../getting-started/mcp-quickstart.md) —
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](./author-with-claude-code.md));
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](./api-keys.md)).
  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:

```text
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](https://axiomide.com/privacy).

## 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](./api-keys.md)).

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:

```bash
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:

```json
{
  "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:

```json
{"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:

```json
{"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-only** — `axiom_search`, `axiom_inspect`, `axiom_get_source`,
  `axiom_list_my_tools`. Safe to call freely; they change nothing.
- **Writes, not destructive** — `axiom_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.
- **Destructive** — `axiom_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](./author-with-claude-code.md)).
- **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](../concepts/sandboxing-and-tenancy.md)).

## Next steps

- Call a flow the classic way — [Invoke a flow via API](../getting-started/invoke-via-api.md).
- Author and publish nodes and flows with the CLI —
  [Author with Claude Code](./author-with-claude-code.md).
- The HTTP API the MCP server wraps — [HTTP API reference](../reference/http-api.md).
