A powerful AI Agent framework for building intelligent assistants with streaming support, tool execution, skills, hooks, and MCP integration.
- Session-Based API: Multi-turn conversations with state management
- Persistent Sessions: File-based storage for session persistence across restarts
- Streaming Support: Real-time streaming of LLM responses with event-based notifications
- Built-in Tools: File operations, bash execution, web fetch, and task management
- Tool System: Flexible tool registration with Zod schema support
- Skills System: Extend agent capabilities with discoverable skills
- Hooks System: Intercept and control agent behavior (PreToolUse, PostToolUse)
- Structured Outputs: JSON Schema validated responses
- MCP Integration: Model Context Protocol server support
- Multi-Provider: Support for Anthropic Claude and OpenAI models
- Cost Tracking: Token usage and cost estimation
- Type-Safe: Full TypeScript support with strict typing
- Zero Config: Auto-reads API keys from environment variables
npm install formagent-sdk
# or
bun add formagent-sdk
# or
yarn add formagent-sdkimport { prompt } from "formagent-sdk"
const response = await prompt("What is 2+2?")
console.log(response) // "4"import { createSession, builtinTools } from "formagent-sdk"
const session = await createSession({
model: "claude-sonnet-4-20250514",
tools: builtinTools,
})
await session.send("List all TypeScript files in the current directory")
for await (const event of session.receive()) {
if (event.type === "text") {
process.stdout.write(event.text)
} else if (event.type === "tool_use") {
console.log(`Using tool: ${event.name}`)
}
}
await session.close()The SDK automatically reads configuration from environment variables:
| Variable | Description |
|---|---|
ANTHROPIC_API_KEY |
Anthropic API key (required for Claude models) |
ANTHROPIC_BASE_URL |
Custom Anthropic API endpoint |
OPENAI_API_KEY |
OpenAI API key (for GPT models) |
OPENAI_BASE_URL |
Custom OpenAI API endpoint |
export ANTHROPIC_API_KEY=your-api-keyimport { tool } from "formagent-sdk"
import { z } from "zod"
const weatherTool = tool({
name: "get_weather",
description: "Get the current weather for a location",
schema: z.object({
location: z.string().describe("City name"),
unit: z.enum(["celsius", "fahrenheit"]).optional(),
}),
execute: async ({ location, unit = "celsius" }) => {
return `Weather in ${location}: 22°${unit === "celsius" ? "C" : "F"}`
},
})
const session = await createSession({
model: "claude-sonnet-4-20250514",
tools: [weatherTool],
})| Tool | Description |
|---|---|
| Bash | Execute bash commands with timeout support |
| Read | Read file contents with optional line range |
| Write | Write content to files, creates directories |
| Edit | Find/replace text in files |
| Glob | Find files matching glob patterns |
| Grep | Search file contents with regex |
| WebFetch | Fetch URL content, converts HTML to markdown |
| TodoWrite | Manage task lists for progress tracking |
| Skill | Discover and invoke specialized skills |
Security defaults (important):
- File tools (
Read/Write/Edit/Glob/Grep) are restricted toprocess.cwd()by default; configureallowedPathsto widen access. WebFetchblocks localhost/private network targets by default; setallowPrivateNetwork: trueto override.Bashblocks a small set of high-risk command patterns by default; setallowDangerous: trueto disable the denylist.
import { builtinTools, fileTools, createBuiltinTools } from "formagent-sdk"
// Use all built-in tools
const session = await createSession({
tools: builtinTools,
})
// Or use just file tools (Read, Write, Edit, Glob, Grep)
const session = await createSession({
tools: fileTools,
})
// Or configure access boundaries explicitly
const tools = createBuiltinTools({
cwd: process.cwd(),
allowedPaths: [process.cwd()],
allowPrivateNetwork: false,
allowDangerous: false,
})Load skills from directories to extend agent capabilities:
import { createSession, DEFAULT_USER_SKILLS_PATH } from "formagent-sdk"
const session = await createSession({
model: "claude-sonnet-4-20250514",
settingSources: [
DEFAULT_USER_SKILLS_PATH, // ~/.claude/skills
"/path/to/project/skills",
],
})
// Claude can now use the Skill tool to discover and invoke skills
await session.send("What skills are available?")Intercept and control tool execution:
import { createSession, builtinTools, type HookCallback } from "formagent-sdk"
const protectEnvFiles: HookCallback = async (input, toolUseId, context) => {
const filePath = input.tool_input?.file_path as string
if (filePath?.endsWith(".env")) {
return {
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: "Cannot modify .env files",
},
}
}
return {}
}
const session = await createSession({
model: "claude-sonnet-4-20250514",
tools: builtinTools,
hooks: {
PreToolUse: [
{ matcher: "Write|Edit", hooks: [protectEnvFiles] },
],
},
})Get validated JSON responses:
import { createSession } from "formagent-sdk"
const session = await createSession({
model: "claude-sonnet-4-20250514",
outputFormat: {
type: "json_schema",
schema: {
type: "object",
properties: {
summary: { type: "string" },
score: { type: "number" },
},
required: ["summary"],
},
},
})
for await (const event of session.receive()) {
if (event.type === "result" && event.structured_output) {
console.log(event.structured_output)
}
}Enable session persistence with FileSessionStorage:
import { createSession, FileSessionStorage, builtinTools } from "formagent-sdk"
// Create persistent storage
const storage = new FileSessionStorage("./sessions")
// Create session with persistence
const session = await createSession({
model: "claude-sonnet-4-20250514",
tools: builtinTools,
sessionStorage: storage,
})
console.log(`Session ID: ${session.id}`) // Save this for later
await session.close()Resume a previous session with full conversation context:
import { createSession, FileSessionStorage } from "formagent-sdk"
const storage = new FileSessionStorage("./sessions")
// Resume from saved session ID
const session = await createSession({
sessionStorage: storage,
resume: "previous-session-id",
})
await session.send("Continue where we left off")Set a default storage for all sessions:
import { setDefaultStorage, FileSessionStorage, createSession } from "formagent-sdk"
// Set once at startup
setDefaultStorage(new FileSessionStorage("./sessions"))
// All sessions now persist automatically
const session = await createSession({ model: "claude-sonnet-4-20250514" })Create a branch from an existing session:
import { forkSession } from "formagent-sdk"
// Fork creates a new session with copied conversation history
const forked = await forkSession("original-session-id")The receive() method yields different event types:
| Type | Properties | Description |
|---|---|---|
text |
text: string |
Text content chunk |
tool_use |
id, name, input |
Tool invocation |
tool_result |
tool_use_id, content, is_error |
Tool execution result |
message |
message: SDKMessage |
Complete message |
result |
structured_output |
Structured output (when configured) |
stop |
stop_reason, usage |
Generation complete |
error |
error: Error |
Error occurred |
See the examples directory for complete examples:
- Basic sessions and prompts
- Streaming responses
- Custom tools and MCP servers
- Skills and hooks
- Structured outputs
- CLI agent implementation
MIT