Claude Code BYOS
Responses Proxy exposes an Anthropic Messages-compatible gateway for Claude Code:
POST /v1/messages
POST /v1/messages/count_tokens
GET /v1/models
These routes share the org-scoped proxy keys, provider registry, model aliases, routing, metrics, and telemetry used by /v1/responses. Registered aliases can use anthropic_messages, responses, or chat_completions providers.
Connect Claude Code
Claude Code may already use Authorization or x-api-key for Anthropic credentials, so the gateway reads the Responses Proxy key from X-Responses-Proxy-Key by default.
export RESPONSES_PROXY_KEY="rprx-..."
export ANTHROPIC_BASE_URL="https://agent-responses-proxy.svc.cloud.morph.so/v1"
export ANTHROPIC_CUSTOM_HEADERS="X-Responses-Proxy-Key: $RESPONSES_PROXY_KEY"
export CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY=1
claude --model "openai/gpt-mini"
GET /v1/models returns only enabled, Claude Code-compatible aliases in the proxy key's org, plus deployment-configured direct models. An alias with capabilities.claude_code.enabled=false, an invalid capability profile, or a Chat Completions provider disabled by deployment does not appear.
Supported alias profiles
The profile can be inferred from the provider wire_api or set explicitly on each alias:
| Profile | Provider wire_api | Behavior |
|---|---|---|
anthropic_native | anthropic_messages | Preserves first-party Messages formatting and provider-native behavior. |
openai_responses | responses | Converts Messages input to Responses and converts Responses JSON or SSE back to Messages. |
openai_chat | chat_completions | Converts Messages to canonical Responses input, then through the Chat Completions adapter; output follows the reverse path. |
auto | Any supported value above | Resolves to the profile matching the provider wire_api. This is the default. |
An explicit profile must match the provider protocol. For example, profile="openai_chat" is valid only for a chat_completions provider.
Capability schema and defaults
Claude Code compatibility is declared under the model alias capabilities object:
{
"claude_code": {
"enabled": true,
"profile": "auto",
"images": true,
"documents": true,
"client_tools": true,
"parallel_tools": true,
"hosted_tools": []
}
}
Defaults apply when capabilities.claude_code or one of its fields is omitted:
| Field | Default | Meaning |
|---|---|---|
enabled | true | Include the alias in model discovery and accept Claude Code traffic. |
profile | auto | Infer the compatibility profile from wire_api. |
images | true | Accept portable base64 and URL image blocks. |
documents | true | Accept portable base64, text, and URL document blocks, subject to profile limits. |
client_tools | true | Translate Claude function definitions, tool_use, and tool_result. |
parallel_tools | true | Allow parallel tool calls when requested by the client. |
hosted_tools | [] | Do not enable any hosted/server tool implicitly. |
Unknown fields, non-Boolean values for Boolean fields, unknown profiles, and unknown hosted-tool names are rejected when an alias is created or updated. Provider-less aliases cannot declare a Claude Code profile.
Existing provider-bound aliases with no claude_code object receive these defaults. Disable or narrow an alias explicitly when its upstream model does not support images, documents, or tools.
Content mapping
anthropic_native aliases send Messages content to the Anthropic provider after model rewriting and normalization. Translated openai_responses and openai_chat aliases use these mappings:
| Claude Messages input | Canonical Responses mapping | Chat Completions mapping |
|---|---|---|
| System text | instructions | System message. |
| User text block | input_text | Text content part. |
| Base64 image | input_image.image_url data URL | image_url.url data URL. |
| URL image | input_image.image_url | image_url.url. |
| Base64 document | input_file.file_data data URL | file.file_data. |
| Text document | UTF-8 text encoded as base64 input_file.file_data | file.file_data. |
| URL document | input_file.file_url | Not portable; rejected for Chat Completions. |
search_result block | JSON serialized into input_text | Text content. |
Assistant tool_use | function_call with JSON arguments | Assistant function call. |
User tool_result | function_call_output | Tool message; returned image/file parts become a following user media message. |
Image detail values auto, low, and high are preserved. Document title or source filename is preserved when present. Provider-scoped Claude file IDs are rejected because they cannot be used with another provider; send base64 data instead.
The proxy validates base64 before contacting the provider, accepts Claude's portable PNG, JPEG, GIF, and WebP image MIME types, and requires absolute HTTP or HTTPS media URLs. Invalid media returns 400 at the gateway boundary.
Thinking and redacted-thinking blocks are not sent to translated providers. Unsupported Anthropic-only request fields are normalized instead of being forwarded blindly. Successful translated responses can include X-Responses-Proxy-Normalized-Fields listing fields such as cache_control, context_management, top_k, and tool-level cache or eager-streaming hints that were removed.
Translated output currently maps model text and function/custom tool calls back to Claude text and tool_use blocks. Buffered and streaming tool arguments must be valid JSON objects; malformed upstream arguments return 502 rather than being silently changed.
Tool mapping and hosted-tool limits
Claude client tools map to OpenAI function tools. The proxy preserves name, description, input_schema, and Boolean strict; it maps tool_choice values auto, none, any, and named tool, including disable_parallel_tool_use.
Hosted tools are deliberately narrower:
web_searchis the only accepted value incapabilities.claude_code.hosted_tools.openai_responsesmaps Claude server-tool types beginning withweb_search_to the Responsesweb_searchtool.user_location,allowed_domains, andblocked_domainsare preserved.anthropic_nativekeeps provider-native web search on the Messages path.openai_chatrequireshosted_tools=[]; Chat Completions has no portable hosted-tool mapping.file_search, code execution, computer use, MCP, web fetch, and every other Claude server-tool type have no translated mapping. They are not dropped silently; translated aliases return an error.
Client tools such as Bash, Read, Edit, and other Claude Code tools are function tools executed by Claude Code. They are controlled by client_tools, not hosted_tools.
Register an alias through the console
In the Morph Cloud Responses console:
- Create or select a provider with
wire_apiset toresponses,chat_completions, oranthropic_messages. - Add and validate a provider credential.
- In Model aliases, enter the alias and upstream model id.
- Choose Auto or the matching Claude Code profile.
- Open Compatibility settings and disable features the upstream model does not support. Hosted web search is unavailable for Chat Completions.
- Use Test to run the alias against the
claude_codesurface, then save it. - Confirm the alias appears in
GET /v1/models, mint a proxy key, and run Claude Code with the alias.
The console's model test is a text smoke test. Test images, documents, tools, and streaming separately before enabling them for production traffic.
Register aliases through the API
Responses alias example:
{
"alias": "openai/gpt-mini",
"provider_id": "openai-main",
"upstream_id": "gpt-4.1-mini",
"capabilities": {
"claude_code": {
"profile": "openai_responses",
"hosted_tools": ["web_search"]
}
}
}
Chat Completions alias example:
{
"alias": "kimi/k2",
"provider_id": "kimi-main",
"upstream_id": "kimi-k2.6",
"capabilities": {
"claude_code": {
"profile": "openai_chat",
"documents": false,
"hosted_tools": []
}
}
}
Test the Claude Code surface before saving or rolling out the alias:
curl -sS -X POST "$RESPONSES_PROXY_BASE_URL/admin/models/test" \
-H "$ADMIN_HEADER" \
-H 'content-type: application/json' \
-d '{
"provider_id": "kimi-main",
"upstream_id": "kimi-k2.6",
"surface": "claude_code",
"capabilities": {
"claude_code": {"profile":"openai_chat","documents":false}
}
}'
Deployment settings
| Variable | Default | Purpose |
|---|---|---|
PROXY_CLAUDE_GATEWAY_CHAT_COMPLETIONS_ENABLED | true | Enables Chat Completions aliases on model discovery and /v1/messages. Set false as a deployment-wide rollback switch. |
PROXY_CLAUDE_GATEWAY_MAX_BODY_BYTES | 33554432 | Maximum Claude gateway request body, including base64 expansion. Requests over the limit return 413. |
PROXY_CLAUDE_GATEWAY_PROXY_KEY_HEADER | x-responses-proxy-key | Header containing the org-scoped proxy key. |
PROXY_CLAUDE_GATEWAY_DIRECT_MODELS | unset | Comma-separated model ids allowed for direct Anthropic passthrough. |
PROXY_CLAUDE_GATEWAY_UPSTREAM_BASE_URL | https://api.anthropic.com/v1 | Upstream base URL used only by direct passthrough. |
The compatibility expansion does not require a database migration; it uses the existing alias capabilities JSON. When request-body capture is enabled, the proxy redacts base64 image and document data before persisting Claude gateway request telemetry.
Stored subscription setup
Stored subscription setup moves a consented Claude Code OAuth bearer credential into an encrypted, org-owned provider credential row. It applies only to first-party Anthropic routing, not OpenAI Responses or Chat Completions aliases.
Enable it in the proxy deployment:
PROXY_ENABLE_CLAUDE_CODE_TOKEN_ESCROW=true
PROXY_SECRET_ENCRYPTION_KEY=...
PROXY_CLAUDE_CODE_OAUTH_REFRESH_URL=...
PROXY_CLAUDE_CODE_OAUTH_CLIENT_ID=...
Create an Anthropic OAuth provider with provider_type="anthropic", base_url="https://api.anthropic.com/v1", wire_api="anthropic_messages", auth_type="bearer", and auth_header="authorization". Then create a setup session from the dashboard and run the one-time command on the machine where Claude Code is already logged in:
printf '%s\n' "$SETUP_TOKEN" | responses-proxy-claude-code teleport \
--base-url "$RESPONSES_PROXY_BASE_URL" \
--session-id "$SESSION_ID" \
--setup-token-stdin
teleport reads Claude Code OAuth state from CLAUDE_CONFIG_DIR or ~/.claude. On Linux it rejects group- or other-readable credential files by default. Fix permissions with chmod 600 ~/.claude/.credentials.json; use --allow-insecure-files only for local throwaway testing.
Refresh settings are deployment-owned. If refresh is unavailable or fails after expiry, the credential is marked needs_reauth and removed from routing until setup is completed again.
Direct passthrough
Direct passthrough is for Claude Code sessions that already hold a local Anthropic credential. The proxy still requires X-Responses-Proxy-Key for org scoping and telemetry, but forwards the caller's Anthropic credential upstream.
PROXY_CLAUDE_GATEWAY_DIRECT_MODELS=claude-sonnet-4-5,claude-haiku-4-5
Do not register an alias with the same name unless registered-alias mode should take precedence.
Rollout and testing
For a staged rollout:
- Deploy the backend and frontend with
PROXY_CLAUDE_GATEWAY_CHAT_COMPLETIONS_ENABLED=falseif Chat aliases should remain hidden initially. - Review existing aliases. Because omitted Claude Code capabilities use permissive defaults, explicitly disable unsupported media or tools.
- Run
surface="claude_code"model tests for Responses and Chat providers. - Exercise
GET /v1/models, buffered and streaming/v1/messages,/v1/messages/count_tokens, a complete client-tool round trip, and each enabled media type. - Enable Chat Completions routing, reload routing or wait for the refresh interval, and monitor request status, latency, and provider-specific failures in the Responses console.
- Roll back Chat compatibility by setting
PROXY_CLAUDE_GATEWAY_CHAT_COMPLETIONS_ENABLED=false; Responses and Anthropic aliases remain available.
Errors
401:X-Responses-Proxy-Keyis missing, invalid, or revoked.404 Unknown model: the model is neither an enabled org alias nor an allowed direct model.413 request_too_large: the JSON body exceedsPROXY_CLAUDE_GATEWAY_MAX_BODY_BYTES.422 ... disabled for alias:capabilities.claude_code.enabledisfalse.422 ... disabled by deployment: Chat Completions routing is turned off globally.400 ... profile ... requires wire_api: the explicit profile does not match its provider.422 Image inputs...,Document inputs..., orClient tools... disabled: the request exceeds the alias capability declaration.422 ... no exact provider mapping: a translated request uses an unsupported Claude server tool or content block.422 ... document URLs: a Chat Completions alias received a URL document; send base64 data.502 ... invalid JSON arguments: the upstream returned malformed function-call arguments.
Current limitations
customproviders remain unroutable. Claude Code aliases requireanthropic_messages,responses, or enabledchat_completionsresources.openai_responsesandopenai_chatnormalize Anthropic-only thinking, prompt-caching, context-management, and beta features. They do not provide bit-for-bit Messages semantics.- Chat Completions document support varies by provider. Base64 file input is the portable proxy shape; URL documents are rejected.
- Provider-scoped file IDs do not cross provider boundaries.
- Translated model output supports text and client tool calls. Model-generated image, document, audio, computer-use, and arbitrary provider-specific output blocks are not converted to Claude Messages blocks.
- Only hosted
web_searchis portable to Responses. Chat Completions has no hosted-tool mapping. /v1/messages/count_tokensuses a local conservative estimate for Responses and Chat aliases. Billing telemetry comes from completed upstream calls.- Local token estimates charge a fixed media allowance and do not count raw base64 bytes as prompt text.
- Failed or truncated translated streams emit an Anthropic error event and degrade the selected resource; they are not closed as successful messages.
- The dashboard model test covers text only; capability toggles are declarations, not provider feature detection.