Sdk cli

Quickstart

Sixty seconds from installation to your first authorized tool call. Log in, capture a plan, get an intent token, invoke a tool.

This quickstart takes you from a blank shell to a signed, policy-enforced tool call. All you need is an API key (your service identity) and an email (the end user your agent is acting on behalf of).

Step 1 — Install and authenticate

pipx install armoriq-sdk
armoriq login
armoriq whoami

armoriq login opens a browser tab on the dashboard. Sign in, approve the device, and the CLI saves your API key to ~/.armoriq/credentials.json.

$ armoriq whoami

  ArmorIQ Credentials

  Email:    alice@acme.com
  API Key:  ak_live_xxxxxxxx...
  User ID:  8c0b55f8-b052-4041-9d15-02835bd919ad
  Org ID:   144e79f7-7873-41f1-aed8-0ebb5b108200
  Saved at: 2026-04-22T08:48:08+00:00
  File:     /Users/you/.armoriq/credentials.json

Step 2 — Your first authorized tool call

from armoriq_sdk import ArmorIQClient

# The only required argument is api_key. After `armoriq login`,
# the client reads it automatically from ~/.armoriq/credentials.json.
client = ArmorIQClient()

# Describe what the agent wants to do — one step per tool call.
plan_definition = {
    "goal": "Get weather for San Francisco",
    "steps": [
        {
            "action": "get_weather",
            "mcp": "weather-mcp",
            "params": {"city": "San Francisco", "units": "fahrenheit"},
        },
    ],
}

# Capture the plan (signs it against the LLM's reasoning).
plan = client.capture_plan(
    llm="gpt-4o",
    prompt="What's the weather in San Francisco?",
    plan=plan_definition,
)

# Get a short-lived intent token (5 minutes by default).
token = client.get_intent_token(plan, validity_seconds=300)

# Invoke the tool. `user_email` attaches the end-user identity
# used for audit trails and per-user policy enforcement.
result = client.invoke(
    mcp="weather-mcp",
    action="get_weather",
    intent_token=token,
    params={"city": "San Francisco", "units": "fahrenheit"},
    user_email="alice@customer.com",
)

print(result.data)
client.close()
import { ArmorIQClient } from '@armoriq/sdk';

const client = new ArmorIQClient({
  apiKey: process.env.ARMORIQ_API_KEY!,   // or read credentials.json manually
  userId: 'weather-service',
  agentId: 'weather-bot',
});

const planDefinition = {
  goal: 'Get weather for San Francisco',
  steps: [
    {
      action: 'get_weather',
      mcp: 'weather-mcp',
      params: { city: 'San Francisco', units: 'fahrenheit' },
    },
  ],
};

const plan  = await client.capturePlan({
  llm: 'gpt-4o',
  prompt: "What's the weather in San Francisco?",
  plan: planDefinition,
});

const token = await client.getIntentToken(plan, { validitySeconds: 300 });

// TypeScript passes `userEmail` via the InvokeOptions param to invokeWithPolicy.
const result = await client.invokeWithPolicy(
  'weather-mcp',
  'get_weather',
  token,
  { city: 'San Francisco', units: 'fahrenheit' },
  { userEmail: 'alice@customer.com' },
);

console.log(result.data);

That's it. The proxy verified the signed plan, checked the policy for weather-mcp.get_weather, confirmed alice@customer.com has permission, forwarded the call to the MCP server, and the result is in your hands.

What just happened

  Your agent              ArmorIQ proxy              weather-mcp
      │                        │                          │
      │  capture_plan ─────────┼──► plan stored + signed  │
      │                        │                          │
      │  get_intent_token ─────┼──► token minted          │
      │                        │                          │
      │  invoke ───────────────►                          │
      │                        │ 1. verify token          │
      │                        │ 2. check policy          │
      │                        │ 3. forward to mcp ──────►│
      │                        │                          │
      │                        │◄──── tool response ──────┤
      │◄────── result ─────────│                          │
      │                        │                          │
      │                        │  audit log written       │

Three services, three guarantees:

  • Intent tokens — cryptographically binds your tool call to the LLM reasoning the plan was captured from. An attacker who steals the token can't alter the call.
  • Policy enforcement — the proxy checks weather-mcp.get_weather against your allow-list before forwarding. Deny rules always win over allow.
  • Per-user audit — every call appears in the audit log tagged with alice@customer.com.

Customize the pattern

No end user (cron jobs, background workers)

Skip user_email. Audit logs record the service-account identity derived from your API key.

result = client.invoke(
    mcp="weather-mcp",
    action="get_weather",
    intent_token=token,
    params={"city": "San Francisco"},
    # no user_email — service-account call
)

Many end users (SaaS multi-tenant)

Call invoke(..., user_email=email) per request. The client is cheap to share across users:

client = ArmorIQClient()

for user_email in ["alice@acme.com", "bob@acme.com", "carol@acme.com"]:
    plan  = client.capture_plan(llm="gpt-4o", prompt=..., plan=...)
    token = client.get_intent_token(plan)
    client.invoke(
        mcp="weather-mcp",
        action="get_weather",
        intent_token=token,
        params={...},
        user_email=user_email,
    )

For long-running sessions (multi-turn conversations with many tool calls), see the session pattern in the Python SDK deep dive.

Config-as-code (teams, CI/CD)

If you want your policy and MCP list in version control instead of code, use armoriq init to scaffold a YAML, then ArmorIQClient.from_config():

client = ArmorIQClient.from_config("armoriq.yaml")

See the full YAML reference and the CLI commands init / validate / register.

Troubleshooting

What you seeWhyFix
ConfigurationException: API key is requiredNo key resolvedRun armoriq login, or set ARMORIQ_API_KEY
InvalidTokenException at invokeToken doesn't match the plan that was signedCapture a fresh plan; don't edit the token's underlying plan
PolicyBlockedExceptionThe tool isn't in your allow-list for this userCheck policies in the dashboard → Policies
TokenExpiredExceptionToken older than validity_secondsGet a new token with get_intent_token
IntentMismatchExceptionPlan steps don't match what was actually invokedYour invoke() args don't match plan.steps — align them

Next steps

On this page