| name | cc-switch-proxy |
| description | Configure and troubleshoot CC Switch proxy for Codex, Claude Code, and other AI coding agents. Covers provider setup, API format conversion, role mapping, and debugging proxy errors. |
| version | 1.0.0 |
| author | Hermes Agent |
| license | MIT |
| platforms | ["windows","linux","macos"] |
| metadata | {"hermes":{"tags":["proxy","cc-switch","codex","claude-code","routing","api-format"],"related_skills":["hermes-agent","claude-code","codex"]}} |
CC Switch Proxy
CC Switch is a local proxy/router for AI coding agents (Codex, Claude Code, Gemini, etc.) that provides unified API routing, model mapping, failover, and usage tracking.
When to Use
- Connecting Codex/Claude Code to custom API endpoints
- Routing through local proxy for usage tracking
- Managing multiple providers with model name mapping
- Debugging
BadRequestError: Unexpected message role or similar API format errors
Key Concepts
API Format Modes
CC Switch supports two upstream API formats:
| Mode | Value | Behavior |
|---|
| Chat Completions | openai_chat | Converts Responses API format → Chat Completions format |
| Responses | openai_responses | Passes through Responses API format unchanged |
Role Mapping (Critical Pitfall)
Codex uses developer role internally (Responses API format). When CC Switch converts to Chat Completions:
developer → system
content: [{"type": "input_text", "text": "..."}] → content: "..."
If conversion fails, you'll see:
BadRequestError: Unexpected message role
Invalid user message at index N
- HTTP 500 from upstream API
wire_api Configuration
In Codex's settings_config, the wire_api field controls the API format:
wire_api = "responses" # Uses OpenAI Responses API format (developer role)
wire_api = "chat_completions" # Uses standard Chat Completions format (system role)
If your upstream API doesn't support Responses API format, set wire_api = "chat_completions".
Configuration
Database Path
~/.cc-switch/cc-switch.db
Key Tables
| Table | Purpose |
|---|
providers | Provider definitions, auth, config, meta |
proxy_config | Proxy server settings per app_type |
proxy_request_logs | Request/response tracking |
provider_endpoints | Upstream API endpoint URLs |
settings | Global CC Switch settings |
Checking Provider Configuration
import sqlite3, json
db_path = "~/.cc-switch/cc-switch.db"
conn = sqlite3.connect(db_path)
cursor = conn.cursor()
cursor.execute("""
SELECT id, name, meta, settings_config
FROM providers
WHERE app_type = 'codex' AND is_current = 1
""")
row = cursor.fetchone()
meta = json.loads(row[2])
settings = json.loads(row[3])
print(f"API Format: {meta.get('apiFormat')}")
print(f"Config: {settings.get('config', '')[:500]}")
Common Meta Fields
apiFormat: "openai_chat" or "openai_responses"
endpointAutoSelect: Auto-select endpoint from catalog
codexChatReasoning: Codex-specific reasoning/thinking config
Troubleshooting
Error: Unexpected message role
Cause: CC Switch didn't convert developer → system role properly.
Fix:
- Check
apiFormat in provider meta
- If upstream doesn't support Responses format, change Codex's
wire_api to "chat_completions"
- Or change
apiFormat to "openai_responses" and ensure upstream supports it
Error: Invalid user message at index N
Cause: Content array format not converted. Codex sends content: [{"type": "input_text", "text": "..."}] but upstream expects content: "...".
Fix: Same as above — ensure proper format conversion or use Chat Completions mode.
Error: HTTP 500 from proxy
Cause: Proxy received request but upstream API rejected it.
Fix: Check proxy_request_logs in the CC Switch database for error_message field.
Proxy Not Running
netstat -ano | findstr 15721
lsof -i :15721
Test Proxy Connection
import urllib.request, json
test_request = {
"model": "your-model",
"messages": [
{"role": "user", "content": "Hello"}
],
"max_tokens": 100,
"stream": False
}
req = urllib.request.Request(
"http://127.0.0.1:15721/v1/chat/completions",
data=json.dumps(test_request).encode(),
headers={"Content-Type": "application/json", "Authorization": "Bearer YOUR_KEY"}
)
response = urllib.request.urlopen(req, timeout=30)
print(response.read().decode())
Session Log Locations
| Location | Content |
|---|
~/.cc-switch/logs/cc-switch.log | Proxy server logs |
~/.codex/sessions/ | Codex session JSONL files |
~/.hermes/logs/agent.log | Hermes agent logs |
Related Skills
hermes-agent — Hermes configuration
codex — Codex CLI delegation
claude-code — Claude Code CLI delegation
Support Files
references/debugging-role-errors.md — Step-by-step debugging guide for "Unexpected message role" errors