agentc
GitHubagentc-sh/agentc
agentc
GitHubagentc-sh/agentc
›Introduction
Get started›Concepts in 5 minutes›Build your first agent›Add your first tool›Serve and connect
Concepts›Architecture overview›The manifest›The compilation pipeline›Archetypes›The graph›Tools and capabilities›Runtime libraries›Skills›Agents and prompts›Serving and protocols›Observability
Guides›Author a manifest›Write a tool›Give your agent a filesystem›Control network egress›Connect external tools via MCP›Connect agents via A2A›Use the bash tool›Control tool access with capabilities›Write templated prompts›Manage prompts with Langfuse›Pass context from the client›Configure a model provider›Connect a CopilotKit frontend›Deploy a standalone binary›Deploy with Docker and PostgreSQL›Instrument with OpenTelemetry›Extend code generation with blocks
Reference
Manifest
Runtime
›Observability

Write a tool

Write a custom tool for your agent in TypeScript or Python.

Tools you write in code come in two languages, TypeScript and Python. This guide covers both. Pick the tab for your language in each step. Both kinds are bundled into the binary at compile time, so there is no runtime installation. For the full field reference, see the tool reference.

TypeScript tools run in an embedded JavaScript runtime. Python tools use RustPython by default, which supports pure-Python packages and needs no Python installation at runtime. Select the static interpreter to use CPython 3.14 for packages with C extensions. Both use the agentc tool development kit (TDK), which gives you typed tool definitions.

Scaffold the package

Generate a ready-to-use package with agentc tool init, then install its dependencies.

agentc tool init math --language javascript
cd math
pnpm install

This creates a ready-to-use package with the entry point, package metadata, and editor type support already configured. agentc.d.ts is one line, and it is what tells an editor the shape of the runtime surface.

The compiler installs dependencies with the package manager configured for the scaffolded package.

Write the tool

A tool has a name, a description, a JSON Schema for its inputs, and an execute function. The name and descriptions are what the model reads to decide when and how to call it, so write them carefully.

Export a tool object from the package entry point. Import the Tool type from the TDK for full type coverage.

math/src/index.ts
import type { Tool, ToolInput, ToolOutput } from "@agentc-sh/tdk";

type AddArgs = { a: number; b: number };

export const adder: Tool<AddArgs, number> = {
  name: "adder",
  description: "Adds two numbers together.",
  parameters: {
    type: "object",
    properties: {
      a: { type: "number", description: "The first number." },
      b: { type: "number", description: "The second number." }
    },
    required: ["a", "b"]
  },
  async execute(input: ToolInput<AddArgs>): Promise<ToolOutput<number>> {
    return { output: input.args.a + input.args.b };
  }
};

A single package can export as many tools as you need.

What the JavaScript runtime provides

JavaScript tools do not run in Node. They run in an embedded JavaScript engine, and the environment they see is assembled by the host, so it is neither Node's nor a browser's. The runtime libraries the host does provide live under the agentc: prefix and are imported like any other module.

math/src/index.ts
import { fetch } from "agentc:http";

const response = await fetch("https://example.com");

agentc:http also installs fetch, Headers, and Response as globals, as the same values it exports, so the import is optional for those three.

math/src/index.ts
const response = await fetch("https://example.com");

The runtime libraries are catalogued at runtime libraries, and the JavaScript surface the host binds is enumerated at standard.

The shapes of all of it come from @agentc-sh/runtime, which the scaffold installs and references for you, so your editor reports what the host actually provides rather than guessing. The package is optional. A component that does not depend on it compiles and runs identically.

`@types/node` is deliberately absent

The scaffold does not depend on @types/node, and its tsconfig.json sets types to an empty array so nothing pulls it in. The guest is not a Node process, and @types/node describes a great deal that the host never binds.

Declare the tool in the manifest

The package exists, but the agent does not see it until you declare a tool block pointing source at the package directory.

agent.acl
tool "adder" {
  kind   = "javascript"
  source = "./math"
}

The block name matches the export name by default. If they differ, set export to the export name.

Write back to state

A tool can return a state_update alongside its output: an array of RFC 6902 JSON Patch operations. For the ReAct graph, target paths under /context/. The patches are persisted and streamed to the client as state delta events, which is how a tool passes data to a frontend.

async execute(input: ToolInput<AddArgs>): Promise<ToolOutput<number>> {
  return {
    output: input.args.a + input.args.b,
    state_update: [
      { op: "add", path: "/context/last_sum", value: input.args.a + input.args.b }
    ]
  };
}

Build

Rebuild so the tool is bundled into the binary:

agentc build

Where to go next

  • Control tool access with capabilities: gate which tools the agent may call.
  • Tools and capabilities: how tools, state, and activity events work.
  • tool reference: every field of the tool block for each kind.
← PreviousAuthor a manifestNext →Give your agent a filesystem

© 2026 pogue.dev. All rights reserved.

Creative CommonsCC BY 4.0
On this pageScaffold the packageWrite the toolWhat the JavaScript runtime providesDeclare the tool in the manifestWrite back to stateBuildWhere to go next

Search docs

Search the agentc documentation