Install
$ agentstack add skill-agentproto-ts-agentproto-llm-endpoint ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
When to use
- "Route Claude through OpenRouter / Moonshot / Groq / ZAI / xAI"
- "Use the local LLM proxy" or "point Claude at my gated LLM endpoint"
- "Why is my model codename rejected?"
- "Claude Code vs Claude SDK with custom endpoints"
- Spawn failures: "Invalid API key", "model does not exist", empty turns
The proxy
The LLM endpoint proxy lives in projects/agentproto/ts/packages/llm-endpoint. It exposes an Anthropic-compatible /v1/messages endpoint and maps "secret codenames" to real provider models.
Codename → provider mapping
| Code | Provider | Real model | Claude equivalent | | ----------- | ---------- | ------------------------------- | ------------------- | | jupiter-7 | Moonshot | kimi-k2.7-code | claude-opus-4-8 | | mars-6 | Moonshot | kimi-k2.6 | claude-3-opus | | saturn-5 | OpenRouter | deepseek/deepseek-v4-pro | claude-sonnet-5 | | neptune-4 | OpenRouter | anthropic/claude-sonnet-4.6 | claude-sonnet-4-6 | | uranus-8 | OpenRouter | google/gemini-3.1-pro-preview | claude-fable-5 | | mercury-9 | OpenRouter | z-ai/glm-5.2 | claude-3-haiku | | halley-1 | OpenRouter | deepseek/deepseek-v4-flash | claude-fable-4 | | orion-2 | OpenRouter | xiaomi/mimo-v2.5 | claude-opus-4-6 | | pegasus-3 | OpenRouter | minimax/minimax-m3 | claude-opus-4-9 | | lyra-4 | OpenRouter | tencent/hy3-preview | claude-opus-4-7 | | vega-5 | OpenRouter | stepfun/step-3.7-flash | claude-sonnet-4-5 | | venus-3 | ZAI | glm-5.2 | claude-3-5-fable | | pluto-2 | Groq | qwen/qwen3.6-27b | claude-haiku-4-5 | | atlas-6 | Groq | llama-3.3-70b-versatile | claude-3-5-sonnet | | titan-7 | Groq | openai/gpt-oss-120b | claude-sonnet-4-7 | | nova-1 | xAI | grok-4.5 | claude-opus-4-8 | | pulsar-2 | xAI | grok-3.5 | claude-sonnet-5 |
Running the proxy locally
# From repo root
pnpm --filter=@agentproto/llm-endpoint start
# → listens on http://localhost:18090/v1
Env vars the proxy reads:
MOONSHOT_API_KEY— Moonshot native/anthropicendpointOPENROUTER_API_KEY— OpenRouter (OpenAI-compatible adapter)ZHIPUAI_API_KEYorZAI_API_KEY— ZAI (OpenAI-compatible adapter)GROQ_API_KEY— Groq (OpenAI-compatible adapter, 128-tool max)
The proxy adapts Anthropic Messages format to OpenAI Chat Completions for OpenRouter/ZAI/Groq. Moonshot exposes a native /anthropic endpoint and needs no adaptation.
Client config
Base URL: http://localhost:18090/v1 (local), or your own gated public origin
API key: local → any non-empty value (e.g. AAAA); the proxy injects the real
upstream key server-side and, ungated, does not check it
public → Authorization: Bearer
(set LLM_ENDPOINT_ACCESS_TOKENS on the proxy; a missing or
unlisted token returns 401 — the upstream key stays server-side)
The proxy accepts ?m= query param override and ?p= override.
Adapter-specific behaviors
Claude SDK (claude-sdk adapter) — RECOMMENDED for proxy
- Model validation: NONE. Passes any model string directly to the SDK.
- How model is sent:
--modelCLI arg → SDKoptions.model. - Base URL: Set via
options.base_url→ANTHROPIC_BASE_URLenv. - Auth:
ANTHROPIC_API_KEYenv (set via--auth api-key).
Working spawn via MCP:
{
"adapter": "claude-sdk",
"auth": { "mode": "api-key" },
"model": "neptune-4",
"options": { "base_url": "http://localhost:18090/v1" },
"prompt": "..."
}
Working spawn via CLI:
# DOES NOT WORK — CLI has no --base-url flag
agentproto sessions start claude-sdk --model neptune-4 --auth api-key
# WORKAROUND: set env vars before the CLI command
ANTHROPIC_BASE_URL=http://localhost:18090/v1 \
ANTHROPIC_API_KEY=AAAA \
agentproto sessions start claude-sdk --model neptune-4 --prompt "..."
Claude Code (claude-code adapter) — LIMITED with proxy
- Model validation: STRICT. The
claudebinary validatesANTHROPIC_MODEL
against Anthropic's real model list. Proxy codenames (neptune-4, etc.) are rejected before any network request.
- How model is sent:
ANTHROPIC_MODELenv var (set viaoptions.model→
adapter manifest env: { ANTHROPIC_MODEL: "{value}" }). The ACP wrapper applies it via session/set_config_option(configId: "model") with best-effort fallback to default on rejection.
- Base URL: Set via
options.base_url→ANTHROPIC_BASE_URLenv. - Auth:
ANTHROPIC_API_KEYenv (api-key mode) orCLAUDE_CODE_OAUTH_TOKEN
(subscription mode).
Result: claude-code with proxy codenames fails with:
> "There's an issue with the selected model (neptune-4). It may not exist or you > may not have access to it."
Workaround: Use claude-sdk instead for proxy access. If you must use claude-code, you are limited to the proxy's regex fallback mappings:
| Model you send | Proxy routes to | | ------------------ | ------------------------------------- | | claude-sonnet-5 | openrouter:deepseek/deepseek-v4-pro | | claude-opus-4-8 | moonshot:kimi-k2.7-code | | claude-fable-5 | moonshot:kimi-k2.7-code | | claude-haiku-4-5 | groq:llama-3.3-70b |
These are NOT the same as the codename mappings — you lose fine-grained provider/model selection.
Why Claude Desktop works with codenames
Claude Desktop (the app) uses the Claude SDK directly (not the claude binary), and the SDK does not validate model names before sending. When you configure a custom endpoint in Claude Desktop settings, the SDK sends the raw model string to your proxy, which resolves the codename.
Claude Code (the CLI/claude-code adapter) uses the claude binary which has strict model validation — this is why the same codename fails there.
Auth patterns
Direct Anthropic API (subscription or api-key)
{
"adapter": "claude-code",
"auth": { "mode": "subscription" },
"prompt": "..."
}
Subscription mode uses CLAUDE_CODE_OAUTH_TOKEN (bearer token from claude auth login). API-key mode uses ANTHROPIC_API_KEY.
Gateway / proxy (Moonshot, OpenRouter, etc.)
{
"adapter": "claude-sdk",
"auth": { "mode": "api-key" },
"model": "neptune-4",
"options": {
"base_url": "http://localhost:18090/v1",
"auth_token": "AAAA"
},
"prompt": "..."
}
The auth_token option sets ANTHROPIC_AUTH_TOKEN → Authorization: Bearer.
- Local (
localhost:18090): ungated — any value works (AAAA); the proxy
ignores it and uses its own upstream keys.
- Public (a gated deployment): when
LLM_ENDPOINT_ACCESS_TOKENSis set the
proxy enforces a bearer gate, so auth_token MUST be one of those gate tokens — a shared secret you set once on the provider/adapter config (an unlisted token → 401). The upstream provider key is still injected server-side; the gate token is separate from it. Send it via auth_token (→ Authorization: Bearer) or X-Proxy-Access — never x-api-key, which the gate ignores.
Per-provider API keys
Stored in ~/.agentproto/providers.json (mode 0600):
{
"openrouter": { "apiKey": "sk-or-v1-...", "updatedAt": "..." },
"moonshot": { "apiKey": "sk-...", "updatedAt": "..." },
"anthropic": { "apiKey": "sk-ant-api03-...", "updatedAt": "..." }
}
Set via: agentproto auth provider set or edit the file directly.
Securing a public deployment (gate env vars)
Opt-in env vars on the proxy (unset = off). Full detail in the package README's "Securing a public deployment".
LLM_ENDPOINT_ACCESS_TOKENS— app-gate allow-list (comma-separated). Present a
listed token as Authorization: Bearer or X-Proxy-Access: . x-api-key is NOT accepted.
LLM_ENDPOINT_EDGE_TOKENS— second, independent layer viaX-Edge-Auth, meant
to be enforced at the edge (Cloudflare WAF) so unauthenticated traffic never reaches the origin; also re-checked in-process.
LLM_ENDPOINT_PUBLIC_MODELS=1— exempts ONLY/v1/models(/models) from the
gates so clients can auto-discover models (e.g. Claude Desktop's launch probe); pack lists stay gated.
llm-endpoint print-waf-rule [--host ] emits the Cloudflare custom-rule expression from the configured token(s) — paste into a Block rule (add a /v1/models carve-out when PUBLIC_MODELS is on).
Common failure modes
"Invalid API key" / "Invalid bearer token"
- Check
~/.agentproto/providers.jsonhas the right key for the provider. - For gateway modes, ensure
auth_tokenis set (not justANTHROPIC_API_KEY). - The
claude-codeadapter scrubsANTHROPIC_API_KEYwhenbase_urlis set
(to prevent leaking your real Anthropic key to a third-party gateway).
"Model does not exist" / "empty turn"
claude-codeadapter: you're using a proxy codename. Useclaude-sdk
instead.
claude-sdkadapter: checkANTHROPIC_BASE_URLis actually set (CLI doesn't
support --base-url; use env var or MCP options.base_url).
- Proxy not running:
curl http://localhost:18090/v1/modelsshould return the
codename list.
"empty turn — no assistant output, no tool call"
- The proxy received the request but the upstream provider returned nothing.
- Check proxy logs:
tail -f /tmp/llm-endpoint.log - Common cause: model name mismatch (proxy regex fallback routed to wrong
provider).
Double /v1/v1/ in proxy paths
The proxy base URL should end with /v1 (e.g., http://localhost:18090/v1). The claude binary appends /v1/messages, resulting in /v1/v1/messages. The proxy handles this, but it's noisy in logs.
Daemon caches old adapter code
The daemon (Node.js) caches require()/import() results. After editing an adapter manifest, restart the daemon:
pkill -f "agentproto serve"
~/.agentproto/start-daemon-local.sh --cli workspace
Quick reference: spawn patterns
| Goal | Adapter | Model | Base URL | Auth | | ------------------------------- | ------------- | ----------------- | --------------------------- | -------------- | | Direct Anthropic (subscription) | claude-code | (default) | (none) | subscription | | Direct Anthropic (API key) | claude-code | (default) | (none) | api-key | | OpenRouter Claude Sonnet 4.6 | claude-sdk | neptune-4 | http://localhost:18090/v1 | api-key | | Moonshot Kimi K2.7 | claude-sdk | jupiter-7 | http://localhost:18090/v1 | api-key | | Groq Llama 3.3 70B | claude-sdk | atlas-6 | http://localhost:18090/v1 | api-key | | Claude Code + proxy (limited) | claude-code | claude-sonnet-5 | http://localhost:18090/v1 | api-key |
See also
agentprotoskill — general daemon/CLI/MCP usageadapter-setup-kitskill — tunnel creation, adapter catalogprojects/agentproto/ts/packages/llm-endpoint— proxy sourceprojects/agentproto/ts/adapters/claude-sdk/src/index.ts— SDK adapter
manifest
projects/agentproto/ts/adapters/claude-code/src/index.ts— Code adapter
manifest
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: agentproto
- Source: agentproto/ts
- License: Apache-2.0
- Homepage: https://agentproto.sh
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.