Quick Start
This page takes you from nothing to a governed agent in a few minutes. Everything here is copy-paste; the snippets mirror the patterns the SDK's own test suite exercises.
1. Install
- pnpm
- npm
- yarn
- bun
pnpm add @agent-assembly/sdk
npm install @agent-assembly/sdk
yarn add @agent-assembly/sdk
bun add @agent-assembly/sdk
The package ships dual ESM/CJS entries and selects a prebuilt native binding for your
platform during postinstall, so there is no extra build step for typical consumers.
The public surface (initAssembly, withAssembly) is stabilizing but may change
between pre-releases. Pin an exact version for reproducible installs:
npm install @agent-assembly/sdk@0.0.1-rc.6
2. Make sure a gateway is reachable
The SDK enforces policy by talking to an Agent Assembly gateway. You have two options:
- Let the SDK auto-start a local gateway. If you have the
aasmbinary on yourPATH(brew install ai-agent-assembly/tap/aasm) and setAA_AUTO_START=1, a zero-configinitAssembly()will probehttp://localhost:7391and start a local gateway for you if nothing is running. Auto-start is opt-in — without it, a missing gateway throws aConfigurationErrorinstead of spawning anything. - Point at a gateway you already run. Set
AAASM_GATEWAY_URL(andAAASM_API_KEYif it requires auth), or passgatewayUrlexplicitly.
:7391 REST + :50051 gRPCStarting the local gateway binds two loopback surfaces in one process:
aasm start --mode local
This exposes the REST API on http://localhost:7391 (what gatewayUrl points to, and
what the SDK probes and, with AA_AUTO_START=1, auto-starts) and the gRPC
AgentLifecycleService on 127.0.0.1:50051, which is the endpoint the native
aa-sdk-client binding dials to register your agent. You don't configure :50051
yourself — registration dials it automatically — so a no-argument initAssembly() both
connects and shows the agent in the dashboard once a gateway is reachable.
To confirm both surfaces are actually up rather than guessing from the SDK's behavior, check them directly:
curl http://localhost:7391/healthz # REST — real JSON: mode, storage, version, uptime_secs
nc -z localhost 50051 && echo "gRPC port open" # gRPC has no health endpoint yet; this only confirms the port accepts connections
See Configuration for the full resolution order.
3. Govern your first agent with withAssembly
withAssembly wraps a map of tools so the local policy is enforced before each one
runs: an allowed call executes normally, while a denied call throws a
PolicyViolationError and the tool body never runs. Pick your framework below —
each tab is the governance-wiring excerpt from that framework's runnable
example, vendored
into this repo and kept in lock-step with this page by a CI drift check — the check
catches this page drifting from the vendored snippet, not the vendored snippet
drifting from the upstream example. Copy the full, runnable script — imports,
tools, and the agent run — from the linked example; the slice below is just the
part that wires in governance.
LangChain.js is the validated path; the remaining frameworks are experimental.
The excerpts are ESM / TypeScript; under CommonJS, swap the import for
const { withAssembly } = require("@agent-assembly/sdk").
- LangChain.js
- Custom (no framework)
- OpenAI (Node) (Experimental)
- Vercel AI SDK (Experimental)
- LangGraph.js (Experimental)
- Mastra (Experimental)
import { withAssembly } from "@agent-assembly/sdk";
import { createPolicyGatewayClient } from "./policy.js";
import { TOOLS } from "./tools.js";
const tools = withAssembly(
{
get_weather: {
execute: async (args: Record<string, unknown>) => TOOLS.get_weather(args).output
},
delete_file: {
execute: async (args: Record<string, unknown>) => TOOLS.delete_file(args).output
}
},
{ gatewayClient: createPolicyGatewayClient(), agentId: "langchain-js-example-agent" }
);
Base tool abstractions like Tool moved out of the langchain monolith into @langchain/core when LangChain split the package in v0.1.0 (Jan 2024; @langchain/core first published to npm 2023-11-22).
langchain< 0.1.0:import { Tool } from "langchain/tools";langchain/@langchain/core≥ 0.1.0 (current):import { tool } from "@langchain/core/tools";
import { withAssembly } from "@agent-assembly/sdk";
import { createPolicyGatewayClient } from "./policy.js";
import { readFile, writeFile } from "./tools.js";
const tools = withAssembly(
{
read_file: {
execute: async (args: Record<string, unknown>) =>
readFile(typeof args.path === "string" ? args.path : "").output
},
write_file: {
execute: async (args: Record<string, unknown>) =>
writeFile(
typeof args.path === "string" ? args.path : "",
typeof args.content === "string" ? args.content : ""
).output
}
},
{ gatewayClient: createPolicyGatewayClient(), agentId: "custom-tool-policy-agent" }
);
import { withAssembly } from "@agent-assembly/sdk";
import { createPolicyGatewayClient } from "./policy.js";
import { searchWeb, sendEmail } from "./tools.js";
const tools = withAssembly(
{
search_web: {
execute: async (args: Record<string, unknown>) =>
searchWeb(typeof args.query === "string" ? args.query : "").output
},
send_email: {
execute: async (args: Record<string, unknown>) =>
sendEmail(
typeof args.to === "string" ? args.to : "",
typeof args.subject === "string" ? args.subject : ""
).output
}
},
{ gatewayClient: createPolicyGatewayClient(), agentId: "openai-node-example-agent" }
);
import { withAssembly } from "@agent-assembly/sdk";
import { createPolicyGatewayClient } from "./policy.js";
import { getWeatherTool, sendEmailTool } from "./tools.js";
// withAssembly wraps each tool's `execute`, keying the policy by the map key.
// The Vercel AI SDK tools run unchanged; only governance is layered on top.
const tools = withAssembly(
{
get_weather: getWeatherTool,
send_email: sendEmailTool,
},
{ gatewayClient: createPolicyGatewayClient(), agentId: "vercel-ai-example-agent" }
);
The React UI hooks were extracted out of the core ai package into a dedicated package in AI SDK 5.0, which removed the deprecated ai/react export. The tool() factory used above is unaffected — it still imports from "ai" unchanged through the 7.x line this example pins.
- AI SDK 4.x:
import { useChat } from "ai/react"; - AI SDK ≥ 5.0 (current):
import { useChat } from "@ai-sdk/react";
import { withAssembly } from "@agent-assembly/sdk";
import { createPolicyGatewayClient } from "./policy.js";
import { TOOLS } from "./tools.js";
/**
* Build governed tools once, then call them from inside graph nodes.
* withAssembly enforces the local policy before each tool runs.
*/
function buildGovernedTools() {
return withAssembly(
{
search_docs: {
execute: async (args: Record<string, unknown>) => TOOLS.search_docs(args).output,
},
execute_shell: {
execute: async (args: Record<string, unknown>) => TOOLS.execute_shell(args).output,
},
},
{ gatewayClient: createPolicyGatewayClient(), agentId: "langgraph-js-example-agent" }
);
}
import { withAssembly } from "@agent-assembly/sdk";
import { createPolicyGatewayClient } from "./policy.js";
import { getStockPriceTool, placeTradeTool } from "./tools.js";
// Wrap the Mastra tools with withAssembly. Each governed entry delegates to the
// real Mastra tool's execute, so the policy is enforced before the tool runs.
const tools = withAssembly(
{
get_stock_price: {
execute: async (args: Record<string, unknown>) => runMastraTool(getStockPriceTool, args),
},
place_trade: {
execute: async (args: Record<string, unknown>) => runMastraTool(placeTradeTool, args),
},
},
{ gatewayClient: createPolicyGatewayClient(), agentId: "mastra-example-agent" }
);
Mastra v1 moved every export except Mastra itself off the @mastra/core root entry point onto subpaths (see the npx @mastra/codemod@latest v1/mastra-core-imports codemod). This example's @mastra/core pin (^1.50.1) already uses the new layout.
@mastra/core0.x:import { Agent, Workflow, createTool } from "@mastra/core";@mastra/core≥ 1.0 (current):import { createTool } from "@mastra/core/tools";(similarlyAgentfrom@mastra/core/agent,Workflowfrom@mastra/core/workflows)
4. What to expect
- Allow. The tool runs normally and returns its result. A governance event is recorded in the audit trail.
- Deny. The wrapped
invoke()rejects with aPolicyViolationErrorwhose message includes the tool name and the gateway's reason — the tool body never runs. - Pending (needs approval). The call waits up to
langchain.approvalTimeoutMs(default applies if unset) for a human decision, then either proceeds or rejects.
That deny-on-policy behavior is the whole point: a denied tool call throws instead of executing. If you want to watch what would be blocked without actually blocking it while you tune policy, register the agent in observe mode:
const ctx = await initAssembly({
agentId: "demo",
enforcementMode: "observe", // dry-run: actions proceed, violations recorded as shadow events
langchain: { tools: { searchWeb } }
});
Next steps
- Guides — the full LangChain walkthrough, the low-level
withAssemblywrapper, experimental frameworks, and how to handle allow/deny decisions and errors. - Configuration — every
AssemblyConfigfield and the gateway/API-key resolution precedence. - Core Concepts — what the native binding, the
adapter registry, and the
initAssemblylifecycle actually do.