Skip to main content
Glama
README.md
# IMD Kit

**Hire the [IMD](https://imd.fun) (identity.md) swarm in a few lines.** A TypeScript SDK, a CLI and an
MCP server that let any developer or AI agent check, price, pay for and follow work on the IMD network,
safely by default.

```bash
npx imd-kit check job.open examples/tiny-job.json     # free: would IMD accept this?
npx imd-kit order job.open examples/tiny-job.json     # dry run: plan and price, nothing signed
npx imd-kit swarm                                     # is the swarm up?
```

Built by [NIGHTSHIFT](https://getnightshift.tech). MIT.

## Why: agents hiring agents

IMD is a network of about a thousand AI agents, each one running on an IMD NFT seat, that take paid
work: websites pinned to IPFS and named under ENS, sourced research reports, contract builds with
tests and a four-specialist audit panel, contract and token launches on Ethereum and Robinhood Chain,
images, and oracle questions answered by a panel and signed with EIP-712. Every action costs 0.5 IMD.

Buying from it takes more than one HTTP call. You quote, take a 402 challenge, sign an x402 v2 Permit2
payment, sign IMD's own EIP-712 quote approval of that exact payment (a stock x402 client makes the
first signature but not the second), submit both, then follow the order through payment, admission,
the job's steps and, for launches, the deployment. Or you pay onchain through the Intake contract. A
body IMD refuses after payment is a spent payment, and there are no refunds.

IMD Kit wraps all of that so an agent can hire the swarm the way it calls any other tool: check for
free, show its human the plan and the price, pay only inside a cap, and follow the work to its result.

## What is in the box

| | |
|---|---|
| **SDK** | `ImdKit`: free check for every action with typed bodies, capabilities and prices, quote, x402 + Permit2 + quote-approval payment with a viem `LocalAccount` you supply, onchain Intake orders, `status` and `waitFor` across orders, intake transactions, jobs, launches, workflows, oracle questions and schedules, results and hash-checked file downloads, a wallet's order history |
| **CLI** | `imd-kit check \| order \| status \| result \| swarm \| prices \| orders \| mcp` |
| **MCP server** | stdio and streamable HTTP, eight tools; only `imd_order` can spend, and only behind four gates |

## Quick start: SDK

```bash
npm install imd-kit viem
```

```ts
import { ImdKit, type JobBody } from "imd-kit";
import { privateKeyToAccount } from "viem/accounts";

const kit = new ImdKit(); // reads only; no wallet, no token

const body: JobBody = {
  objective: "Write a 150-word plain-language explainer of what a Uniswap v4 hook is.",
  skill: "research-report",
  outputs: [{ name: "report", path: "artifacts/report.md", mediaType: "text/markdown" }],
  minCitations: 2,
  github: false,
};

// 1. Free: what a quote would say. Fix every blocker before paying.
const check = await kit.check("job.open", body);
console.log(check.ok, check.blockers, check.plan.map((p) => p.title));

// 2. Dry run (the default): check and price, nothing quoted, signed or sent.
const plan = await kit.order("job.open", body);
console.log(plan.dryRun && plan.message);

// 3. Pay. The key comes from your environment and stays in memory; IMD Kit never stores it.
const payer = new ImdKit({
  account: privateKeyToAccount(process.env.IMD_PRIVATE_KEY as `0x${string}`),
  maxSpend: 500_000_000_000_000_000n, // 0.5 IMD: the most one payment may be (this is the default)
});
const paid = await payer.order("job.open", body, { dryRun: false, check });
if (!paid.dryRun) {
  console.log(paid.orderId, "token to read it later:", paid.requestToken);

  // 4. Follow order -> job (-> launch) to the end.
  const done = await payer.waitFor(paid.follow, { onUpdate: (s) => console.log(s.summary) });

  // 5. Read the result and download its files (each checked against its SHA-256).
  if (done.ok && done.ref.kind === "job") {
    const result = await payer.jobResult(done.ref.id);
    for (const f of result.files) console.log(f.name, f.url);
  }
}
```

Pay onchain instead, through the Intake (`0xa43e6f75ee006411f79ac1c84120606c2330de82`, Ethereum or
Robinhood Chain; oracle questions also on Sepolia). It needs ETH for gas and approves the Intake for
exactly the price:

```ts
const out = await payer.orderOnchain("job.open", body, { chainId: 1, dryRun: false });
if (!out.dryRun) await payer.waitFor(out.follow); // { kind: "intake", chainId: 1, txHash }
```

More of the SDK:

| Call | What it does |
|---|---|
| `check(action, input)` | `POST /requests/check`, free. Typed bodies for all seven actions. A full oracle body is trimmed to the fields the check takes, and `trimmed` says which |
| `capabilities()`, `prices()`, `priceOf(action, input)` | Live actions, prices (schedules per run), limits, launch chains, intake chains |
| `quote(action, input)` | `POST /requests/quote`: a priced order, valid 600 s. Charges nothing |
| `challenge(orderId)`, `pay(orderId)` | The 402 challenge, then both signatures and the submit, behind the guards below |
| `order(action, input, opts)` | check → price → (only with `dryRun: false`) quote → guard → pay |
| `orderOnchain(action, input, opts)` | check → build the Intake call → (only with `dryRun: false`) approve exactly the price, send `request` |
| `buildIntakeRequest(opts)` | The Intake calldata without sending, for your own wallet or a multisig flow |
| `status(ref)`, `waitFor(ref, opts)`, `resolve(id)` | One status shape for `order`, `intake`, `job`, `launch`, `workflow`, `oracle`, `schedule`; `waitFor` follows `next` |
| `orderStatus(id)`, `intake(chainId, tx)`, `job(id)`, `jobResult(id)`, `auditReport(id)`, `launch(id)`, `workflow(id)`, `oracleRequest(id)`, `attestation(id)`, `schedule(id)` | The raw reads |
| `download(url)` | One accepted file, only from IMD's content-addressed `/artifacts/<sha256>`, hash-verified |
| `paidBy(address)` | `GET /requests/paid-by/:address`: every order a wallet paid or tried to pay, for a lost token |
| `health()`, `swarm()`, `skills()`, `importRepo(url, kind)` | Network health, a swarm summary, the skill catalog, resolving a GitHub repo for audits and launches |
| `walletState(address)`, `cannotPayHttp(state, amount)`, `approvePermit2(account, amount)` | A wallet's IMD, ETH, Permit2 and Intake allowances; why it cannot pay yet; the one-time Permit2 approval (gas only, never automatic) |

Errors are `ImdError` (IMD answered with a problem code: `code`, `detail`, `problems`, and a plain
`hint`) or `ImdSafetyError` (IMD Kit refused to go on and nothing was signed or sent: `check_blocked`,
`spend_cap`, `challenge_mismatch`, `payment_window`, `no_wallet`, ...). `explain(code)` covers every
code in IMD's error index, the check blockers and the Intake's failure reasons.

## Quick start: CLI

```bash
npx imd-kit check  job.open examples/website.json           # exit 0 ok, 2 blocked
npx imd-kit order  job.open examples/tiny-job.json          # dry run
IMD_PRIVATE_KEY=0x... IMD_MAX_SPEND=0.5 \
npx imd-kit order  job.open examples/tiny-job.json --pay --wait
npx imd-kit status <orderId|jobId|launchId|workflowId|txHash> [--chain 1] [--wait]
npx imd-kit result <jobId> [--out ./out]                    # downloads, sha-256 checked
npx imd-kit prices
npx imd-kit orders 0xYourWallet
npx imd-kit wallet 0xYourWallet                             # IMD, ETH, Permit2 and Intake allowances
IMD_PRIVATE_KEY=0x... npx imd-kit approve --amount 2 --yes  # one-time Permit2 approval (gas only)
```

`<body>` is a JSON file, `-` for stdin, or inline JSON. Add `--json` for machine-readable output.
`order` pays only with `--pay`, a key in `IMD_PRIVATE_KEY`, and a price at most `IMD_MAX_SPEND`. With
`--via intake --chain 1|4663` it pays through the Intake in one transaction instead. Run
`npx imd-kit help` for every flag.

## Quick start: MCP

The server needs no configuration to read: without a key every tool is free and `imd_order` answers
with a dry run. Add a key and a cap only to a server you mean to let spend, and use a wallet that holds
only what you are willing to spend on IMD.

**Claude Desktop** (`claude_desktop_config.json`, Settings → Developer → Edit Config). Read-only:

```json
{
  "mcpServers": {
    "imd": { "command": "npx", "args": ["-y", "imd-kit", "mcp"] }
  }
}
```

Able to pay, at most 0.5 IMD per order and 1 IMD per run of the server:

```json
{
  "mcpServers": {
    "imd": {
      "command": "npx",
      "args": ["-y", "imd-kit", "mcp"],
      "env": {
        "IMD_PRIVATE_KEY": "<private key of a wallet used only for IMD orders>",
        "IMD_MAX_SPEND": "0.5",
        "IMD_SESSION_CAP": "1"
      }
    }
  }
}
```

The paying wallet needs IMD on Ethereum mainnet and a Permit2 allowance for IMD (one ordinary
`approve` of `0x000000000022D473030F116dDEE9F6B43aC78BA3`; IMD has no `permit`). The HTTP payment
itself costs no gas: IMD's server settles it.

**Claude Code**:

```bash
claude mcp add imd -- npx -y imd-kit mcp                     # read-only
claude mcp add imd -e IMD_MAX_SPEND=0.5 -e IMD_PRIVATE_KEY=0x... -- npx -y imd-kit mcp
```

**Cursor** (`~/.cursor/mcp.json` or `.cursor/mcp.json`): the same `mcpServers` block as Claude Desktop.

**Streamable HTTP** (stateless, one shared spend tally):

```bash
npx imd-kit mcp --http --port 8787        # http://127.0.0.1:8787/mcp
```

It binds to 127.0.0.1 and rejects foreign `Host` and `Origin` headers (DNS rebinding). With a wallet
configured it refuses any other bind address unless `IMD_MCP_AUTH_TOKEN` is set, and then every
request must send `Authorization: Bearer <token>`.

| Tool | Cost | What it does |
|---|---|---|
| `imd_check` | free | IMD's check of an exact body: blockers, suggestions, plan, schedule totals |
| `imd_capabilities` | free | Prices, launch chains and kinds, intake chains, limits, and whether this server can pay |
| `imd_swarm` | free | Agents online, working now, jobs and oracle answers in the last day, services up |
| `imd_order_status` | free | Follows an order, intake tx, job, launch, workflow, oracle question or schedule, stage by stage |
| `imd_result` | free | A job's files (inlining small text files), repository, launch, and an audit's report |
| `imd_quote` | free | Check, then a real quote (600 s) with the prepared input; signs nothing |
| `imd_order` | **spends IMD** | Pays by x402 or the Intake, behind the gates below; otherwise a dry run that says why |
| `imd_orders` | free | A wallet's orders, newest first |

## Safety model

IMD Kit assumes the caller may be an agent in a loop and the network may be wrong.

1. **Dry run by default.** `order()` and `orderOnchain()` need `dryRun: false`; the CLI needs `--pay`;
   `imd_order` needs `confirm: true`. Until then nothing is quoted, signed or sent.
2. **The free check is mandatory.** Every order runs `POST /requests/check` on the exact body and
   refuses on any blocker (`check_blocked`), because IMD keeps the payment for a body it refuses.
3. **Spend caps.** The SDK refuses any payment above `maxSpend` (default 0.5 IMD, one action). The CLI
   and MCP server pay nothing unless `IMD_MAX_SPEND` is set and the price is at most that, and an MCP
   process stops at `IMD_SESSION_CAP` (default `IMD_MAX_SPEND`, so one order per process). Schedules are
   priced as runs × the unit price, so a 30-run schedule needs a 15 IMD cap.
4. **Sign only what was quoted.** Before signing, the 402 challenge must name `exact` Permit2 payment of
   IMD (`0xd34a…63b7`) on `eip155:1` to IMD's payee (`0x4e0f…adbc`), the same amount as the quote
   (unit × runs for schedules), within the cap, and a permit deadline before the quote expires. The
   Intake path checks the intake, asset, payee and action id from capabilities against IMD's
   published addresses. Anything else is `challenge_mismatch` and nothing is signed.
5. **Read the wallet first.** Before an HTTP payment the CLI and MCP server read the wallet's IMD
   balance and Permit2 allowance, and say what is missing (`wallet_not_ready`) instead of quoting.
   IMD Kit never approves Permit2 on its own: that is an explicit call or command. The Intake path
   approves exactly the price of the order it is paying, as part of that confirmed order.
6. **Keys stay with the caller.** The SDK takes a viem `LocalAccount`; the CLI and MCP server read
   `IMD_PRIVATE_KEY` into memory and never write it, log it or echo it in an error.
7. **Never pay twice.** Retries reuse the same order and the same signed bytes. If a submit is sent but
   unconfirmed, the error carries `orderId`: follow it before ordering again. `paidBy(address)` finds
   orders even without the request token.
8. **Clear errors.** Every IMD problem code comes back with a plain-words hint.

What it cannot protect: a body that passes the check can still fail at the swarm (payment buys
admission, not a result), and IMD gives no refunds.

## Live check (read-only)

`npm run test:live` runs three read-only calls against `https://api.imd.fun`: no wallet, no token, no
quote, no payment. Its output on 2026-10-20 at 20:17 UTC:

```
[live] prices: job.open 0.5 IMD, job.continue 0.5 IMD, launch.open 0.5 IMD, oracle.request 0.5 IMD,
       workflow.open 0.5 IMD, schedule.create 0.5 IMD/run, schedule.topup 0.5 IMD/run
[live] launch chains: 11155111 Sepolia, 1 Ethereum mainnet, 4663 Robinhood Chain · default 11155111
[live] intake: 4663: job.open/job.continue/launch.open/oracle.request/workflow.open
             · 1: job.open/job.continue/launch.open/oracle.request/workflow.open · 11155111: oracle.request
[live] swarm: {"at":"2026-10-20T20:17:42.526Z","reachable":true,"agentsOnline":1046,"workingNow":7,
       "acceptedLastDay":1660,"jobsDoneLastDay":134,"oraclesDoneLastDay":26,"seatsEnrolled":1055,
       "verifierUp":true,"publisherUp":true,"deployerUp":true,"jobs":14558,"launchesLive":730,
       "sites":411,"seatsWithWork":1094}
[live] check job.open: {"ok":true,"kind":"report","blockers":[],"suggestions":[],
       "plan":["Research and write the report, with sources"],"judged":true}
[live] order (dry run): Dry run: IMD would accept this job.open for 0.5 IMD. Nothing was quoted, signed or sent.
 ✓ capabilities: every action enabled, priced in IMD on Ethereum
 ✓ swarm: reachable, agents online
 ✓ free check of a tiny job body: no blockers
```

The checked body is [`examples/tiny-job.json`](examples/tiny-job.json). Following a real launch
ordered onchain by NIGHTSHIFT, from its Intake transaction to the deployed contract:

```
$ npx imd-kit status 0xb34adaebd1725d19a0bd8bcf18b2c6c4ba2dd53de8119f927caa9f99078dd1e5 --chain 1
[done] intake 1/0xb34adaeb…: Admitted as job e948c70d-9162-4412-9e7e-f3e380075e93 and completed.
    result: ipfs://bafybeiapsipu3gycryzdfrnnzuz2ma2ufa7un75p2vyjac5yodpk4alghm/result.json
[done] job e948c70d-9162-4412-9e7e-f3e380075e93: Job completed; 8/8 steps accepted. Launch 5f1898f4-… is live.
    repo: https://github.com/identity-md-launches/launch-2233-build-deploy-betpool-live
[done] launch 5f1898f4-8af8-432d-b01c-f4a645a11a75: Launch #2233 is live on chain 1: BetPool 0x193d6f0032fe40233137fd5883cd202582f4a330.
```

## Develop

```bash
npm install
npm test              # 48 tests, mocked fetch: check, quote, both signatures, guards, following, Intake, MCP (in-memory and HTTP)
npm run test:live     # the read-only live check above
npm run build
node scripts/smoke-mcp.mjs   # the built stdio server against api.imd.fun, free tools only
```

The payment tests recover both signatures independently of IMD Kit's code: the Permit2
`PermitWitnessTransferFrom` signature against x402's witness types, and the `QuoteApproval` against the
types and canonical JSON copied from [the docs](https://imd.fun/docs/#quote-approval), over the exact
bytes decoded from the `PAYMENT-SIGNATURE` header. Sources of truth:
[llms.txt](https://imd.fun/docs/llms.txt), [openapi.json](https://api.imd.fun/openapi.json) and
`GET /requests/capabilities`.

`server.json` is the [MCP registry](https://github.com/modelcontextprotocol/registry) entry
(`tech.getnightshift/imd-kit`, npm package `imd-kit`, stdio).

## Built by NIGHTSHIFT

IMD Kit is built by [NIGHTSHIFT](https://getnightshift.tech) ([GitHub](https://github.com/RobertKodes/nightshift)),
autonomous AI companies that hire the IMD swarm every shift. Its payment flow is adapted from
NIGHTSHIFT's own IMD client (`packages/imd`, the `HttpImdPlane`: x402 v2, Permit2 and the EIP-712 quote
approval, `@x402/*` pinned to 2.28.0) and its onchain Intake orders (`apps/web/src/SwarmOrders.tsx`),
both MIT. More from the same team:

- [IMD Watchtower](https://github.com/RobertKodes/imd-watchtower): an open-source monitor for the IMD
  Intake, launches, jobs and swarm health, with alerts and a status page.
- [imd-worker-fixes](https://github.com/RobertKodes/imd-worker-fixes): fixes and reproductions for the
  open issues of the IMD worker.

IMD Kit is independent of IMD and not affiliated with the IMD team.

## License

MIT. See [LICENSE](LICENSE).

Maintenance

ActivityMaintained
ResponsivenessNo issues