Skill: setup-claude-sub-proxy

## Description

13 stars

Best use case

Skill: setup-claude-sub-proxy is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

## Description

Teams using Skill: setup-claude-sub-proxy should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/setup-claude-sub-proxy/SKILL.md --create-dirs "https://raw.githubusercontent.com/Ring8688/claude-sub-proxy/main/.claude/skills/setup-claude-sub-proxy/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/setup-claude-sub-proxy/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How Skill: setup-claude-sub-proxy Compares

Feature / AgentSkill: setup-claude-sub-proxyStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

## Description

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# Skill: setup-claude-sub-proxy

## Description

Set up a local proxy server that uses the Claude Code Agent SDK to expose a standard Messages API endpoint — powered by your Claude.ai subscription (MAX/Pro), no API credits needed.

Core principle: The Agent SDK handles authentication automatically by reading Claude Code credentials (`~/.claude/.credentials.json`), then processes requests through the Claude Code runtime.

## Trigger

Trigger this skill when the user mentions scenarios such as:
- "Use a subscription token to call the API"
- "Don't want to use an API key, use claude.ai subscription instead"
- "Set up a claude sub proxy"
- "Use MAX subscription to call the API"
- "claude-sub-proxy"
- "Help me set up" / "帮我设置"

## Guided Setup Flow

When this skill is triggered, **ask the user which setup mode they need** before proceeding:

1. **Embedded** (`.claude/skills/setup-embedded/SKILL.md`) — Integrate into an existing project as an internal service. Best for: adding Claude API to your own app, localhost-only, minimal footprint.

2. **Standalone** (`.claude/skills/setup-standalone/SKILL.md`) — Deploy as an independent service with authentication. Best for: shared team proxy, Docker deployment, exposing to a network.

3. **Configure** (`.claude/skills/configure-proxy/SKILL.md`) — Customize advanced options on an existing installation. Best for: changing model, system prompt, enabling tools, tuning thinking tokens.

4. **Quick start** — Just `npm install && npm start` with defaults. Best for: trying it out locally in 30 seconds.

Ask the user something like: "How do you plan to use claude-sub-proxy?" and present these options. Then follow the corresponding skill guide.

## Architecture Overview

```
User App  →  Local Proxy (localhost:42069)  →  Agent SDK  →  Claude Code Runtime
                   ↕                                ↕
              HTTP Server                  Auto credential management
         (Messages API compatible)      (~/.claude/.credentials.json)
```

## Project Structure

```
claude-sub-proxy/
├── .claude/skills/
│   ├── setup-claude-sub-proxy/SKILL.md  # This file
│   ├── setup-embedded/SKILL.md          # Embedded integration guide
│   ├── setup-standalone/SKILL.md        # Standalone deployment guide
│   └── configure-proxy/SKILL.md         # Advanced configuration
├── package.json              # ESM project with @anthropic-ai/claude-agent-sdk
├── README.md / README_CN.md
├── Dockerfile
├── src/
│   ├── server.js             # HTTP server entry + route handling
│   ├── claude-executor.js    # Agent SDK query() wrapper (streaming & non-streaming)
│   ├── format-bridge.js      # Messages API ↔ SDK format conversion
│   ├── logger.js             # Logging utility
│   └── config.txt            # Configuration file
```

## Usage Steps

### 1. Install & Start

```bash
cd claude-sub-proxy
npm install   # Install Agent SDK dependency
npm start     # Start the server
```

### 2. Authentication

Authentication is automatic. Run `claude` CLI at least once to authenticate (creates `~/.claude/.credentials.json`).

### 3. Use the Proxy

Point any Anthropic SDK or compatible client to `http://localhost:42069`:

```python
import anthropic

client = anthropic.Anthropic(
    base_url="http://localhost:42069",
    api_key="not-needed"  # Can be any value
)

message = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Hello!"}]
)
```

## Key Technical Details

### Agent SDK Integration

The proxy uses `query()` from `@anthropic-ai/claude-agent-sdk` with these settings:
- `tools: []` — No built-in tools (pure API proxy, unless `tools_enabled=true`)
- `maxTurns: 1` — Single conversation turn
- `persistSession: false` — No session persistence
- `includePartialMessages: true` — For SSE streaming

### Authentication

Claude Code credentials (`~/.claude/.credentials.json`) — auto-managed by SDK. Token refresh is handled internally.

### Format Conversion

**Input (Messages API → SDK):**
- Single message: content becomes the `prompt` string
- Multi-turn: prior messages formatted as `<conversation_history>` XML, last message as prompt
- `body.system` → `options.systemPrompt`
- `body.model` → `options.model`
- `body.thinking.budget_tokens` → `options.maxThinkingTokens`

**Output (SDK → Messages API):**
- Non-streaming: `SDKAssistantMessage.message` (BetaMessage) returned as JSON
- Streaming: `SDKPartialAssistantMessage.event` (BetaRawMessageStreamEvent) forwarded as SSE

### Stream Event Types

The SDK yields `BetaRawMessageStreamEvent` — identical to the Anthropic API SSE format:
- `message_start` / `content_block_start` / `content_block_delta` / `content_block_stop` / `message_delta` / `message_stop`

## API Endpoints

| Method | Path | Description |
|--------|------|-------------|
| POST | `/v1/messages` | Messages API (streaming & non-streaming) |
| GET | `/auth/status` | Check authentication status |
| GET | `/health` | Health check |

## Configuration

Two methods — environment variables (`CSP_*` prefix) take priority over `src/config.txt`:

| config.txt key | Environment Variable | Default | Description |
|---------------|---------------------|---------|-------------|
| `port` | `CSP_PORT` | `42069` | Server listening port |
| `host` | `CSP_HOST` | Auto | `127.0.0.1` locally, `0.0.0.0` in Docker |
| `log_level` | `CSP_LOG_LEVEL` | `INFO` | `ERROR` / `WARN` / `INFO` / `DEBUG` / `TRACE` |
| `model_default` | `CSP_MODEL_DEFAULT` | `claude-sonnet-4-6` | Default model when not specified in request |
| `proxy_api_key` | `CSP_PROXY_API_KEY` | *(empty)* | Proxy authentication key (empty = open access) |
| `system_prompt` | `CSP_SYSTEM_PROMPT` | *(empty)* | Custom system prompt (request-level takes priority) |
| `tools_enabled` | `CSP_TOOLS_ENABLED` | `false` | Enable Claude Code built-in tools |
| `max_thinking_tokens` | `CSP_MAX_THINKING_TOKENS` | *(empty)* | Default thinking budget (request-level takes priority) |

## Notes

- For personal learning use only, please comply with Anthropic's Terms of Service
- The server listens on `127.0.0.1` (localhost only) by default; auto-switches to `0.0.0.0` in Docker
- Each request spawns a Claude Code process (~1-2s overhead)
- `temperature`, `top_p`, `top_k`, `max_tokens` are managed by the Claude Code runtime

---

## Module Specifications

### format-bridge.js

Handles conversion between Messages API format and Agent SDK parameters.

**Exports:** Named exports (ESM)

#### `extractTextContent(content) → string`

Extracts text from message content. Handles both string content and content block arrays (filters for `type: 'text'`).

#### `requestToSDKParams(body, config) → { prompt: string, options: object }`

Converts a Messages API request body to SDK `query()` parameters:
- Builds prompt from messages array (single or multi-turn with `<conversation_history>` XML)
- Maps `body.system` to `options.systemPrompt`
- Maps `body.model` to `options.model`
- Maps `body.thinking.budget_tokens` to `options.maxThinkingTokens`
- Applies config-level `system_prompt`, `tools_enabled`, `max_thinking_tokens`
- Sets `tools: []`, `maxTurns: 1`, `persistSession: false`

#### `sdkMessageToAPIResponse(sdkMsg) → object`

Extracts `BetaMessage` from `SDKAssistantMessage.message`. Returns a Messages API compatible response with `id`, `type`, `role`, `content`, `model`, `stop_reason`, `usage`.

#### `streamEventToSSE(sdkMsg) → string`

Converts `SDKPartialAssistantMessage` to an SSE line: `event: {type}\ndata: {json}\n\n`

---

### claude-executor.js

Wraps Agent SDK `query()` calls for HTTP request handling.

**Exports:** Named exports (ESM)

#### `executeNonStreaming(body, config) → Promise<object>`

1. Calls `requestToSDKParams(body, config)` to get prompt + options
2. Sets `includePartialMessages: false`
3. Iterates SDK generator, captures last `assistant` message
4. Returns `sdkMessageToAPIResponse(lastAssistantMessage)`
5. Throws on `assistant.error` or missing response

#### `executeStreaming(body, res, config) → Promise<void>`

1. Calls `requestToSDKParams(body, config)` to get prompt + options
2. Sets `includePartialMessages: true`
3. Writes SSE headers (`text/event-stream`)
4. Sets up abort on `res.on('close')`
5. Iterates SDK generator, writes `stream_event` as SSE lines
6. On error after headers sent: writes SSE error event
7. Calls `res.end()` in finally block

---

### server.js

HTTP server entry point with simplified routing.

**Export:** `{ startServer }` (ESM)

#### Route Table

| Route | Handler |
|-------|---------|
| `OPTIONS *` | CORS preflight response |
| `GET /health` | Returns `{ status: 'ok', server, timestamp }` |
| `GET /auth/status` | Checks credentials file, returns `{ authenticated, source, expires_at }` |
| `POST /v1/messages` | parseBody → authenticate → streaming/non-streaming executor |
| Other | 404 JSON |

#### `loadConfig() → object`

Loads `src/config.txt`, then applies `CSP_*` environment variable overrides.

#### `checkClaudeCredentials() → { authenticated: boolean, expires_at?: string }`

Reads `~/.claude/.credentials.json`, checks for `claudeAiOauth.accessToken`.

#### `authenticateRequest(req, config) → { ok: boolean, error?: string }`

Validates request against `proxy_api_key`. Open access when not configured.

---

### logger.js

Static logging class with 5 log levels. ESM export.

**Export:** `export default Logger`

---

## Key Data Structures

### Claude Code Credentials (`~/.claude/.credentials.json`)

```json
{
  "claudeAiOauth": {
    "accessToken": "eyJ...",
    "refreshToken": "eyJ...",
    "expiresAt": 1709400000000
  }
}
```

Read automatically by the Agent SDK. Token refresh is handled by the SDK internally.

---

## Edge Cases Checklist

| Scenario | Handling |
|----------|----------|
| **No credentials** | SDK throws auth error → returned as 500 to client |
| **Client disconnect** | `res.on('close')` aborts SDK query via AbortController |
| **Multi-turn messages** | Prior messages formatted as `<conversation_history>` XML context |
| **Streaming error** | After headers sent: SSE error event written; before: 500 JSON |
| **System prompt** | Request-level > config-level > SDK default |
| **Thinking tokens** | Request-level > config-level |
| **Docker environment** | Auto-binds `0.0.0.0` (detected via `/.dockerenv` or cgroup) |
| **CORS** | Allows `Content-Type, Authorization, x-api-key, anthropic-version, anthropic-beta` |

Related Skills

We are still matching the closest adjacent skills for this page. In the meantime, continue through the full directory.