Skip to content

API Reference

The same API, six languages. Every entry below shows its exact per-language equivalent in synced tabs — pick your language once and it sticks across the page. Per-port index pages: JavaScript · Python · Go · Java · C# · Elixir.

The one aggregator: MCP tools + skills + built-ins + A2A agents + your own tools behind one Tool interface.

import { createToolkit } from "toolnexus"
const tk = await createToolkit({
mcpConfig: "./mcp.json", // path | object
skillsDir: "./skills", // string | string[]
skills: [/* SkillDef[] */], // data skills
skillProvider: async () => [/* SkillDef[] */], // lazy, resolved once
skillsFilter: { deploy: true }, // per-agent allowlist
skillSampleLimit: 0, // 0=10, n=cap, -1=off
builtins: true,
agents: [/* Agent[] */],
extraTools: [/* Tool[] */],
})

Skills — load, supply as data, filter, inventory

Section titled “Skills — load, supply as data, filter, inventory”

Directories, in-memory SkillDefs, and a lazy provider all compose (first-wins dedupe). See the Agent skills guide for the concepts; this is the exact surface.

interface SkillDef {
name: string
description?: string
content: string
resources?: string[] // logical names; nil ⇒ instruction-only
base?: string // logical base; default "skill://<name>/"
}

Discover + validate without building a toolkit. Returns parsed skills plus typed skip reasons: missing-name · malformed-frontmatter · duplicate-name · unreadable.

import { listSkills } from "toolnexus"
const inv = listSkills({ dirs: "./skills" })
// inv.skills: SkillInfo[] inv.skipped: { location, reason }[]

Read an mcp.json, connect every server (stdio + streamable-HTTP), expose each tool as a Tool.

import { loadMcp } from "toolnexus"
const mcp = await loadMcp("./mcp.json") // mcp.tools: Tool[]

Turn a toolkit into the tool-schema shape each provider expects.

import { toOpenAI, toAnthropic, toGemini } from "toolnexus"
const tools = toOpenAI(tk) // toAnthropic(tk) · toGemini(tk)
import { createClient } from "toolnexus"
const client = createClient({ model: "gpt-5", baseURL: "", apiKey: "" })
const res = await client.ask("", tk)

Wrap a function as a Tool (source: "native"). Pass them via extraTools or hand them to an agent’s uses.tools. See the Native tools guide.

import { defineTool } from "toolnexus"
const add = defineTool({
name: "add", description: "Add two numbers",
run: (a) => String(Number(a.x) + Number(a.y)),
})
// decorator sugar: @tool(description, { name?, inputSchema? }) + collectTools(obj)

Turn any endpoint into a Tool (source: "http"). {placeholder} path substitution, ${ENV_VAR} header expansion (never logged), resultMode text (default) · json · status+text. See the HTTP tools guide.

import { httpTool } from "toolnexus"
const weather = httpTool({
name: "weather", description: "Current weather",
method: "GET", url: "https://api.example.com/weather/{city}",
}) // timeout ms (default 30000)

builtins (toolkit option, default on) ships the same ten tools in every port, source: "builtin", identical names:

bash · read · write · edit · grep · glob · webfetch · question · apply_patch · todowrite

question suspends through the human loop (a kind: "question" Request). The toggle accepts true | false | { enabled?, disabled?, tools?: { <name>: false } }disabled: true wins, and a tools entry set to false drops that one built-in. See the Built-in tools guide.

const tk = await createToolkit({
builtins: { tools: { bash: false } }, // all built-ins except bash
})

An Agent is a Tool: a soul (system prompt) × a scoped toolkit × the client loop. team is the only set of targets the built-in task tool can delegate to — no team ⇒ no task tool. asTool turns an agent into a Tool for extraTools; composeSoul builds a soul from a directory (AGENTS.md + sections). Result statusdone · pending · incomplete · interrupted · closed · timeout · error. An agent (or the runtime) also accepts hooks and onMetric — the same two lifecycle seams a hand-built client takes, forwarded verbatim; an agent’s value replaces the runtime-wide one, which is how a compactor gives each agent its own context budget. See the Sub-agents & teams guide.

import { agents } from "toolnexus"
const explore = agents.agent("explore", { does: "read-only research", uses: { tools: [lookup] } })
const coder = agents.agent("coder", {
does: "implements changes", soulFile: "./AGENTS.md",
team: [explore], budget: { maxTokens: 10_000 },
})
const r = await coder.run("fix the failing test", {
llm: { baseUrl: "", style: "openai", model: "gpt-4o-mini" },
})
// r.status · r.text · r.totalTokens ; delegate as a tool: explore.asTool({ llm })

A2A — serve inbound, consume remote agents

Section titled “A2A — serve inbound, consume remote agents”

Two directions of the same protocol (JSON-RPC 2.0; card at GET /.well-known/agent-card.json). Serve exposes a toolkit as a remote A2A agent; remote agents pull a peer’s card and expose its skills as <agent>_<skill> tools (source: "a2a"). See the A2A guide.

const handle = await tk.serve("127.0.0.1:0", {
client: llm,
a2a: { name: "my-agent", store: "memory" }, // store: "memory" | "file:<dir>" | TaskStore
})
// handle.url speaks A2A ; await handle.stop()
const tk = await createToolkit({
agents: [agent({ card: "https://peer.example.com/.well-known/agent-card.json" })],
})
await tk.addAgent("https://other.example.com/.well-known/agent-card.json")

waitFor is the host resolver (Request) → Answer. A tool suspends by returning a pending result (isError: true, metadata.pending = Request); the loop calls waitFor, then re-runs the tool with Answer on the tool context. Request { id, kind, prompt, url?, data?, expiresAt? } · Answer { id, ok, data?, reason? }. This is also the bridge for MCP elicitation and the built-in question tool. See the Suspension guide.

import { pending, authRequired, pendingOf } from "toolnexus"
const tk = await createToolkit({
waitFor: async (req) => ({ id: req.id, ok: true, data: { answer: "" } }),
})
// inside a tool: return pending({ kind: "input", prompt: "…" }) // or authRequired(url)