
Security News
arXiv Is Rate Limiting Authors Following a Flood of AI Slop Submissions
arXiv now limits authors to two submissions a month as AI slop overwhelms moderators, delays good papers, and sparks debate over applying the limit to everyone.
claude-runner
Advanced tools
The easiest way to build AI agents with Claude. MCP-native, sandbox-first, 5 lines to start.
The easiest way to build AI agents with Claude. MCP-native, sandbox-first, 5 lines to start.
The official SDK is an engine 🔧 — claude-runner is the car 🚗.
A thin, clean wrapper around the official Claude Agent SDK. One dependency. Zero bloat.
import { Runner } from 'claude-runner';
const runner = new Runner();
const result = await runner.run('Analyze this codebase and suggest improvements');
console.log(result.text);
The official @anthropic-ai/claude-agent-sdk is powerful but low-level — 40+ options, 20+ message types, raw async generators. Every developer builds their own wrapper.
claude-runner is that wrapper:
| raw Agent SDK | claude-runner | |
|---|---|---|
| Lines to start | 20+ | 5 |
| Message types | 20+ nested | 7 flat events |
| MCP config | Object only | Shorthand strings |
| Sandbox | Manual spawnClaudeCodeProcess | sandbox: 'e2b' |
| Session resume | resume: id option | runner.resume(id) |
| Permissions | canUseTool callback | permissions: 'auto' |
| Custom tools | tool() + createSdkMcpServer() | defineTool() |
npm install claude-runner
Requires Claude Code CLI to be installed and authenticated.
Run agents directly from your terminal:
# Simple prompt
npx claude-runner "Analyze this codebase and suggest improvements"
# Choose model
npx claude-runner -m opus "Refactor the auth module"
# With MCP servers
npx claude-runner --mcp github="npx @modelcontextprotocol/server-github" "List open issues"
# Auto-approve all tools (for CI/scripts)
npx claude-runner -p auto "Fix all failing tests"
# Resume a previous session
npx claude-runner --resume abc-123-uuid "Now deploy it"
# JSON output (no streaming)
npx claude-runner --json "What files are in this project?"
import { Runner } from 'claude-runner';
const runner = new Runner();
const result = await runner.run('Fix the failing tests in this project');
console.log(result.text); // Claude's response
console.log(`Cost: $${result.cost}`); // API cost
console.log(`Turns: ${result.turns}`); // Agentic turns
for await (const event of runner.stream('Refactor the auth module')) {
switch (event.type) {
case 'text':
process.stdout.write(event.text);
break;
case 'tool_start':
console.log(`\n[${event.tool}]`);
break;
case 'tool_end':
console.log(`[${event.tool}] done (${event.duration}ms)`);
break;
case 'done':
console.log(`\nCost: $${event.result.cost.toFixed(4)}`);
break;
}
}
// First run — Claude analyzes and asks for approval
const r1 = await runner.run('Create a test plan for the auth module');
console.log(r1.text); // "Here's the plan... approve?"
// Resume — continue the conversation with full context
const r2 = await runner.resume(r1.sessionId, 'Approved. Generate the tests.').result;
console.log(r2.text); // "Tests generated at..."
const stream = runner.stream('Build a REST API for user management');
// Inject guidance while Claude is working
setTimeout(() => stream.send('Use Express, not Fastify'), 5000);
for await (const event of stream) {
if (event.type === 'text') process.stdout.write(event.text);
}
Connect to any MCP server with shorthand strings or full config objects.
const runner = new Runner({
mcp: {
// Command string (auto-parsed)
github: 'npx @modelcontextprotocol/server-github',
// URL (HTTP/SSE server)
docs: 'https://api.example.com/mcp',
// Full config
postgres: {
command: 'npx',
args: ['@modelcontextprotocol/server-postgres', process.env.DATABASE_URL!],
env: { PGPASSWORD: process.env.PGPASSWORD! },
},
},
});
const result = await runner.run('How many users signed up last week?');
All MCP tools are auto-discovered and auto-allowed. Claude sees them and can use them immediately.
Define tools that run in your process:
import { Runner, defineTool } from 'claude-runner';
import { z } from 'zod';
const weather = defineTool(
'get_weather',
'Get current weather for a city',
{ city: z.string() },
async ({ city }) => ({
content: [{ type: 'text', text: `72°F and sunny in ${city}` }],
})
);
const runner = new Runner({ tools: [weather] });
const result = await runner.run('What is the weather in San Francisco?');
Control what Claude can do:
// Auto-approve everything (for CI, sandboxed environments)
const runner = new Runner({ permissions: 'auto' });
// Deny unknown tools (safe default)
const runner = new Runner({ permissions: 'deny-unknown' });
// Interactive approval
const runner = new Runner({
permissions: 'prompt',
onPermission: async ({ tool, description }) => {
return confirm(`Allow ${tool}? ${description}`);
},
});
// Fine-grained policy
const runner = new Runner({
permissions: {
allow: ['Read', 'Glob', 'Grep', 'mcp__github__*'],
deny: ['Bash(rm *)'],
prompt: ['Bash', 'Write'],
},
onPermission: async (req) => confirm(`Allow ${req.tool}?`),
});
Run agents in isolated Docker containers. Your project is bind-mounted at /workspace — all tool calls operate inside the container.
// Docker container — filesystem isolation, no extra deps
const runner = new Runner({
sandbox: 'docker',
docker: {
image: 'node:22-slim',
mount: ['/shared/fixtures'], // additional read-only mounts
network: 'host', // optional network mode
},
permissions: 'auto',
});
const result = await runner.run('Install deps and run the test suite');
// E2B cloud sandbox
const runner = new Runner({
sandbox: 'e2b',
e2b: { apiKey: process.env.E2B_API_KEY },
});
// Custom spawner — bring your own container runtime
const runner = new Runner({
sandbox: (options) => myCustomSpawner(options),
});
Check Docker availability:
import { getDockerStatus } from 'claude-runner';
const version = getDockerStatus(); // "29.1.3" or null
macOS note: Rancher Desktop only shares
$HOMEby default. Docker Desktop shares/Users,/Volumes,/private,/tmp. Ensure yourcwdis in a shared directory.
Define programmatic subagents:
const runner = new Runner({
agents: {
researcher: {
description: 'Research agent for gathering information',
prompt: 'You are a research assistant. Search thoroughly.',
tools: ['Read', 'Glob', 'Grep', 'WebSearch'],
model: 'haiku',
},
coder: {
description: 'Coding agent for implementation',
prompt: 'You are an expert programmer. Write clean code.',
tools: ['Read', 'Write', 'Edit', 'Bash'],
model: 'sonnet',
},
},
});
Runnerclass Runner {
constructor(options?: RunnerOptions);
run(prompt: string, overrides?: RunOverrides): Promise<RunResult>;
stream(prompt: string, overrides?: RunOverrides): RunStream;
resume(sessionId: string, prompt?: string): RunStream;
get lastSessionId(): string | null;
abort(): void;
}
RunResultinterface RunResult {
text: string; // Final response text
sessionId: string; // For resume
cost: number; // USD
duration: number; // ms
usage: { input; output }; // Token counts
turns: number; // Agentic turns
toolCalls: ToolCallSummary[];
error?: string;
}
RunEvent (7 types)| Type | Fields | When |
|---|---|---|
text | text | Each streamed text chunk |
tool_start | tool, id | Tool execution begins |
tool_end | tool, id, duration | Tool execution ends |
session_init | sessionId, model, tools | Session initialized |
mcp_status | server, status | MCP server connected/failed |
error | message, code? | Error occurred |
done | result | Run complete |
RunStreaminterface RunStream extends AsyncIterable<RunEvent> {
result: Promise<RunResult>; // Await final result
text: Promise<string>; // Await full text
send(message: string): void; // Inject mid-stream message
interrupt(): Promise<void>; // Pause execution
abort(): void; // Stop completely
sessionId: string | null; // Current session ID
}
RunnerOptions| Option | Type | Default | Description |
|---|---|---|---|
model | string | 'claude-sonnet-4-6' | Claude model (shorthands supported — see below) |
cwd | string | process.cwd() | Working directory |
systemPrompt | string | { preset: 'claude_code' } | minimal | System prompt |
mcp | Record<string, McpConfig | string> | {} | MCP servers |
tools | ToolDefinition[] | [] | Custom tools |
agents | Record<string, AgentDefinition> | — | Subagents |
sandbox | 'local' | 'e2b' | 'docker' | SpawnFn | 'local' | Execution environment |
permissions | 'auto' | 'prompt' | 'deny-unknown' | PermissionPolicy | 'deny-unknown' | Permission handling |
onPermission | (req) => Promise<boolean> | — | Permission callback |
maxTurns | number | — | Max agentic turns |
maxBudget | number | — | Max cost in USD |
effort | 'low' | 'medium' | 'high' | 'max' | — | Effort level |
sdkOptions | object | — | Pass-through to Agent SDK |
Use shorthand names or full model IDs:
// Shorthands
const runner = new Runner({ model: 'opus' }); // claude-opus-4-6
const runner = new Runner({ model: 'sonnet' }); // claude-sonnet-4-6
const runner = new Runner({ model: 'haiku' }); // claude-haiku-4-5
// Version-specific shorthands
const runner = new Runner({ model: 'opus-4.5' }); // claude-opus-4-5-20250918
const runner = new Runner({ model: 'sonnet-4.5' }); // claude-sonnet-4-5-20250514
const runner = new Runner({ model: 'opus-4.6' }); // claude-opus-4-6
const runner = new Runner({ model: 'sonnet-4.6' }); // claude-sonnet-4-6
// Full model IDs also work
const runner = new Runner({ model: 'claude-opus-4-6' });
// Per-run override
const result = await runner.run('Quick task', { model: 'haiku' });
| Shorthand | Full Model ID |
|---|---|
opus | claude-opus-4-6 |
opus-4.6 | claude-opus-4-6 |
opus-4.5 | claude-opus-4-5-20250918 |
sonnet | claude-sonnet-4-6 |
sonnet-4.6 | claude-sonnet-4-6 |
sonnet-4.5 | claude-sonnet-4-5-20250514 |
haiku | claude-haiku-4-5-20251001 |
haiku-4.5 | claude-haiku-4-5-20251001 |
claude-runner is runtime-agnostic. No framework lock-in.
claude-runner is a thin wrapper (~1,500 lines) around the official @anthropic-ai/claude-agent-sdk. It:
Options objectquery() session with MCP servers, permissions, and tools configuredSDKMessage types into 7 flat RunEvent typesYou get the full power of Claude Code (skills, agents, tools, MCP) through a simple API.
Ready-to-run examples in the examples/ directory:
| Example | Description | Run |
|---|---|---|
| Code Reviewer | Reviews your codebase for bugs and security issues | node examples/code-reviewer.js |
| Test Generator | Analyzes code and generates tests (multi-turn) | node examples/test-generator.js |
| GitHub Bot | Analyzes GitHub issues via MCP | GITHUB_TOKEN=xxx node examples/mcp-github-bot.js |
MIT
FAQs
The easiest way to build AI agents with Claude. MCP-native, sandbox-first, 5 lines to start.
We found that claude-runner demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Security News
arXiv now limits authors to two submissions a month as AI slop overwhelms moderators, delays good papers, and sparks debate over applying the limit to everyone.

Research
/Security News
A new GhostAction wave hits hundreds of GitHub repos, expanding CI/CD secret theft to cloud and AI credentials in source code and git history.

Research
/Security News
Tensorlake npm SDK version 0.5.144 was compromised in a ChainDrop / Shai-Hulud attack, delivering credential-stealing malware.