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

@logitropic/claude-app-server

v0.1.2

Published

Codex-style Rust app-server for the local Claude Code CLI.

Readme

claude-app-server

claude-app-server is a Codex-style Rust app-server for the local Claude Code CLI.

It keeps the project structure and runtime flow close to Codex app-server, while the agent behavior follows the TypeScript Claude app-server reference: each turn launches claude --print --output-format stream-json, parses Claude Code stream events, and forwards JSON-RPC notifications to the client.

Status

This is a standalone v1 implementation.

Implemented:

  • Codex-style Rust workspace layout.
  • stdio://, ws://IP:PORT, unix://, unix://PATH, and off transports.
  • Codex-style WebSocket bearer authentication.
  • TypeScript-compatible thread and turn behavior.
  • In-memory thread state.
  • Claude Code stream-json event mapping.

Not implemented yet:

  • Persistent thread storage.
  • MCP/app/plugin marketplace systems.
  • Codex config manager, device key, analytics, remote control enrollment.
  • Codex-specific command execution APIs.

Requirements

  • Rust stable with Cargo.
  • Claude Code CLI installed and authenticated.

Check Claude Code:

claude --version

If needed, log in with Claude Code before running the server.

Workspace Layout

.
├── app-server
│   └── src
│       ├── main.rs
│       ├── lib.rs
│       ├── message_processor.rs
│       ├── claude_runner.rs
│       ├── outgoing_message.rs
│       ├── thread_state.rs
│       └── request_processors/
├── app-server-protocol
│   └── src
│       ├── jsonrpc_lite.rs
│       └── protocol/
└── app-server-transport
    └── src
        ├── outgoing_message.rs
        └── transport/

Crates:

  • claude-app-server-protocol: JSON-RPC envelopes, request/response structs, notifications, thread/turn/item types.
  • claude-app-server-transport: transport parsing, stdio, WebSocket, unix socket, connection events, outbound queue, WebSocket auth.
  • claude-app-server: binary/runtime, message processor, request dispatch, in-memory state, Claude subprocess runner.

Build

cargo build -p claude-app-server

The binary is written to:

target/debug/claude-app-server

Running

The CLI follows Codex app-server’s --listen style.

stdio

cargo run -p claude-app-server -- --listen stdio://

stdio:// is the default:

cargo run -p claude-app-server

WebSocket

cargo run -p claude-app-server -- --listen ws://127.0.0.1:3284

The WebSocket transport is plain ws://. There is no QR code, pair key, or default TLS.

When binding to a non-loopback address without auth, the server logs a warning:

cargo run -p claude-app-server -- --listen ws://0.0.0.0:3284

Unix Socket

Use the default app-server control socket path:

cargo run -p claude-app-server -- --listen unix://

Use an explicit socket path:

cargo run -p claude-app-server -- --listen unix:///tmp/claude-app-server.sock

Off

Parse config and exit without starting a listener:

cargo run -p claude-app-server -- --listen off

Claude Path

By default, the server resolves claude from PATH. You can pass an explicit binary:

cargo run -p claude-app-server -- \
  --claude-path /absolute/path/to/claude

WebSocket Auth

WebSocket auth uses Authorization: Bearer <token>, matching Codex-style transport auth.

Capability Token From File

printf 'super-secret-token\n' > /tmp/claude-app-server-token

cargo run -p claude-app-server -- \
  --listen ws://127.0.0.1:3284 \
  --ws-auth capability-token \
  --ws-token-file /tmp/claude-app-server-token

Clients must send:

Authorization: Bearer super-secret-token

Capability Token SHA-256

TOKEN='super-secret-token'
HASH="$(printf '%s' "$TOKEN" | shasum -a 256 | awk '{print $1}')"

cargo run -p claude-app-server -- \
  --listen ws://127.0.0.1:3284 \
  --ws-auth capability-token \
  --ws-token-sha256 "$HASH"

Signed Bearer Token

printf 'at-least-32-bytes-shared-secret-here\n' > /tmp/claude-app-server-jwt-secret

cargo run -p claude-app-server -- \
  --listen ws://127.0.0.1:3284 \
  --ws-auth signed-bearer-token \
  --ws-shared-secret-file /tmp/claude-app-server-jwt-secret

Optional JWT validation flags:

  • --ws-issuer <issuer>
  • --ws-audience <audience>
  • --ws-max-clock-skew-seconds <seconds>

Protocol

Messages are JSON-RPC 2.0 objects. stdio and unix socket messages are newline-delimited JSON. WebSocket messages are JSON text frames.

Initialize

Request:

{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"client":{"name":"demo","version":"1.0.0"},"cwd":"/tmp"}}

Response:

{"jsonrpc":"2.0","id":1,"result":{"server":{"name":"claude-app-server","version":"0.1.1"},"capabilities":{"threads":["start","resume","fork"],"turns":["start","steer","interrupt"],"models":["claude-opus-4-6","claude-sonnet-4-6","claude-haiku-4-5"],"skills":["Read","Write","Edit","Bash","Glob","Grep","WebFetch","WebSearch","Task"]}}}

Notification:

{"jsonrpc":"2.0","method":"initialized","params":{"server":"claude-app-server"}}

Methods

Implemented methods:

| Method | Description | | --- | --- | | initialize | Initializes a client connection. | | thread/start | Creates an in-memory thread. | | thread/resume | Reads an in-memory thread created in this process. | | thread/fork | Forks an existing Claude session after at least one completed turn. | | turn/start | Starts a Claude Code turn. | | turn/steer | Queues steer content for the active turn. | | turn/interrupt | Cancels the active Claude process. | | approval/respond | Updates the thread permission mode for later turns. | | model/list | Lists Claude model ids advertised by the server. | | skills/list | Lists Claude Code built-in tools. | | app/list | Returns an empty app list. |

Both snake_case and camelCase params are accepted where the TypeScript reference accepts them, for example thread_id and threadId, permission_mode and permissionMode.

Turns

turn/start accepts either plain content:

{"jsonrpc":"2.0","id":3,"method":"turn/start","params":{"thread_id":"THREAD_ID","content":"Say hello"}}

or Codex-style text input:

{"jsonrpc":"2.0","id":3,"method":"turn/start","params":{"threadId":"THREAD_ID","input":[{"type":"text","text":"Say hello"}]}}

The server returns immediately:

{"jsonrpc":"2.0","id":3,"result":{"turn":{"id":"TURN_ID"}}}

Then it streams notifications until the turn completes or fails.

Notifications

During a turn, the server may emit legacy compatibility notifications and richer Codex-style notifications.

Legacy notifications:

| Notification | Description | | --- | --- | | turn/started | The turn has started. Includes an optional turn snapshot. | | item/progress | Streaming text or thinking delta. | | item/created | Finalized text, thinking, tool call, or tool result item. | | usage/update | Token usage update from Claude stream events. | | turn/permission_denied | Claude reported permission denials. | | turn/completed | The turn completed or was interrupted. Includes an optional turn snapshot. | | turn/failed | The turn failed before completion. |

Rich notifications:

| Notification | Description | | --- | --- | | item/started | A rich agent message, reasoning item, command execution, or tool call started. | | item/completed | A rich item completed or failed. | | item/agentMessage/delta | Streaming assistant text for a rich agent_message item. | | item/reasoning/summaryTextDelta | Streaming thinking text for a rich reasoning item. | | item/commandExecution/outputDelta | Bash command output text from the matching tool result. | | turn/plan/updated | Plan update derived from Claude TodoWrite tool calls. | | hook/started | Claude CLI hook lifecycle event when emitted by --include-hook-events. | | hook/completed | Claude CLI hook completion event when emitted by --include-hook-events. |

Claude Invocation

Each turn spawns Claude Code with:

claude --print --output-format stream-json --verbose --include-partial-messages \
  --include-hook-events \
  --permission-mode <mode> \
  --session-id <thread_id>

Later turns use:

--resume <claude_session_id>

Forked first turns use:

--resume <source_session_id> --fork-session

Supported permission modes:

  • default
  • acceptEdits
  • bypassPermissions
  • dontAsk

Smoke Test

Basic stdio handshake:

printf '%s\n' \
  '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"client":{"name":"smoke","version":"1"}}}' \
  '{"jsonrpc":"2.0","id":2,"method":"model/list"}' \
  | cargo run -q -p claude-app-server -- --listen stdio://

Real Claude turn:

node <<'EOF'
const { spawn } = require('child_process');
const readline = require('readline');

const child = spawn('cargo', ['run', '-q', '-p', 'claude-app-server', '--', '--listen', 'stdio://'], {
  stdio: ['pipe', 'pipe', 'inherit']
});
const rl = readline.createInterface({ input: child.stdout });
let id = 0;
let threadId;

function send(method, params) {
  child.stdin.write(JSON.stringify({ jsonrpc: '2.0', id: ++id, method, params }) + '\n');
}

rl.on('line', line => {
  const msg = JSON.parse(line);
  console.log(msg);
  if (msg.id === 1) send('thread/start', { cwd: '/tmp', permission_mode: 'dontAsk' });
  if (msg.id === 2) {
    threadId = msg.result.thread.id;
    send('turn/start', {
      thread_id: threadId,
      content: 'Respond with exactly CLAUDE_APP_SERVER_SMOKE_OK and nothing else.'
    });
  }
  if (msg.method === 'turn/completed' || msg.method === 'turn/failed') {
    child.kill('SIGTERM');
  }
});

send('initialize', { client: { name: 'smoke', version: '1' } });
EOF

Development

Run checks:

cargo fmt --all --check
cargo clippy --workspace --all-targets -- -D warnings
cargo test --workspace

Build:

cargo build -p claude-app-server

npm Distribution

The npm package follows Codex-style native distribution:

  • @logitropic/claude-app-server is a lightweight Node launcher.
  • Platform binaries are optional dependency aliases published as platform-tagged versions of the same npm package.
  • Native payloads live under vendor/<target-triple>/claude-app-server/claude-app-server(.exe).

Stage packages from a populated vendor tree:

python3 scripts/stage_npm_packages.py \
  --release-version 0.1.1 \
  --package claude-app-server \
  --vendor-src dist/native/vendor \
  --output-dir dist/npm

For local testing on the current machine:

cargo build --release -p claude-app-server
TARGET_TRIPLE=aarch64-apple-darwin
PLATFORM_PACKAGE=claude-app-server-darwin-arm64
mkdir -p "dist/native/vendor/${TARGET_TRIPLE}/claude-app-server"
cp target/release/claude-app-server \
  "dist/native/vendor/${TARGET_TRIPLE}/claude-app-server/claude-app-server"
python3 scripts/build_npm_package.py \
  --package "${PLATFORM_PACKAGE}" \
  --release-version 0.1.1 \
  --vendor-src dist/native/vendor \
  --pack-output "dist/npm/${PLATFORM_PACKAGE}-0.1.1.tgz"

Compatibility Notes

This Rust implementation was smoke-tested against the TypeScript reference for:

  • handshake and discovery methods,
  • thread lifecycle methods,
  • fork-before-turn error behavior,
  • real Claude turn streaming,
  • notification sequence,
  • text item output.

Known intentional differences:

  • Server name is claude-app-server instead of symphony-claude.
  • CLI uses Codex-style --listen; there is no TypeScript-style start subcommand.
  • WebSocket auth uses bearer tokens; there is no QR code or ?key= pair key.