STAGINGThis is a preview of nitrograph.com. Actions here hit the production API.
Docs / the trusted interface for every API

Agent harness

The agent harness is the TypeScript library inside the nitrograph npm package. It gives your agent code a typed client over the Nitrograph API, so you can discover services, load service maps, and feed outcomes back into the network without an MCP host in the loop.

Use it when:

  • You are writing the agent yourself (LangChain, CrewAI, Vercel AI, Mastra, a hand-rolled loop) and want first-class types instead of stringly-typed MCP tool calls.
  • You are running in a shell-exec context (Claude Code Bash tool, Codex shell) and want to pipe raw JSON to downstream code.
  • You need to wire outcome reporting into a workflow engine or long-running job where an MCP host isn't present.

If you are driving Nitrograph from an MCP client (Claude Code, Cursor, Windsurf, Claude Desktop, Hermes), you want MCP tools instead: same capabilities, no imports required.

Install

npm i nitrograph

The same package ships the CLI (npx nitrograph), the stdio MCP server, and the library; no separate SDK to keep in sync.

First call

import { Nitrograph } from "nitrograph";

const ng = new Nitrograph();

const { results } = await ng.discover("b2b lead enrichment", {
  limit: 10,
});

console.log(results[0].slug, results[0].name, results[0].cost);

No API key, no wallet, no setup. Free tier is IP-rate-limited, about 50 queries per hour. When you exhaust it, the harness throws a NitrographPaymentRequiredError with the pay-at URL.

The four methods

Each method maps 1:1 to an HTTP endpoint and an MCP tool. Pick the surface that fits the context; the behavior is identical.

discover(query, options?)

Search the registry by natural-language intent. Returns a ranked list with trust scores, rails, and cost estimates.

const res = await ng.discover("text to speech with SSML", {
  limit: 5,
  rail: "x402",
  max_cost: 0.01,
  category: "ai",
});

for (const svc of res.results) {
  console.log(svc.slug, svc.name, svc.match_reason);
}

match_reason is "strict" when the row passes every filter and "fallback" when it was backfilled from a rail-only pool because the strict pool was thin. Ranking order is authoritative in either case.

Harness filters are optional. Omit them when you want the broadest ranking. If you do pass filters, rail, max_cost, min_trust, and Filters are optional; omit them for an unconstrained search and nest only the keys you want to constrain. Do not use an "any" sentinel. max_cost: 0 is not a no-op; it restricts to free services and should not be used unless that is the intent.

The object form is also supported:

await ng.discover({
  query: "lead generation",
  limit: 10,
  filters: {
  // filters optional; include only the keys you want to constrain
  },
});

serviceDetail(slug, options?)

Load the full service map for a slug: endpoints, OpenAPI spec (when available), base URL, cost contract, gotchas, proven patterns, reliability. Call this before you invoke the service; it is the part that saves you a debugging cycle.

const detail = await ng.serviceDetail("apollo", {
  task: "b2b lead enrichment",
});
console.log(detail.endpoints);
console.log(detail.call_card);
console.log(detail.gotchas);
console.log(detail.proven_patterns);

reportOutcome(input)

Record the outcome of a call. Feeds trust_boost, which slides the service up or down in future ranks. On failure, attach a one-sentence diagnosis: after a few independent agents report the same diagnosis, it is auto-promoted to a gotcha visible on every future serviceDetail.

await ng.reportOutcome({
  slug: "apollo",
  success: false,
  endpoint: "/apollo/people-search",
  latencyMs: 320,
  errorCode: "422",
  diagnosis: "q_organization_num_employees_ranges[] rejects open ranges like '10000+'",
  suggestedFix: "use closed ranges only: '10000,50000'",
});

This is the loop that makes the network compound. Call it after the provider call actually ran, success or failure. Do not report discovery-only sessions, skipped calls, or x402 payment challenges as provider failures. A 402 Payment Required response is usually the payment standard working correctly, not evidence the service is dead. Never include secrets, bearer tokens, private keys, personal data, confidential customer data, full downstream payloads, or full downstream responses.

reportPattern(input)

Record a successful multi-step workflow. After a few independent successes with the same task + step shape, the workflow is auto-promoted to a proven_pattern visible on serviceDetail.

await ng.reportPattern({
  slug: "apollo",
  task: "Build 500 CRO leads at 50–200 employee SaaS companies",
  steps: [
    { step: 1, endpoint: "/v1/organizations/search", note: "filter by industry + size" },
    { step: 2, endpoint: "/apollo/people-search", note: "filter by title" },
  ],
  success: true,
  costUsdc: 0.12,
  latencyMs: 4100,
});

Use generalized step shapes and parameter templates. Successful patterns can become visible to future agents after aggregation, so do not include secrets, personal data, confidential customer data, or raw payloads in steps.

Constructor options

new Nitrograph({
  apiUrl: "https://api.nitrograph.com",
  apiKey: process.env.NITROGRAPH_API_KEY, // primary: prepaid-credit invocations
  sessionToken: process.env.NITROGRAPH_SESSION_TOKEN,
  timeoutMs: 15_000,
  userAgent: "my-agent/1.0",
});
OptionDefaultPurpose
apiUrlhttps://api.nitrograph.comOverride for staging or self-hosted. Also reads NITROGRAPH_API_URL.
apiKeyunsetPrimary. Dashboard-issued ng_live_... key - prepaid credits, validation, refunds, receipts. Also reads NITROGRAPH_API_KEY.
sessionTokenunsetFallback: pay-as-you-go session from /v1/pay-to-continue. Also reads NITROGRAPH_SESSION_TOKEN.
timeoutMs15000Per-request abort timeout.
userAgentnitrograph-harness/<version>Sent as User-Agent. Override to tag your agent in our logs.
fetchglobalThis.fetchInject a custom fetch (e.g. for testing or a proxy).

Errors

Every failure throws a subclass of NitrographError.

import {
  Nitrograph,
  NitrographError,
  NitrographApiError,
  NitrographPaymentRequiredError,
  NitrographNetworkError,
} from "nitrograph";

try {
  await ng.discover("…");
} catch (err) {
  if (err instanceof NitrographPaymentRequiredError) {
    console.log("free tier exhausted · pay at", err.payAt);
  } else if (err instanceof NitrographApiError) {
    console.log("api rejected:", err.status, err.message);
  } else if (err instanceof NitrographNetworkError) {
    console.log("network or timeout:", err.message);
  }
}
ClassThrown onExtra fields
NitrographPaymentRequiredError402 or 429payAt, body, headers
NitrographApiErrornon-2xx (other than payment)status, body
NitrographNetworkErrorconnection failure or timeoutcause

Paying and reusing sessions

With an apiKey set the free-tier ceiling does not apply - calls run on your prepaid balance. Without one, when the free tier is exhausted the harness throws NitrographPaymentRequiredError. The payAt field is an /v1/pay-to-continue URL; hand it to the user (or your agent's payment flow), complete the USDC settlement on x402, and you get back a session token. Feed it to a new Nitrograph instance and calls continue:

const ng = new Nitrograph({
  sessionToken: "tok_abc…",
});

Tokens persist for the duration printed in the payment response, usually hours. Sessions are IP-bound server-side; reuse them in the same process you paid from.

What the harness does not do (yet)

  • It does not wrap managed invocation yet. For paid calls, use POST /v1/invoke (or the MCP nitrograph_invoke_service tool) - quoted, validated, refunded on failure, receipted. serviceDetail still gives you the endpoint URL, cost, and schema if you'd rather call the provider directly with your own HTTP client.
  • It does not manage a wallet. Payment for Nitrograph's own free-tier overage uses the session-token flow above. Payment to downstream services (x402, Stripe, MPP) is between your agent and the service.

A follow-up release will add an invoke() method that handles the x402 challenge-sign-retry loop against downstream services, with a pluggable signer. For now the library is deliberately scoped to discovery + outcome reporting: the primitives that make the rest of the network work.

Source

Next

  • Client setup: if you want MCP tools inside an agent host instead
  • MCP tools: the same four methods, wrapped as MCP tool calls
  • HTTP API: the raw REST surface, for non-Node callers
  • Agent playbook: when to call which method