The lightweight foundation for building agents (TypeScript implementation)
The lightweight framework for building agents in Typescript
README
<div align="center">
<a href="https://stirrup.artificialanalysis.ai">
</a>
<h1>The lightweight foundation for building agents (TypeScript implementation)</h1>
<br>
</div>
Stirrup is a lightweight framework, or starting point template, for building agents in TypeScript/JavaScript. It differs from other agent frameworks by:
- Working with the model, not against it: Stirrup gets out of the way and lets the model choose its own approach to completing tasks. Many frameworks impose rigid workflows that can degrade results.
- Best practices and tools built-in: We analyzed the leading agents (Claude Code, Codex, and others) to understand and incorporate best practices relating to topics like context management and foundational tools (e.g., code execution).
- Fully customizable: Use Stirrup as a package or as a starting template to build your own fully customized agents.
Note: This is the TypeScript implementation of the Python Stirrup framework.
Features
- ๐ Online search / web browsing: Search and fetch web pages
- ๐งช Code execution: Run code locally, in Docker, or in an E2B sandbox
- ๐ MCP client support: Connect to MCP servers and use their tools/resources
- ๐ Document input and output: Import files into context and produce file outputs
- ๐งฉ Skills system: Extend agents with modular, domain-specific instruction packages
- ๐ ๏ธ Flexible tool execution: A generic
Toolinterface allows easy tool definition and extension with Zod validation - ๐ค Human-in-the-loop: Includes a built-in user input tool that enables human feedback or clarification during agent execution
- ๐ง Context management: Automatically summarizes conversation history when approaching context limits, with deduplication to prevent summary accumulation
- ๐ Flexible provider support: Pre-built support for OpenAI-compatible APIs (Chat Completions + Responses API), Anthropic, and Vercel AI SDK
- ๐ผ๏ธ Multimodal support: Process images, video, and audio with automatic format conversion
- ๐ Speed metrics: Track output tokens per second (OTPS), generation time, and per-tool execution durations
- ๐พ Agent run cache: Cache and resume interrupted agent runs from where they left off
- โ Output file validation: Finish tool validates that reported output files actually exist before completing
- โ Type-safe: Built from the ground up with TypeScript
Installation
npm install @stirrup/stirrup
# or
pnpm add @stirrup/stirrup
# or
yarn add @stirrup/stirrup
Quick Start
import { Agent, DEFAULT_TOOLS, SIMPLE_FINISH_TOOL } from '@stirrup/stirrup';
import { ChatCompletionsClient } from '@stirrup/stirrup/clients/openai';
async function main() {
// Create client using ChatCompletionsClient
// Automatically uses OPENROUTER_API_KEY environment variable
const client = new ChatCompletionsClient({
baseURL: 'https://openrouter.ai/api/v1',
model: 'anthropic/claude-4.5-sonnet',
});
// As no tools are provided, the agent will use the default tools, which consist of:
// - Web tools (web search and web fetching, note web search requires BRAVE_API_KEY)
// - Local code execution tool (to execute shell commands)
const agent = new Agent({
client,
name: 'agent',
maxTurns: 15,
tools: DEFAULT_TOOLS,
finishTool: SIMPLE_FINISH_TOOL,
});
// Run with session context - handles tool lifecycle, logging and file outputs
// Structured logging is enabled by default
await using session = agent.session({ outputDir: './output/getting_started_example' });
const result = await session.run(
`What is the population of Australia over the last 3 years? Search the web to find out and create a
simple chart using python and matplotlib showing the current population per year.`
);
console.log("Result:", result.finishParams);
}
main().catch(console.error);
Note: This example uses OpenRouter. Set
OPENROUTER_API_KEYin your environment before running. Web search requires aBRAVE_API_KEY. The agent will still work without it, but web search will be unavailable.
Full Customization
For using Stirrup as a foundation for your own fully customized agent, you can clone and import Stirrup locally:
# Clone the repository
git clone https://github.com/ArtificialAnalysis/StirrupJS.git
cd StirrupJS
# Install dependencies
npm install
# Build
npm run build
How It Works
Agent- Configures and runs the agent loop until a finish tool is called or max turns reachedsession()- Context manager that sets up tools, manages files, handles logging, and ensures cleanupTool- Define tools with Zod parameters for full type safetyToolProvider- Manage tools that require lifecycle (connections, temp directories, etc.)DEFAULT_TOOLS- Standard tools included by default: code execution and web tools
Using Other LLM Providers
Stirrup supports multiple providers out of the box.
OpenAI-Compatible APIs
import { ChatCompletionsClient } from '@stirrup/stirrup/clients/openai';
// Create client using Deepseek's OpenAI-compatible endpoint
const client = new ChatCompletionsClient({
baseURL: 'https://api.deepseek.com',
model: 'deepseek-chat',
apiKey: process.env.DEEPSEEK_API_KEY,
});
const agent = new Agent({ client, name: 'deepseek_agent', ... });
Anthropic
import { AnthropicClient } from '@stirrup/stirrup/clients/anthropic';
const client = new AnthropicClient({
model: 'claude-sonnet-4-5',
apiKey: process.env.ANTHROPIC_API_KEY,
});
const agent = new Agent({ client, name: 'claude_agent', ... });
OpenAI Responses API
For models that support the newer Responses API (e.g., o3, o4-mini):
import { OpenResponsesClient } from '@stirrup/stirrup/clients/open-responses';
const client = new OpenResponsesClient({
model: 'o3-mini',
reasoningEffort: 'medium',
});
const agent = new Agent({ client, name: 'responses_agent', ... });
Vercel AI SDK
Stirrup integrates seamlessly with the Vercel AI SDK, giving you access to any provider supported by their ecosystem.
import { VercelAIClient } from '@stirrup/stirrup/clients/vercel-ai';
import { anthropic } from '@ai-sdk/anthropic';
const client = new VercelAIClient({
model: anthropic('claude-sonnet-4-5'),
});
const agent = new Agent({ client, name: 'vercel_agent', ... });
Default Tools
When you use DEFAULT_TOOLS, you get:
| Tool Provider | Tools Provided | Description |
|---|---|---|
LocalCodeExecToolProvider |
code_exec |
Execute shell commands in an isolated temp directory |
WebToolProvider |
web_fetch, web_search |
Fetch web pages and search (search requires BRAVE_API_KEY) |
Extending with Pre-Built Tools
import { Agent, DEFAULT_TOOLS, CALCULATOR_TOOL, SIMPLE_FINISH_TOOL } from '@stirrup/stirrup';
import { ChatCompletionsClient } from '@stirrup/stirrup/clients/openai';
// Create client
const client = new ChatCompletionsClient({ ... });
// Create agent with default tools + calculator tool
const agent = new Agent({
client,
name: 'web_calculator_agent',
tools: [...DEFAULT_TOOLS, CALCULATOR_TOOL],
finishTool: SIMPLE_FINISH_TOOL,
});
Defining Custom Tools
Stirrup uses Zod for type-safe tool definitions:
import { z } from 'zod';
import { Agent, Tool, ToolUseCountMetadata, DEFAULT_TOOLS } from '@stirrup/stirrup';
// Define parameters schema
const GreetParamsSchema = z.object({
name: z.string().describe('Name of the person to greet'),
formal: z.boolean().default(false).describe('Use formal greeting'),
});
// Create the tool
const GreetTool: Tool<typeof GreetParamsSchema, ToolUseCountMetadata> = {
name: 'greet',
description: 'Greet someone by name',
parameters: GreetParamsSchema,
executor: async (params) => {
const greeting = params.formal ? `Good day, ${params.name}.` : `Hey ${params.name}!`;
return {
content: greeting,
metadata: new ToolUseCountMetadata(1),
};
},
};
// Add to agent
const agent = new Agent({
client,
name: 'greeting_agent',
tools: [...DEFAULT_TOOLS, GreetTool],
...
});
Advanced Features
Structured Logging
Stirrup JS includes a powerful structured logging system powered by Pino. It's enabled by default when using agent.session():
// Defaults to pretty-printed debug logs
await using session = agent.session();
// Customize logging
await using session = agent.session({
loggerOptions: {
level: 'info', // 'trace' | 'debug' | 'info' | 'warn' | 'error'
pretty: false, // Set to false for JSON output (production)
}
});
// Disable default logger
await using session = agent.session({ noLogger: true });
Speed Metrics
Every agent run tracks performance metrics including output tokens per second (OTPS), generation time, and per-tool durations. These are available in the run result and displayed automatically by the structured logger:
const result = await session.run('Create a chart');
console.log(result.speedStats);
// {
// modelSlug: 'claude-sonnet-4-5',
// totalGenerationMs: 5200,
// totalOutputTokens: 1200,
// totalToolMs: 3100,
// generationCount: 4,
// toolBreakdown: { code_exec: 2800, web_fetch: 300 }
// }
Agent Run Cache
When an agent run is interrupted (max turns reached, errors), the conversation state is automatically cached to ~/.cache/stirrup/. Resume from where you left off:
// First run - gets interrupted at max turns
await using session = agent.session({ outputDir: './output' });
await session.run('Complex multi-step task');
// Resume from cache
await using session2 = agent.session({ outputDir: './output', resume: true });
await session2.run('Complex multi-step task'); // Picks up where it left off
Message Alternation
Some LLM providers require strict user/assistant message alternation. Enable automatic continuation prompts:
const agent = new Agent({
client,
name: 'strict-agent',
blockSuccessiveAssistantMessages: true, // Inject "Please continue" when needed
...
});
Shared Execution Environment
Sub-agents can share the parent's code execution sandbox to avoid file transfer overhead:
const subAgent = new Agent({
client,
name: 'worker',
shareParentExecEnv: true, // Reuse parent's sandbox
...
});
Event Monitoring
Monitor agent progress in real-time with typed events:
agent.on('turn:start', ({ turn, maxTurns }) => {
console.log(`Turn ${turn}/${maxTurns}`);
});
agent.on('tool:start', ({ name }) => {
console.log(`Executing ${name}...`);
});
Development
# Install
npm install
# Build
npm run build
# Run examples
npx tsx examples/getting-started.ts
# Test
npm test
# Type check
npm run typecheck
# Run documentation
uv run mkdocs serve
License
Licensed under the MIT LICENSE.
MongoDB - Build AI That Scales
