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

@renkoya1/claude-app-server

v0.5.2

Published

JSON-RPC server that exposes Claude Agent SDK to any client (codex app-server compatible wire protocol).

Readme

claude-app-server

JSON-RPC server that exposes the official Claude Agent SDK to any client over a wire protocol modeled on codex app-server.

┌─────────────────┐    JSON-RPC 2.0   ┌──────────────────────┐    @anthropic-ai/   ┌──────────────────┐
│ your client     │  (stdio JSONL)    │ claude-app-server    │  claude-agent-sdk   │  Anthropic API   │
│ (VS Code, web,  │ ◀───────────────▶ │ (Rust frontend +     │ ──────────────────▶ │  api.anthropic   │
│  desktop, CLI)  │                   │  TypeScript sidecar) │                     │  .com            │
└─────────────────┘                   └──────────────────────┘                     └──────────────────┘

Why two languages? Rust owns the protocol surface, transport, thread state and multi-client fan-out. The TypeScript sidecar owns the agent loop (tool use, MCP, hooks, skills, subagents, sessions) by hosting @anthropic-ai/claude-agent-sdk, which is the official Claude SDK implementation. The split keeps the protocol layer fast and statically typed while letting the SDK update itself with Claude Code releases.

This is not the official claude CLI or Claude Code product. It is a standalone server that lets you build your own client on top of the Claude agent. Authentication piggybacks on claude login.

Install (users)

npx @renkoya1/claude-app-server          # one-off run
npm i -g @renkoya1/claude-app-server     # install globally

That's the whole install. npm install runs a postinstall step that downloads the Rust binary for your platform from this package's GitHub Release. Supported platforms: darwin-arm64, darwin-x64, linux-x64, linux-arm64.

The package ships:

  • bin/launcher.mjs — Node wrapper that exec's the Rust binary.
  • dist/sidecar/ — compiled TypeScript sidecar bundling @anthropic-ai/claude-agent-sdk.
  • dist/bin/<platform>/claude-app-server — Rust binary for your platform.

Authentication

This server uses @anthropic-ai/claude-agent-sdk, which is the same engine that powers the claude CLI / Claude Code app. If you have already run claude login, you do not need any environment variables.

Credential resolution order (same as the official claude CLI):

  1. ANTHROPIC_API_KEY env var, if set, takes precedence.
  2. OAuth tokens written by claude login:
    • macOS: Keychain entry Claude Code-credentials.
    • Linux / Windows: ~/.claude/.credentials.json.
  3. AWS Bedrock / Google Vertex credentials, if configured for the SDK.

So the two usual setups are:

# You already use Claude Code — nothing to do.
claude login                                  # one-time
npx @renkoya1/claude-app-server

# Or you want raw API key access (no Claude subscription):
ANTHROPIC_API_KEY=sk-ant-... npx @renkoya1/claude-app-server

Building a custom agent

You have two parallel ways to configure an agent, and you can mix them freely. Both produce a fully working Claude Agent SDK session behind the JSON-RPC surface.

Way 1 — Filesystem conventions (no code, file-based)

The Claude Agent SDK auto-loads everything from the standard Claude Code directory layout. Drop files in the right place and they just work — no JSON-RPC config required.

| Feature | Location | Notes | |-------------------|-----------------------------------------------------------------------------------|------------------------------------------------| | Rules | ~/.claude/CLAUDE.md (user) and <cwd>/CLAUDE.md (project) | Injected into the system prompt. | | Skills | ~/.claude/skills/<name>/SKILL.md, <cwd>/.claude/skills/<name>/SKILL.md | The model invokes them via the Skill tool. | | Subagents (file) | ~/.claude/agents/<name>.md, <cwd>/.claude/agents/<name>.md | Reachable from the Task tool. | | Slash commands | ~/.claude/commands/<name>.md, <cwd>/.claude/commands/<name>.md | User invokes as /<name>. | | Hooks | ~/.claude/settings.json hooks field | PreToolUse / PostToolUse / etc. | | Memories | ~/.claude/memories/*.md | Loaded into context as agent recall. | | MCP servers | ~/.claude/mcp.json | Auto-connected. | | Settings | ~/.claude/settings.json, <cwd>/.claude/settings.json, .claude/settings.local.json | All SDK settings keys. |

You can ship an agent as a self-contained bundle:

my-agent-bundle/
├── CLAUDE.md                              # rules
└── .claude/
    ├── settings.json                       # SDK settings
    ├── skills/
    │   └── sql-expert/SKILL.md
    ├── agents/
    │   └── sql-reviewer.md
    ├── commands/
    │   └── analyze-query.md                # /analyze-query
    └── mcp.json

Then tell the server to use it as the working directory:

{ "method": "thread/start", "params": { "cwd": "/path/to/my-agent-bundle" } }

The SDK reads CLAUDE.md, the skills, the file subagents, the slash commands, and the MCP servers automatically. No protocol options needed.

Way 2 — Programmatic config via JSON-RPC

thread/start and turn/start are open-ended: any field the official @anthropic-ai/claude-agent-sdk Options type accepts is forwarded verbatim into query({ options }). New SDK options work without a protocol bump.

Common knobs:

| params key | Effect | |---------------------------|-------------------------------------------------------------------------| | model | Pick the model (claude-opus-4-7, claude-sonnet-4-6, claude-haiku-4-5-20251001, ...). | | systemPrompt | Replace the default Claude Code system prompt with your own. | | cwd | Working directory the agent operates from. | | additionalDirectories | Extra directories the agent may read/write beyond cwd. | | permissionMode | default / acceptEdits / bypassPermissions / plan / dontAsk / auto. | | allowedTools | Restrict the agent to a tool whitelist (e.g. ["Read", "Grep"]). | | disallowedTools | Tool blacklist (e.g. ["Bash"] to forbid shell exec). | | maxTurns | Cap the autonomous tool-use loop length. | | env | Env vars passed into the agent's child processes. | | skills | "all" or an array of skill names to enable. | | mcpServers | Per-thread MCP server registration (DB connectors, internal tools, ...).| | agents | Programmatic subagents reachable via the Task tool. | | settingSources | Subset of user / project / local to read settings from. | | bridgeCanUseTool | true → tool calls emit item/*/requestApproval; client replies with permission/respond (see Bridged callbacks). | | bridgeHooks | "all" or array of hook events → server emits hook/started/hook/completed. |

Example — a SQL-only agent with a DB-backed MCP server:

{
  "method": "thread/start",
  "id": 1,
  "params": {
    "model": "claude-sonnet-4-6",
    "systemPrompt": "You are a SQL expert. Use the `db` MCP to query.",
    "permissionMode": "plan",
    "allowedTools": ["Read", "Grep"],
    "skills": ["sql-expert"],
    "maxTurns": 8,
    "mcpServers": {
      "db": {
        "type": "stdio",
        "command": "node",
        "args": ["mcp-postgres.js"],
        "env": { "DATABASE_URL": "postgres://localhost/app" }
      }
    },
    "agents": {
      "performance-reviewer": {
        "description": "Reviews SQL plans for performance issues.",
        "prompt": "You audit query plans. Use EXPLAIN ANALYZE.",
        "tools": ["Read"]
      }
    }
  }
}

Way 3 — Hybrid (file defaults, JSON-RPC overrides)

Filesystem conventions provide the baseline; JSON-RPC params override or extend per session. This is the most common production setup — ship a .claude/ skill/agent bundle with your UI, and let the user flip permissionMode, model, systemPrompt from the UI.

{
  "method": "thread/start",
  "params": {
    "cwd": "/path/to/my-agent-bundle",     // file conventions load here
    "systemPrompt": "User-selected persona", // override CLAUDE.md preset
    "permissionMode": "plan",                // UI choice
    "skills": ["sql-expert", "git-helper"]   // narrow file skills
  }
}

Bridged callbacks (canUseTool, hooks)

JavaScript callbacks cannot cross a JSON boundary, but the sidecar bridges them as request/response events:

// Opt-in once at thread/start:
{ "method": "thread/start", "params": { ..., "bridgeCanUseTool": true, "bridgeHooks": "all" } }

// Then server emits e.g.:
// {"method":"item/commandExecution/requestApproval","params":{"requestId":"...","tool":"Bash","input":{...}}}
// Client must reply:
{ "method": "permission/respond", "id": 99, "params": { "requestId": "...", "result": { "behavior": "allow" } } }

Same pattern for hooks: server emits hook/started / hook/completed with the hook payload; client optionally replies via hook/respond.

Runtime control (mid-session steering)

Once a session is running, mutate it without recreating:

| Method | Effect | |-----------------------------------|---------------------------------------------------------| | thread/model/set | Switch model mid-session. | | thread/maxThinkingTokens/set | Adjust extended-thinking budget. | | agent/define | Add a new programmatic subagent (recreates session). | | agent/list / agent/remove | Inspect / remove current subagents. | | mcpServer/set | Reconfigure MCP servers (recreates session). | | turn/steer | Append user input to the active turn without new turn. | | turn/interrupt | Abort the active turn. |

Feature parity with Claude Code

Everything the official Claude Code CLI supports works here, with one exception:

| SDK feature | Status | |------------------------------------------------|-----------------------------------------------------------| | File-based: rules, skills, subagents, commands, hooks, memories, MCP, settings | ✅ Auto-loaded by the SDK | | Programmatic: agents, mcpServers (stdio/sse/http), systemPrompt, etc. | ✅ Pass via thread/start params | | canUseTool callback | ✅ Via bridgeCanUseTool: true | | hooks callbacks | ✅ Via bridgeHooks: "all" | | Runtime control (model, permission mode, MCP) | ✅ Via thread/*/set endpoints | | createSdkMcpServer() + tool() (in-process JS tool) | ⚠️ Fork the sidecar (see below) |

For in-process JS tools, ship a custom sidecar:

# your-custom-sidecar.mjs imports claude-agent-sdk + adds tools via tool()
CLAUDE_APP_SERVER_SIDECAR=/path/to/your-custom-sidecar.mjs \
  npx @renkoya1/claude-app-server

The CLAUDE_APP_SERVER_SIDECAR env var lets you swap the default sidecar with your own — every other piece of the server (transport, thread store, JSON-RPC dispatch) stays intact.

Notes

  • The frontend does not type-check unknown keys; the SDK rejects malformed shapes and the server emits a turn/completed with isError: true and the SDK error in errors[].
  • The full SDK Options reference: https://github.com/anthropics/claude-agent-sdk-typescript

Build from source (contributors)

You only need this section if you cloned the repo to develop the server itself. End users should use npx above.

git clone https://github.com/RenKoya1/claude-app-server.git
cd claude-app-server
npm install                    # installs launcher + sidecar runtime deps
npm run build                  # cargo build --release + tsc + stage dist/
node bin/launcher.mjs          # smoke test

Run

claude-app-server speaks newline-delimited JSON-RPC 2.0 on stdin/stdout. Drive it from your client:

{
  printf '%s\n' '{"id":1,"method":"initialize","params":{"clientInfo":{"name":"demo","version":"0.1.0"}}}'
  printf '%s\n' '{"method":"initialized","params":{}}'
  printf '%s\n' '{"id":2,"method":"thread/start","params":{"model":"claude-haiku-4-5-20251001"}}'
  # remember thr_xxx from id:2 result
  printf '%s\n' '{"id":3,"method":"turn/start","params":{"threadId":"thr_xxx","input":[{"type":"text","text":"hello"}]}}'
} | npx claude-app-server

You will see item/agentMessage/delta notifications stream the response, followed by a final turn/completed with token usage.

Environment variables (all optional)

| Var | Meaning | |--------------------------------|-------------------------------------------------------------------------| | ANTHROPIC_MODEL | default model when thread/start omits one. | | ANTHROPIC_API_KEY | raw API key. Only needed if you don't use claude login for auth. | | CLAUDE_HOME | reported as codexHome in initialize (default ~/.claude). | | CLAUDE_APP_SERVER_SIDECAR | override the sidecar script path. Auto-set by the launcher. | | CLAUDE_APP_SERVER_SKIP_DOWNLOAD=1 | skip the postinstall binary download. | | CLAUDE_APP_SERVER_RELEASE_URL | override the binary download template used by postinstall. | | RUST_LOG | tracing filter, e.g. info,claude_app_server=debug. | | LOG_FORMAT=json | emit JSON tracing lines on stderr. |

Protocol overview

The wire protocol mirrors codex app-server so a client already written against Codex can talk to this server with minimal changes:

  • Newline-delimited JSON. jsonrpc field omitted.
  • Required initialize handshake per connection. Subsequent requests before initialized are rejected with code -32002.
  • Repeated initialize calls return -32003 Already initialized.
  • When the ingress queue saturates, requests are rejected with code -32001 Server overloaded; retry later. — retry with backoff.

Supported methods

Lifecycle

| Method | Notes | |---------------|----------------------------------------------------------------| | initialize | Handshake; must precede every other request on a connection. | | initialized | Notification acking the handshake. |

Threads

| Method | Notes | |------------------------|------------------------------------------------------------------------| | thread/start | Create a thread. Accepts model, cwd, ephemeral, systemPrompt. | | thread/resume | Reopen a thread by id; supports excludeTurns. | | thread/fork | Branch a thread; ephemeral supported. | | thread/list | In-memory list, sorted newest first. | | thread/loaded/list | Ids of currently loaded threads. | | thread/read | Returns the stored thread; includeTurns hydrates history. | | thread/archive | Move thread out of the active set; emits thread/archived. | | thread/unarchive | Restore an archived thread; emits thread/unarchived. | | thread/unsubscribe | Stop receiving events for a thread; emits thread/closed. | | thread/name/set | Rename a thread; emits thread/name/updated. | | thread/inject_items | Push raw SDK user messages into a session's input stream. | | thread/compact/start | Request manual context compaction. | | thread/goal/set | Create/update the persisted goal; emits thread/goal/updated. | | thread/goal/get | Read the current goal (null if none). | | thread/goal/clear | Remove the goal; emits thread/goal/cleared if state changed. |

Turns

| Method | Notes | |------------------|-------------------------------------------------------------------------| | turn/start | Stream a turn through the Claude Agent SDK. Tools/MCP/skills/hooks work.| | turn/interrupt | Abort the in-flight turn via the sidecar's AbortController. | | turn/steer | Append additional user input to the active turn. |

Models / config

| Method | Notes | |---------------|----------------------------------------------------------------------| | model/list | Reports Opus 4.7, Sonnet 4.6, Haiku 4.5. | | config/read | Effective model, claude home, platform, current cwd. |

Skills / hooks / MCP

| Method | Notes | |----------------------------|-------------------------------------------------------------| | skills/list | Discover SKILL.md files under ~/.claude/skills/. | | hooks/list | Placeholder until the SDK exposes a hook discovery API. | | mcpServerStatus/list | Placeholder until the SDK exposes MCP status API. | | mcpServer/tool/call | Currently returns -32601; route through turn/start. |

Filesystem

| Method | Notes | |-----------------------|----------------------------------------------------------------------| | fs/readFile | Return base64-encoded bytes for an absolute file path. | | fs/writeFile | Write base64-decoded bytes to an absolute path; parents auto-created.| | fs/createDirectory | recursive defaults to true. | | fs/getMetadata | isDirectory / isFile / isSymlink / createdAtMs / modifiedAtMs / sizeBytes. | | fs/readDirectory | Sorted child entries with isDirectory / isFile. | | fs/remove | recursive / force default to true. | | fs/copy | Files copy directly; directories require recursive: true. | | fs/watch | Cross-platform notify-backed watcher with watchId-keyed routing. | | fs/unwatch | Cancel a registered watcher. |

Command exec

| Method | Notes | |---------------------------|-------------------------------------------------------------------------| | command/exec | Spawn argv, capture stdout/stderr/exit. Optional streaming via processId + streamStdoutStderr. | | command/exec/write | Write base64 stdin to a running streaming process (or close stdin). | | command/exec/terminate | Kill a running streaming process. |

command/exec is unsandboxed. Codex enforces a sandbox; we leave that to the host environment. Use only with trusted local UIs.

Notifications emitted

  • thread/started, thread/archived, thread/unarchived, thread/name/updated, thread/goal/updated, thread/goal/cleared, thread/closed.
  • thread/status/changed on idle/active transitions.
  • turn/started, turn/completed (with tokenUsage).
  • item/started, item/completed, item/agentMessage/delta.
  • fs/changed (per watchId).
  • command/exec/outputDelta (when streamStdoutStderr: true).

Item kinds streamed

  • userMessage — recorded on turn/start.
  • agentMessage — the assistant's text reply.
  • toolUse — the assistant invoked a tool (Bash, Read, Edit, MCP tool, …).
  • toolResult — the result of a tool invocation; isError flags failures.
  • reasoning — extended thinking output, when enabled.

Gaps vs Codex app-server

These exist in codex-rs/app-server but are out of scope for now. A client touching them gets -32601 Method not supported.

| Area | Status | |-----------------------------|-----------------------------------------------------------------| | websocket / unix-socket | Only --listen stdio:// is wired up. | | Persistence | Threads + rollouts are RAM-only and lost on restart. | | process/spawn / PTY exec | Use command/exec. PTY mode + Windows sandbox not implemented. | | mcpServer* rich queries | Stub responses; SDK does not currently expose MCP status APIs. | | marketplace/*, plugin/* | No plugin/marketplace concept. | | review/start | No automated reviewer pipeline. | | thread/realtime/* | No realtime / WebRTC bridge. | | feedback/upload, windowsSandbox/*, experimentalFeature/*, ... | Not implemented. | | turn/steer | Not implemented; AbortController-based interrupt is. | | thread/compact/start, thread/shellCommand, thread/inject_items | Not implemented. | | Schema export | No generate-ts / generate-json-schema subcommands yet. |

Repository layout

claude/
├── package.json           # npm package manifest
├── bin/launcher.mjs       # `claude-app-server` entry point
├── scripts/
│   ├── build-local.sh     # cargo + tsc + stage dist/
│   └── postinstall.mjs    # download platform binary on `npm install`
├── dist/
│   ├── sidecar/           # compiled TS sidecar (shipped)
│   └── bin/<triple>/      # Rust binary (shipped)
├── crates/                # Rust source (not shipped; use cargo)
│   ├── claude-app-server/
│   ├── claude-app-server-protocol/
│   └── claude-app-server-transport/
├── sidecar/               # TS source (not shipped)
│   ├── package.json
│   ├── tsconfig.json
│   └── src/index.ts
└── README.md

Building a custom client

The simplest client is just JSON-RPC over a process pipe:

import { spawn } from "node:child_process";

const server = spawn("npx", ["claude-app-server"], {
  stdio: ["pipe", "pipe", "inherit"],
});

let nextId = 1;
function send(method: string, params?: unknown) {
  const id = nextId++;
  server.stdin.write(JSON.stringify({ id, method, params }) + "\n");
  return id;
}

server.stdout.on("data", (chunk) => {
  for (const line of chunk.toString().split("\n")) {
    if (!line.trim()) continue;
    const msg = JSON.parse(line);
    if (msg.method === "item/agentMessage/delta") {
      process.stdout.write(msg.params.delta);
    } else if (msg.method === "turn/completed") {
      console.log("\n[done]", msg.params.tokenUsage);
    }
  }
});

send("initialize", { clientInfo: { name: "demo", version: "0.0.1" } });
server.stdin.write(JSON.stringify({ method: "initialized", params: {} }) + "\n");
const threadId = "<read from id:1 response>";
send("turn/start", { threadId, input: [{ type: "text", text: "hello" }] });

(Real clients should track request ids, route responses, and respect the initialize handshake before sending other requests.)

Caveats

  • The Rust → TypeScript IPC is internal and may change between versions. Speak to the JSON-RPC surface, not the sidecar.
  • claude login itself is not implemented here. Run the official claude CLI once to populate credentials.
  • The bundled binary is downloaded on npm install. Sandboxed CI environments without outbound HTTP need CLAUDE_APP_SERVER_RELEASE_URL pointing at a mirror or npm run build from a source checkout.

Contributing

We welcome PRs. Please read CONTRIBUTING.md before opening one.

Releases are restricted to maintainers. Only repository maintainers with push access to RenKoya1/claude-app-server (and the NPM_TOKEN secret on this GitHub repo) can publish to npm. Contributors should:

  • Fork the repo on GitHub.
  • Push a feature branch to your fork.
  • Open a PR against RenKoya1/claude-app-server:main.

A maintainer will review, merge, and cut the next release (see RELEASING.md for the procedure they follow).