
Security News
Re-Enabled GitHub Actions Expose Thousands of Repositories to Mini Shai-Hulud
Two compromised GitHub Actions were re-enabled with malicious tags intact, exposing thousands of downstream repositories to Mini Shai-Hulud.
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-ready, 5 lines to start.
Built on 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 environments:
// E2B cloud sandbox
const runner = new Runner({ sandbox: 'e2b' });
// Docker container
const runner = new Runner({ sandbox: 'docker' });
// Custom spawner
const runner = new Runner({
sandbox: (options) => myCustomSpawner(options),
});
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 (~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.
MIT
FAQs
The easiest way to build AI agents with Claude. MCP-native, sandbox-first, 5 lines to start.
The npm package claude-runner receives a total of 43 weekly downloads. As such, claude-runner popularity was classified as not popular.
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
Two compromised GitHub Actions were re-enabled with malicious tags intact, exposing thousands of downstream repositories to Mini Shai-Hulud.

Research
/Security News
A malicious Firefox extension fetches its payload after installation to evade detection, steal Google session cookies, and automate account takeover.

Research
/Security News
The compromise affects MemTensor's MemOS, an open source memory framework for large language models (LLMs) and AI agents. Both npm package @memtensor/memos-cloud-openclaw-plugin and the PyPI package MemoryOS are compromised. They drop cross-platform Go binaries that exfiltrate developer secrets.