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

@balchemyai/agent-sdk

v0.3.7

Published

Balchemy external AI agent onboarding and MCP client SDK

Readme

@balchemyai/agent-sdk

TypeScript SDK for external agents that connect to Balchemy through MCP. Use it to onboard an agent, request scoped access, call tools, refresh tokens, and consume SSE events.

Balchemy does not ask an inner model to make trade decisions. Your outer LLM chooses tools. Balchemy checks policy, executes through the authorized path, and keeps the record.

Install

npm install @balchemyai/agent-sdk

Create a Client

import { BalchemyAgentSdk } from "@balchemyai/agent-sdk";

const sdk = new BalchemyAgentSdk({
  apiBaseUrl: "https://api.balchemy.ai/api",
});

apiBaseUrl includes /api and has no trailing slash.

Auth Path 1: SIWE

Use this path when the agent controls a wallet and can sign an off-chain message.

import { BalchemyAgentSdk } from "@balchemyai/agent-sdk";

const sdk = new BalchemyAgentSdk({
  apiBaseUrl: "https://api.balchemy.ai/api",
});

const { message } = await sdk.requestSiweNonce({
  address: "YOUR_WALLET_ADDRESS",
  chainId: 8453,
  domain: "agent.example.com",
  uri: "https://agent.example.com",
  statement: "Sign in to Balchemy",
});

const signature = await wallet.signMessage(message);

const response = await sdk.onboardWithSiwe({
  message,
  signature,
  agentId: "agent-example",
  scope: "trade",
});

const mcp = sdk.connectMcp({
  endpoint: response.mcp.endpoint,
  apiKey: response.mcp.apiKey ?? "",
});

Auth Path 2: Walletless Identity

Use this path when the platform operator has registered an identity provider and the agent holds a valid HMAC identity token or ES256 JWT.

import { BalchemyAgentSdk } from "@balchemyai/agent-sdk";

const sdk = new BalchemyAgentSdk({
  apiBaseUrl: "https://api.balchemy.ai/api",
});

const response = await sdk.onboardWithIdentity({
  provider: "registered-provider-id",
  identityToken: "PROVIDER_JWT",
  agentId: "agent-example",
  chainId: 8453,
  scope: "trade",
});

const mcp = sdk.connectMcp({
  endpoint: response.mcp.endpoint,
  apiKey: response.mcp.apiKey ?? "",
});

If the endpoint returns FEATURE_DISABLED, the backend needs AGENT_WALLETLESS_ONBOARDING_ENABLED=true.

MCP Calls

const { tools } = await mcp.listTools();

const reply = await mcp.askBot({
  message: "What is the price of SOL?",
});

For other tools, call the runtime-advertised name returned by tools/list:

const research = await mcp.callTool("ask_bot", {
  message: "Find low-risk Base setups with deep liquidity.",
});

The SDK does not expose typed swap/trade helper methods from the root client. Privileged tools require the backend-advertised tool surface, the right MCP key scope, replay/rate protection, and the approved product flow.

Scope

| Scope | Access | | --- | --- | | read | Runtime-advertised market data, status, and read-only query tools | | trade | Read tools plus trade execution tools |

The default MCP endpoint exposes a curated agent-facing surface for chat, execution, setup, behavior rules, runtime status, safe reports, runtime control, and subscriptions. Raw research, discovery, and portfolio snapshot tools stay hidden unless they are returned by tools/list for that bot.

Use agent_status for read-scope runtime state. Safe read wrappers are agent_context_snapshot, agent_market_brief, agent_candidate_report, and agent_risk_report. Runtime mutation uses manage-scope agent_control.

AgentLoop fails closed before trade_command: local shadowMode=false is not enough. The backend runtime must report autonomous_runtime.mode=live_armed, armed=true, and paused=false from a fresh agent_status response.

Errors

All SDK methods throw AgentSdkError on failure.

import { AgentSdkError } from "@balchemyai/agent-sdk";
import type { AgentSdkErrorCode } from "@balchemyai/agent-sdk";

type SdkFailure = {
  code: AgentSdkErrorCode;
  message: string;
};

function toSdkFailure(err: unknown): SdkFailure | null {
  if (!(err instanceof AgentSdkError)) {
    return null;
  }

  return {
    code: err.code,
    message: err.message,
  };
}

Known codes include auth_error, policy_error, rate_limit, provider_auth_error, network_error, execution_error, and invalid_response.

Tool Response Helpers

import { getToolText, isToolError, parseToolJson } from "@balchemyai/agent-sdk";

const response = await mcp.callTool("agent_status", {});

const status = isToolError(response)
  ? { error: getToolText(response) }
  : { data: parseToolJson(response) };

Token Store

import { TokenStore } from "@balchemyai/agent-sdk";

const store = new TokenStore({
  refreshFn: async () =>
    sdk.onboardWithIdentity({
      provider: "registered-provider-id",
      identityToken: "PROVIDER_JWT",
      agentId: "agent-example",
      chainId: 8453,
      scope: "trade",
    }),
});

await store.set(response);
const token = await store.get();

The store refreshes when the token is near expiry.

Identity Access Token

OnboardingResponse includes identityAccess when the platform issues a short-lived identity access token beside the MCP key.

import type { IdentityAccess } from "@balchemyai/agent-sdk";

const access: IdentityAccess | undefined = response.identityAccess;
const scope = access?.scope;
const expiresAt = access?.expiresAt;

SSE Events

import { SseEventStream } from "@balchemyai/agent-sdk";
import type { SseEvent } from "@balchemyai/agent-sdk";

const stream = new SseEventStream(
  `${response.mcp.endpoint}/events/sse`,
  response.mcp.apiKey ?? "",
  { reconnectDelayMs: 2000, maxReconnects: 0 }
);

async function readEvents() {
  for await (const event of stream) {
    handleEvent(event);
  }
}

function handleEvent(event: SseEvent) {
  return event;
}

Identity Token Revocation

await sdk.revokeIdentityToken({
  identityToken: response.identityAccess.token,
  jti: "token-jti",
  ttlSeconds: 86400,
});

const { revoked } = await sdk.getIdentityTokenRevokeStatus({
  identityToken: response.identityAccess.token,
  jti: "token-jti",
});

The revoke routes are public at the HTTP guard layer, but they still require an Authorization: Bearer <identity-access-token> proof matching the token ID.

Platform Endpoints

| Endpoint | Path | Auth | | --- | --- | --- | | MCP server | POST /mcp/{publicId} | Authorization: Bearer <mcp-api-key> | | SIWE nonce | POST /api/nest/auth/evm/nonce | Public | | SIWE onboarding | POST /api/public/erc8004/onboarding/siwe | Public | | Walletless onboarding | POST /api/public/erc8004/onboarding/identity | Public | | Token revoke | POST /api/public/erc8004/onboarding/tokens/revoke | Bearer identity proof | | JWKS | GET /.well-known/jwks.json | Public | | MCP discovery | GET /.well-known/mcp.json | Public | | Agent directory | GET /api/nest/agents/verified/page | Public |

JWKS is root-relative. Do not prefix it with /api.

Operator Setup

| Variable | Required | Description | | --- | --- | --- | | AGENT_WALLETLESS_ONBOARDING_ENABLED | Walletless path | Enables identity onboarding. Default: false. | | SIWE_DOMAIN_ALLOWLIST | SIWE path | Comma-separated domains allowed in SIWE verification. | | ERC8004_IDENTITY_PROVIDERS | Walletless path | Registered provider IDs. | | AGENT_IDENTITY_ISSUER_PRIVATE_KEY_PEM | Identity tokens | ES256 private key for short-lived identity access tokens. | | API_URL | Always | Base API URL used to build MCP endpoint URLs. |

Notes

  • agent_seed_request is disabled on the platform.
  • requestSeed() remains for compatibility and throws AgentSdkError with code execution_error.
  • MCP keys are scoped. Choose read or trade during onboarding.
  • Walletless onboarding requires an operator-approved provider.

Docs