npm.io
0.1.0 • Published yesterdayCLI

axis-agent-mcp

Licence
Apache-2.0
Version
0.1.0
Deps
4
Size
243 kB
Vulns
0
Weekly
0

axis-mcp

A Model Context Protocol server that wraps the AXIS agent-identity protocol. It packages both resolution-side motions of the protocol into one install:

  • Carry & Present (operator side) — the agent holds its credential locally, mints AXIS Identity Tokens (AITs), and surfaces a signup link to its operator when it isn't yet registered.
  • Look Up (platform side) — resolve any inbound agent to its operator and authority, and verify a presented token. Free, account-less, read-only.

Beyond resolution, it is a self-hosted operator tool: the human operator manages the agent's identity and permissions entirely on their own machine — local operator-key custody, a local permission policy, and local enforcement — so you run an agent's identity and authorization with no hosted Governor and no Kipple infra beyond the public registry. See the Operator CLI and Operator-key local custody below.

It wraps the existing axis-protocol-sdk (signing, local verification) and the live AXIS Prime registry's public read endpoints (https://registry.axisprime.ai). No new backend. (Hosted, recoverable, multi-device custody + managed policy remain a separate explicit upgrade — the Governor — not required here.)

MCP is a surface, not the identity layer. AXIS agent identity lives in the protocol and axis-protocol-sdk; this MCP server is the surface that lets an MCP-speaking agent (Claude, etc.) use it as tools. An agent on any other stack — a plain HTTP client, a Python agent, the next protocol — uses axis-protocol-sdk directly and gets the same identity. AXIS isn't tied to MCP; MCP is simply the most ergonomic surface for LLM agents today.

Tools

Motion B — Look Up (zero local state, read-only)

Tool What it does
axis_verify(token, expected_audience?) Verify an AIT via the registry; optionally check its aud claim.
axis_resolve_agent(agent_id, presenting_ait?) Resolve an agent's public identity record.
axis_resolve_operator(operator_id) Resolve an operator's public record.
Motion A — Carry & Present (local custody)
Tool What it does
axis_status() Report the local credential + the mandatory custody disclosure (or a signup affordance).
axis_requirements(platform_url) Read a platform's /.well-known/axis-access (+ axis-scopes): audience, tier, required scopes, registration URL.
axis_present(audience_or_platform_url, scope?, ttl?=300, dlg?) Mint an AIT and return it + the Authorization: Bearer <ait> header. Refuses any scope not granted in local policy. When a scope is asserted and no dlg is passed, auto-attaches the newest registered delegation covering that scope (dlg_source: "auto") — so a gate can resolve it without the caller knowing to pass dlg.
axis_delegate(scope, ttl?) Sign a delegation (operator → agent) with the local operator key and register it via the registrar path so a platform gate can resolve it. Refuses any scope not granted in local policy.
post_comment(url, text, scope?) One-call engage: post a comment on an AXIS-gated platform. Discovers requirements, ensures a registered delegation for the comment scope, mints the token, and POSTs it — hiding the endpoint, header, and payload. Returns the exact axis grant … to run (never self-grants) if the operator hasn't granted the scope.
axis_request_onboarding(platform_url?) Generate the agent keypair locally and return a one-click device-code operator signup link. Platform URL optional — omit to onboard platformless at the default (email) tier and engage specific platforms later. The link also carries the operator public signing key + a registration proof so the registrar registers it (POST /operators/:id/key), making operator-signed grants registry-verifiable.
axis_complete_onboarding(device_code?) Poll the registrar device-token endpoint once for a pending onboarding: pending (re-invoke), complete (binds the returned agent_id to the local key), or denied/expired.
axis_dashboard(activity_limit?=20) The operator dashboard in one call: agents, per-agent permissions (scope/expiry/approval), and recent signed activity, with a render hint. The in-chat equivalent of the axis dashboard localhost page — read-only, never returns key material.
Prompts
Prompt What it does
my_agents Discoverable entry in the client's prompt menu; drives the model to call axis_dashboard and render the operator dashboard inline — so you find it in the UI instead of learning a CLI command.

Operator CLI (axis)

The MCP server serves your agent's LLM. The axis CLI is for you, the human operator: it manages your agent's identity and permissions entirely locally — no hosted Governor, no Kipple infra beyond the public registry. Both share the ~/.axis-mcp store.

npm run build              # builds dist/cli.js (bin: `axis`)
axis status                # held credential(s): ids, tier, granted scopes, custody, op-key
axis agents                # list local agents
axis operator-key init     # generate the operator key + register the public key
axis grant content:comment --ttl 30   # grant a scope (policy + operator-signed delegation)
axis approve content:publish           # approve a requires-approval scope
axis require-approval content:publish  # flag a scope deny-by-default
axis revoke content:comment            # revoke one scope
axis revoke --all                      # revoke the whole agent
axis audit --tail 20                   # show the signed local audit log
axis dashboard                         # open the localhost click-instead-of-type dashboard
axis help

Run without installing the bin via npm run axis -- <cmd> (e.g. npm run axis -- status).

Local operator dashboard (axis dashboard)

For operators who'd rather click than type, axis dashboard starts a tiny localhost-only web page that does what the CLI does — list agents, grant / revoke / approve scopes, flag requires-approval, and tail the audit log — backed by small JSON endpoints on the same Node server. It reuses the exact same policy / store / scope / delegation logic as the CLI (policy.ts, tools/delegate.ts), so a grant from the page mints the same operator-signed delegation that axis grant does.

axis dashboard               # http://127.0.0.1:7878/?token=…  (auto-opens the browser)
axis dashboard --port 9000   # pick a port
axis dashboard --no-open     # don't auto-open the browser

Security: the server binds 127.0.0.1 only (never 0.0.0.0), so it is not reachable from other machines. A random per-session token is minted at start and embedded in the printed URL; every control endpoint requires it (header x-axis-token or ?token=), and mutating endpoints additionally reject cross-site requests (same-origin / Sec-Fetch-Site guard) — so a random web page open in the same browser can't drive your local permission state. The dashboard is for the human operator; it's distinct from the stdio MCP server (which serves the agent's LLM), and both share ~/.axis-mcp.

Permission model. axis grant <scope> writes the scope into ~/.axis-mcp/policy.json and mints/refreshes an operator-signed delegation covering the agent's full granted set. axis_present and axis_delegate then refuse to mint or sign for any scope that isn't granted (clear error pointing at axis grant). Scopes flagged axis require-approval are deny-by-default until axis approve. The scope grammar is AXIS v0.3 (resource:action, with resource:* and * wildcards); malformed scopes are rejected with a did-you-mean hint.

Operator-key local custody

axis operator-key init generates an operator Ed25519 keypair (distinct from the agent identity key, in a separate keychain account). Registering the public key with the registry via POST /operators/:id/key is registrar-authenticated and proof-bearing, so it follows whichever registry the MCP points at — cloud or your own (see Self-hosting). Either way grants are operator-signed locally regardless of registration; registration only enables third parties to verify the delegation proof against a registry-held key.

One-click onboarding registers the operator key too. You don't have to run axis operator-key init separately: axis_request_onboarding ensures the operator key exists and carries its public key + a registration proof (operator_pubkey, operator_proof) into the activation link. When the operator signs in, the registrar registers it via POST /operators/:id/key — so the operator key is registry-verifiable from the same one-click flow. The proof is an Ed25519 signature over the RFC 8785 JCS canonicalization of { publicKey } (proofType jcs-eddsa-2026), the same regime the registry uses for POST /register. This path is live — the registrar reads those params from the link and registers the operator key during onboarding. The registrar's outcome (registered / conflict / pending) is surfaced as operator_key_status by axis_complete_onboarding and in axis_status.

Self-hosting against your own registry

Self-host mode behaves exactly like cloud: you run your own axis-registry instance (your own ledger) and register operator keys through a registrar credential in that instance — the same code path as registry.axisprime.ai.

  1. Deploy axis-registry and seed a registrar row (the shipped seed-key.sql gives you a one-time registrar API key).
  2. Point the MCP at it, supply that key, then provision the operator key:
    export AXIS_REGISTRY_URL=https://registry.example.com   # or http://localhost:8787
    export AXIS_REGISTRAR_API_KEY=<your registrar key>
    axis operator-key init

The MCP registers your operator public key directly (registrar Bearer + JCS-EdDSA proof of ownership). Without AXIS_REGISTRAR_API_KEY the command still provisions and holds the key locally but skips registry registration — a no-registrar write to the central registry is correctly refused, so use the cloud one-click onboarding for that path.

Key custody (LOCAL mode)

The agent's Ed25519 identity key is generated on this machine and stored in the OS keychain (via keytar). If the keychain is unavailable, it falls back to an AES-256-GCM-encrypted file under ~/.axis-mcp/. The private key is never returned by any tool and never leaves the machine.

Disclosure (always surfaced by axis_status and onboarding): Your agent's identity key is stored on this computer. Anyone who can use this computer can act as your agent.

Every present/sign action is recorded to a local append-only signed audit log at ~/.axis-mcp/audit.jsonl (default-on).

Run

npm install
npm run build      # tsc → dist/
npm start          # node dist/server.js  (stdio transport)
# or, no build step:
npm run dev        # tsx src/server.ts
Register with an MCP client

claude_desktop_config.json (or any MCP client config):

{
  "mcpServers": {
    "axis": {
      "command": "node",
      "args": ["C:/Users/josha/dev/axis-mcp/dist/server.js"]
    }
  }
}
Environment overrides
Var Default Purpose
AXIS_REGISTRY_URL https://registry.axisprime.ai Registry base URL.
AXIS_REGISTRAR_URL https://signup.axisprime.ai Registrar base for onboarding links (live).
AXIS_REGISTRAR_API_KEY (unset) Self-host only. A registrar key seeded in your own registry so the MCP can register the operator key and delegations directly against AXIS_REGISTRY_URL (Bearer + proof). Unset on the cloud path. (AXIS_REGISTRAR_KEY is accepted as an alias.)
AXIS_DELEGATION_REGISTRAR_URL (unset) Product path for delegation registration. A cloud registrar mint endpoint that holds the registrar key server-side; the MCP POSTs the operator-signed delegation to it, so no registrar credential lives on the operator's machine. Preferred over AXIS_REGISTRAR_API_KEY when set.
AXIS_COMMENTS_GATE_URL https://axis-comments.editor-9a4.workers.dev Comment gate base for post_comment on the axis-comments-ghost audience (until AXIS discovery advertises an actions manifest).
AXIS_MCP_HOME ~/.axis-mcp Local data home (audit log, key fallback, metadata, policy).
AXIS_MCP_DISABLE_KEYCHAIN (unset) Set to 1 to force the encrypted-file key fallback (never the OS keychain). Used by the test suite; available where the keychain is undesirable.
AXIS_HTTP_TIMEOUT_MS 30000 Registry / well-known fetch timeout.

Tests

npm test           # all (unit + live registry + server smoke)
npm run test:unit  # offline: present/delegate signing round-trips
npm run test:live  # hits registry.axisprime.ai public read endpoints

See BUILD-STATUS.md for what's verified and the registrar onboarding contract.

License

TBD. No license is set yet (this repo is private). Likely Apache 2.0 to match the open AXIS stack (protocol, registry, SDK); but MUST be MIT / MIT-0 if this is published as a ClawHub skill (ClawHub external mandate). Decide before the repo goes public or is published (GitHub / npm / ClawHub). Tracked in the Cross-Project Coordination DB (Notion).