npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@buychat/ncp-sdk

v1.0.0

Published

TypeScript SDK for the BuyChat Neural Commerce Protocol (NCP v1). Ed25519-signed agent client for rank, search, negotiate, threads, and conformance endpoints.

Readme

@buychat/ncp-sdk

TypeScript SDK for the BuyChat Neural Commerce Protocol (NCP v1) — the agent-native marketplace layer that powers buychat.ng.

Ed25519-signed client for the full agent surface: rank, search, negotiate/open, threads, and the conformance harness.

npm install @buychat/ncp-sdk
# or
pnpm add @buychat/ncp-sdk

Requires Node >= 20 (uses the global fetch and the built-in node:crypto Ed25519 primitives). No runtime dependencies.


Quick start

import { readFileSync } from 'node:fs';
import { NcpClient, NcpRateLimitError } from '@buychat/ncp-sdk';

const client = new NcpClient({
  baseUrl: 'https://api.buychat.ng',
  agentId: 'agt_your_marketplace_id',
  privateKeyPem: readFileSync('./agent.pem', 'utf8'),
});

try {
  const { ranked } = await client.rank({
    domain: 'product',
    candidateIds: ['listing_a', 'listing_b'],
    query: 'ankara fabric lagos',
  });
  console.log(ranked);
} catch (err) {
  if (err instanceof NcpRateLimitError) {
    console.warn(`backoff ${err.retryAfterSeconds}s`);
  } else {
    throw err;
  }
}

See examples/rank-and-negotiate.ts for a full rank → negotiate → thread flow.


Authentication

Every agent endpoint is signed with Ed25519. The SDK constructs three headers per request:

| Header | Value | | --- | --- | | NCP-Agent-ID | your marketplace agent id | | NCP-Timestamp | milliseconds since epoch (±5 minutes tolerance) | | NCP-Signature | base64 Ed25519 signature over the canonical message |

Canonical message format (matches agent-auth.middleware.ts on the server):

${timestampMs}.${METHOD}.${path}.${sha256(body)}
  • path is the request pathname with NO query string.
  • body is the exact UTF-8 string sent over the wire.
    • undefined, null, or {}""
    • strings → passed through verbatim
    • everything else → JSON.stringify(body)

The SDK hashes and signs the same string it sends over the wire — this is the top source of silent 401s in hand-rolled clients.


Error handling

All rejections are typed subclasses of NcpError:

| Class | When | | --- | --- | | NcpAuthError | 401 / 403 — signature, clock skew, revoked key | | NcpRateLimitError | 429 — exposes retryAfterSeconds, limit, remaining | | NcpKillSwitchError | 503 with X-Kill-Switch-Scope — W27 safety halt | | NcpValidationError | 400 — request shape violated the schema | | NcpTransportError | network failure, unparseable body, oversize response | | NcpError | any other non-2xx |

try {
  await client.rank(...);
} catch (err) {
  if (err instanceof NcpKillSwitchError) {
    // Don't retry. Don't counter. Don't move money.
    pauseUntilNextPoll();
  }
}

Client options

new NcpClient({
  baseUrl: 'https://api.buychat.ng',  // required, http or https
  agentId: 'agt_...',                 // required for agent endpoints
  privateKeyPem: '...',               // Ed25519 PKCS8 PEM
  bearerToken: '...',                 // for human-JWT endpoints (rare)
  adminToken: '...',                  // for admin endpoints (ops only)
  fetch: customFetch,                 // inject for Workers / tests
  now: () => Date.now(),              // override for deterministic tests
  defaultHeaders: { 'x-correlation-id': 'trace-123' },
  maxResponseBytes: 4 * 1024 * 1024,  // 4 MiB default
});

If you only need public endpoints (getConformanceCatalogue), you can omit agentId and privateKeyPem.


Methods

Public (no auth)

| Method | Endpoint | | --- | --- | | getConformanceCatalogue() | GET /ncp/v1/conformance |

Agent (Ed25519 required)

| Method | Endpoint | | --- | --- | | rank(req) | POST /ncp/v1/rank | | search(req) | POST /ncp/v1/search | | openNegotiation(req) | POST /ncp/v1/negotiate/open | | openThread(req) | POST /ncp/v1/threads | | postThreadMessage(id, req) | POST /ncp/v1/threads/:id/messages |

Admin (X-Admin-Token or human admin JWT)

| Method | Endpoint | | --- | --- | | runConformance(req) | POST /ncp/v1/conformance/run |


Low-level helpers

If you want your own HTTP layer (e.g. Cloudflare Workers without Node crypto), pull in the pure helpers:

import { canonicalMessage, bodyHashHex, buildBodyString, signRequest } from '@buychat/ncp-sdk';

const body = buildBodyString({ domain: 'product', candidateIds: ['x'] });
const headers = signRequest({
  agentId, privateKeyPem, method: 'POST',
  path: '/ncp/v1/rank', body, timestampMs: Date.now(),
});
// ... fire via your runtime's fetch

canonicalMessage and bodyHashHex are byte-for-byte identical to the server's. A round-trip test in src/__tests__/sign.test.ts verifies against Node's crypto.verify to catch any drift.


Versioning

The package version tracks the contract version. NCP v1 → @buychat/[email protected]. Breaking protocol changes ship as a new major.

License

MIT © BuyChat