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

clauditorium

v1.4.1

Published

REST API wrapper for the Claude CLI

Downloads

28

Readme

Claude API Server

A REST API wrapper for the Claude CLI.


TL;DR - Get Running in 30 Seconds

Prerequisites: Node.js 18+ and Claude CLI authenticated (claude login)

npx clauditorium

Or install globally:

npm install -g clauditorium
clauditorium

Test it:

curl -X POST http://localhost:5051/ask \
  -H "Content-Type: application/json" \
  -d '{"prompt": "Hello!"}'

That's it. Server runs on http://localhost:5051.


Documentation Site

Tutorial-first docs are published on GitHub Pages:

  • https://erikmaday.github.io/clauditorium/

The site includes:

  • Quickstart and hands-on tutorials
  • Endpoint and config reference pages
  • Embedded OpenAPI explorer sourced from openapi.yaml

Features

  • Simple REST API for Claude
  • Per-request model selection via model parameter
  • Multi-turn chat with server-managed conversation_id context
  • Configurable via environment variables
  • Request tracking with unique IDs
  • Structured JSON logs for operational observability
  • Prometheus /metrics endpoint for runtime and HTTP telemetry
  • Environment and request validation with consistent error responses
  • Lean runtime dependencies (Express, cors, pino, prom-client)

Architecture

The server is organized into focused modules:

  • src/config - Environment parsing and runtime version loading
  • src/core - Logging and error helpers
  • src/clients - Claude CLI process wrapper
  • src/services - Prompt formatting helpers
  • src/routes - Endpoint handlers and request validation
  • src/middleware - Request ID, error handling, not-found handling
  • src/app.ts - Express app composition
  • src/server.ts - Startup and banner
  • src/index.ts - CLI entrypoint

Installation

# Run directly with npx (no install needed)
npx clauditorium

# Or install globally
npm install -g clauditorium

# Or install locally in a project
npm install clauditorium

Development

npm run lint
npm run test
npm run test:coverage
npm run build
npm run docs:check

Coverage thresholds are enforced in CI via npm run test:coverage.

Run docs locally:

npm run docs:dev
npm run docs:build
npm run docs:preview

Compatibility

  • Node.js: >=18 (CI-tested on Node 22)
  • Docker: tested via GitHub Actions Ubuntu runner with Docker Engine
  • Claude auth for containerized usage: set CLAUDE_CODE_OAUTH_TOKEN at runtime

Use In Another Dockerized App

Install clauditorium in your own Node service image and run it with a Claude token:

FROM node:22-bookworm-slim

WORKDIR /app

RUN apt-get update \
  && apt-get install -y --no-install-recommends ca-certificates binutils \
  && rm -rf /var/lib/apt/lists/* \
  && npm install -g @anthropic-ai/claude-code

COPY package*.json ./
RUN npm ci --omit=dev

CMD ["npx", "clauditorium"]

Runtime example:

docker run --rm -p 5051:5051 \
  -e CLAUDE_CODE_OAUTH_TOKEN="<token>" \
  your-image-name

Quick smoke check (container already running on :5051):

curl -s http://localhost:5051/health
curl -s http://localhost:5051/models
curl -s -X POST http://localhost:5051/ask \
  -H "Content-Type: application/json" \
  -d '{"prompt":"Reply with only: smoke-ok"}'

One-command smoke test script (expects image exposing port 5051):

./scripts/smoke-container.sh your-image-name

API Contract Governance

OpenAPI contract correctness is CI-gated with strict enforcement:

  • npm run openapi:types:check verifies generated TS types from openapi.yaml are up to date
  • npm run openapi:lint validates spec quality and structure
  • npm run openapi:validate performs full OpenAPI schema validation
  • npm run contract:test verifies runtime routes and response payloads against openapi.yaml

Run all contract checks locally:

npm run api:contract:check

Any endpoint, request/response shape, status code, or auth requirement change must include matching updates to openapi.yaml and contract tests in the same PR.

OpenAPI is the source of truth. Regenerate TypeScript contract types after spec changes with:

npm run openapi:types:generate

Docs OpenAPI embed is synced from root openapi.yaml:

npm run docs:sync-openapi
npm run docs:sync-openapi:check

API Endpoints

| Endpoint | Method | Description | |----------|--------|-------------| | /ask | POST | Send a prompt, get a response | | /chat | POST | Chat with persistent conversation_id context | | /chat/:conversation_id | GET | Get conversation lifecycle metadata | | /chat/:conversation_id | DELETE | Delete conversation from in-memory store | | /models | GET | List available Claude models discovered from local CLI binary | | /health | GET | Health check | | /health/history | GET | Recent Claude CLI readiness check history | | /health/recheck | POST | Re-run Claude CLI readiness check | | /metrics | GET | Prometheus metrics endpoint (operational, not in OpenAPI spec) | | /version | GET | Version info | | /openapi.yaml | GET | Raw OpenAPI specification | | /docs | GET | Interactive API documentation UI |

/version reports the package version from package.json at runtime.

OpenAPI spec: openapi.yaml Interactive docs: http://localhost:5051/docs

POST /ask

curl -X POST http://localhost:5051/ask \
  -H "Content-Type: application/json" \
  -d '{"prompt": "What is the capital of France?"}'

With a specific model:

curl -X POST http://localhost:5051/ask \
  -H "Content-Type: application/json" \
  -d '{"prompt": "What is the capital of France?", "model": "claude-haiku-4-5-20251001"}'

POST /chat

Start a new conversation (response returns conversation_id):

curl -X POST http://localhost:5051/chat \
  -H "Content-Type: application/json" \
  -d '{
    "message": "Hello! What is 2+2?",
    "system": "You are a helpful assistant.",
    "model": "claude-sonnet-4-5-20250929"
  }'

Continue the same chat without resending full history:

curl -X POST http://localhost:5051/chat \
  -H "Content-Type: application/json" \
  -d '{
    "conversation_id": "YOUR_CONVERSATION_ID",
    "message": "Can you summarize that?"
  }'

If conversation_id is unknown/expired, /chat returns 400 validation_error. /chat responses include lifecycle and context metadata (conversation, context) on every successful call.

DELETE /chat/:conversation_id

curl -X DELETE http://localhost:5051/chat/YOUR_CONVERSATION_ID

GET /chat/:conversation_id

curl http://localhost:5051/chat/YOUR_CONVERSATION_ID

GET /models

curl http://localhost:5051/models

Example response:

{
  "count": 2,
  "models": [
    "claude-haiku-4-5-20251001",
    "claude-sonnet-4-5-20250929"
  ]
}

GET /health

curl http://localhost:5051/health

GET /health/history

curl http://localhost:5051/health/history
curl "http://localhost:5051/health/history?since=2026-01-01T00:00:00.000Z"

POST /health/recheck

curl -X POST http://localhost:5051/health/recheck \
  -H "x-api-key: your-api-key"

GET /version

curl http://localhost:5051/version

Documentation endpoints

curl http://localhost:5051/openapi.yaml

Open in browser: http://localhost:5051/docs

GET /metrics

curl http://localhost:5051/metrics

The model parameter is optional for both endpoints. When omitted, the CLI default model is used. /chat responses include conversation_id, which you can reuse for continuation calls. Using the same conversation_id intentionally carries prior message context; omit it (or delete it first) to start fresh.

If CLAUDE_API_KEY is set, requests to /ask and /chat must include:

-H "x-api-key: your-api-key"

/health now includes readiness details for Claude CLI. With CLAUDE_API_STRICT_HEALTH=true, /health returns 503 when Claude CLI is not ready. /health observability also includes Claude runtime queue metrics (active_requests, queued_requests, rejected_total, queue_timeouts_total).

GET /health/history returns an in-memory rolling window of recent readiness checks for diagnostics. Use GET /health/history?since=2026-01-01T00:00:00.000Z to filter entries by timestamp.

POST /health/recheck triggers a new Claude CLI readiness check without restarting the server. This endpoint requires x-api-key and returns 503 if CLAUDE_API_KEY is not configured.

Error Taxonomy

| Error Code | Typical HTTP Status | Retryable? | Client Action | |------------|---------------------|------------|---------------| | validation_error | 400 | No | Fix request body or env value | | unauthorized | 401 | No | Provide correct x-api-key | | not_found | 404 | No | Correct endpoint path/method | | payload_too_large | 413 | No | Reduce request payload size | | rate_limited | 429 | Yes (after delay) | Wait for window to reset; retry after retry_after_seconds | | concurrency_limited | 429 | Yes | Claude worker queue is full; retry shortly | | queue_timeout | 504 | Yes | Queue wait exceeded configured timeout; retry | | timeout | 504 | Yes | Retry with simpler prompt or higher timeout | | cli_error | 500 | Sometimes | Check Claude CLI stderr/auth/session | | spawn_error | 500 | Sometimes | Verify Claude CLI install and PATH | | models_unavailable | 500 | Sometimes | Ensure Claude CLI is installed and locally discoverable | | unknown_error | 500 | Yes | Retry; inspect logs if persistent | | internal_error | 500 | Yes | Retry; inspect server logs | | api_key_not_configured | 503 | No | Set CLAUDE_API_KEY on server |

Sample error response:

{
  "error": "validation_error",
  "message": "prompt is required",
  "request_id": "a1b2c3d4"
}

Configuration

Set these environment variables to customize behavior:

| Variable | Default | Description | |----------|---------|-------------| | CLAUDE_API_HOST | 127.0.0.1 | Server host | | CLAUDE_API_PORT | 5051 | Server port | | CLAUDE_API_TIMEOUT | 120 | Request timeout (seconds) | | CLAUDE_API_STARTUP_CHECK_TIMEOUT | 5 | Startup timeout for Claude CLI readiness check (seconds) | | CLAUDE_API_MAX_CONCURRENT | 4 | Max concurrent Claude subprocesses | | CLAUDE_API_MAX_QUEUE | 100 | Max queued Claude requests waiting for a slot | | CLAUDE_API_QUEUE_TIMEOUT_MS | 15000 | Max queue wait time before request fails | | CLAUDE_API_DRAIN_TIMEOUT_SECONDS | 30 | Graceful shutdown drain timeout for in-flight Claude jobs (seconds) | | CLAUDE_API_RATE_LIMIT_WINDOW_SECONDS | 60 | Per-IP rate-limit window for /ask and /chat (seconds) | | CLAUDE_API_RATE_LIMIT_MAX_REQUESTS | 0 | Max requests per IP per window (0 disables rate limiting) | | CLAUDE_API_HEALTH_HISTORY_LIMIT | 25 | Max readiness entries retained in memory | | CLAUDE_API_CONVERSATION_TTL_SECONDS | 86400 | Conversation inactivity TTL (seconds) | | CLAUDE_API_MAX_CONVERSATIONS | 1000 | Max stored conversations in memory before oldest inactive eviction | | CLAUDE_API_CONTEXT_WARN_TOKENS | 12000 | Soft warning threshold for estimated conversation token usage | | CLAUDE_API_CONTEXT_TARGET_TOKENS | 18000 | Soft target threshold for estimated conversation token usage | | CLAUDE_API_CONTEXT_COMPACT_KEEP_MESSAGES | 6 | Keep this many recent messages verbatim during auto-compaction | | CLAUDE_API_CONTEXT_SUMMARY_MAX_CHARS | 2000 | Max characters used for generated rolling summary during compaction | | CLAUDE_API_ISOLATE_CWD | true | Run Claude in an isolated empty working directory to avoid project-context leakage | | CLAUDE_API_CLAUDE_CWD | system temp dir | Optional override path for isolated Claude working directory | | CLAUDE_API_BODY_LIMIT | 1mb | Max JSON request body size | | CLAUDE_API_CORS | false | Enable CORS | | CLAUDE_API_LOG_LEVEL | INFO | Log level | | CLAUDE_API_STRICT_HEALTH | false | Return 503 from /health when Claude CLI is not ready | | CLAUDE_API_KEY | (unset) | Optional API key for /ask and /chat |

Example:

CLAUDE_API_PORT=8080 clauditorium

Troubleshooting

"Claude CLI not found"

# Verify Claude CLI is installed
which claude
claude --version

"Authentication required"

claude login

Request timeouts

CLAUDE_API_TIMEOUT=300 clauditorium

CORS errors in browser

CLAUDE_API_CORS=true clauditorium

License

MIT - see LICENSE