npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@mulmuri/claude-agent-sdk

v0.1.0

Published

Claude Agent SDK-compatible client that reaches Claude Code through Codex CLI (interactive `claude "..."`) or the Claude Code MCP server — never through `claude -p`.

Readme

@mulmuri/claude-agent-sdk

A Claude Agent SDK-compatible client that never calls claude -p. Instead of driving the Claude Code CLI directly, it reaches Claude through one of two user-selectable transports:

| Backend | Chain | How it works | | --- | --- | --- | | "interactive" | SDK → Codex CLI → claude "..." | Spawns codex exec, which is instructed to run the Claude Code CLI interactively (claude "...", stdin closed — the session answers once and exits) and relay the reply verbatim. | | "mcp" (default) | SDK → claude mcp serve (MCP) | Spawns Claude Code as an MCP server and drives it as an MCP client. Primary path: the server's Agent tool. Current Claude Code builds expose an empty agent registry in serve mode, so the SDK automatically falls back to the server's Workflow tool (whose agent() primitive does work), reads the run's result from its state file, and tails the agent transcript to stream real assistant messages. |

The public interface mirrors query() from @anthropic-ai/claude-agent-sdk: an async generator of SDK-shaped messages (system:init → assistant/user → result) with an interrupt() method.

Requirements

  • Node.js ≥ 18
  • Claude Code CLI installed and authenticated (both backends)
  • Codex CLI installed and authenticated ("interactive" backend only)

No API keys are read by this package — authentication is whatever the CLIs already have.

Install

npm install @mulmuri/claude-agent-sdk

Usage

import { query } from "@mulmuri/claude-agent-sdk";

for await (const message of query({
  prompt: "Summarize this repository",
  options: {
    backend: "mcp", // or "interactive" — the user's choice
    model: "sonnet",
    cwd: "/path/to/project",
  },
})) {
  switch (message.type) {
    case "assistant":
      for (const block of message.message.content) {
        if (block.type === "text") console.log(block.text);
      }
      break;
    case "result":
      if (message.is_error) console.error("failed:", message.result);
      else console.log("final:", message.result);
      break;
  }
}

The backend can also be selected without code changes via the CLAUDE_AGENT_BACKEND environment variable (interactive or mcp); options.backend wins when both are set.

Interrupting a running query:

const q = query({ prompt: "long task...", options: { backend: "interactive" } });
setTimeout(() => q.interrupt(), 10_000);
for await (const message of q) { /* ... */ }

Options

| Option | Backends | Description | | --- | --- | --- | | backend | — | "interactive" | "mcp". Default: CLAUDE_AGENT_BACKEND env var, then "mcp". | | model | both | Forwarded to claude --model (interactive) or mapped to the Agent tool's sonnet/opus/haiku/fable short names (mcp). | | cwd | both | Working directory for the spawned CLI processes. | | systemPrompt | both | Interactive: passed via claude --append-system-prompt. MCP: prepended to the prompt inside <system-instructions> tags (best effort — the Agent tool has no system-prompt parameter). | | permissionMode | both | "default" | "acceptEdits" | "bypassPermissions" | "plan". Forwarded to the Agent tool's mode on MCP. | | abortController | both | Aborting kills the underlying CLI processes. | | timeoutMs | both | Overall wall-clock timeout for the query. | | stderr | both | Callback receiving stderr from the spawned processes. | | pathToClaudeExecutable | both | Default claude on PATH. | | pathToCodexExecutable | interactive | Default codex on PATH. | | codexSandbox | interactive | Codex sandbox policy. Default danger-full-access — Codex must be able to spawn claude, which needs network and credential access, so stricter sandboxes typically break the relay. | | allowedTools, disallowedTools, maxTurns | — | Accepted for interface compatibility, not enforced (see below). |

What you get in the message stream

  • system (subtype init) — session id, cwd, tool names (the MCP server's real tool list, or ["Bash"] for the Codex relay), model, permission mode.
  • assistant — on the interactive backend these stream live from Codex's JSONL events: text messages, plus tool_use blocks for every shell command Codex runs (you can watch it invoke claude "..."). On the MCP backend a single assistant message carries Claude's reply.
  • user — tool_result blocks paired with the relay's tool_use blocks (interactive backend only).
  • result — subtype: "success" with the final reply in result, or subtype: "error_during_execution" with diagnostic output.

Compatibility notes (intentional gaps)

The Claude Agent SDK and the Claude Code CLI surfaces are not 1:1, so some SDK features cannot be honored over these transports:

  • total_cost_usd is always 0 and usage reflects the relay's tokens on the interactive backend (Codex's usage), zeros on MCP — Claude's own token counts are not observable without --print-mode JSON output, which this package deliberately does not use.
  • allowedTools / disallowedTools / maxTurns are accepted but not enforced; neither transport exposes those controls.
  • Single-shot prompts only — prompt is a string. Multi-turn streaming input (AsyncIterable<SDKUserMessage>) and session resumption are not supported; each query() is a fresh session.
  • Hooks, custom MCP servers, canUseTool callbacks — not supported.
  • The interactive backend's reply fidelity depends on the Codex relay; the SDK reads Claude's reply from a file Claude itself wrote (not from Codex's paraphrase) whenever possible.
  • Model selection on the MCP backend is best effort. The SDK forwards it through ANTHROPIC_MODEL and the workflow's agent() options, but if the machine's ~/.claude/settings.json pins a model via env.ANTHROPIC_MODEL / env.CLAUDE_CODE_SUBAGENT_MODEL, those settings win inside Claude Code. On the interactive backend the flag is passed as claude --model, which wins.

How the interactive backend works (the fine print)

  1. The SDK writes your prompt (and optional system prompt) to temp files.

  2. It spawns codex exec --json --ephemeral with an instruction to run, exactly:

    claude [--model M] [--append-system-prompt "$(cat sys.txt)"] "$(cat prompt.txt)" </dev/null >out.txt 2>err.txt

    Note: no -p/--print. With a prompt argument and stdin closed, the interactive CLI answers once and exits on its own. If a TTY turns out to be required, Codex is instructed to retry under script -q /dev/null.

  3. Codex's JSONL events are mapped to SDK assistant/user messages in real time, so you can observe the relay working.

  4. When Codex exits, the SDK reads out.txt (Claude's verbatim reply, ANSI stripped) as the canonical result, falling back to Codex's final message.

Development

npm install
npm run build
node examples/mcp.mjs          # MCP backend smoke test
node examples/interactive.mjs  # Codex-relay backend smoke test

Testing

The test suite runs against the real CLIs and real models — no mocks:

npm test            # everything: build + type-level check + unit + E2E
npm run test:unit   # fast: queue/parser/command-builder unit tests
npm run test:e2e    # real `claude mcp serve` + real `codex exec` round trips
npm run test:types  # consumer-style code must type-check against dist types
SKIP_E2E=1 npm test # CI without CLI credentials: skips live-model tests

The E2E tests assert the full message protocol (init first, one terminal result, consistent session ids, unique uuids), the actual answer content, system-prompt steering, error results for missing executables, interrupt and abort semantics, timeouts — and, on the interactive backend, that the relay's observed shell command really invoked claude without -p/--print. Expect the E2E tier to take a few minutes and consume a small number of real model calls (haiku where possible).

Publishing

npm publish --access public

prepublishOnly rebuilds dist/; only dist/, README.md, and LICENSE are shipped.

License

MIT