@aizigao/pi-claude-code-headers-compat

Pi extension package for Claude-compatible header and request-path adaptation across multiple providers.

Packages

Package details

extension

Install @aizigao/pi-claude-code-headers-compat from npm and Pi will load the resources declared by the package manifest.

$ pi install npm:@aizigao/pi-claude-code-headers-compat
Package
@aizigao/pi-claude-code-headers-compat
Version
0.4.0
Published
Jun 1, 2026
Downloads
909/mo · 614/wk
Author
aizigao
License
MIT
Types
extension
Size
23.3 KB
Dependencies
2 dependencies · 1 peer
Pi manifest JSON
{
  "extensions": [
    "./index.ts"
  ]
}

Security note

Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.

README

pi-claude-code-headers-compat

简体中文 | English

npm version

The requests sent by Pi with "api": "anthropic-messages" use the SDK format, which is not exactly the same as the requests sent by Claude Code. The specific problem I am facing is that my proxy provider is currently blocked by Cloudflare as crawler traffic. The purpose of this package is to make the request headers look broadly closer to what Claude Code sends.

The goal of this plugin is not to change the structure of models.json, but to add an extra compatibility layer while keeping Pi's original provider configuration style.

Who this is for

This package is suitable for users who:

  • already configured custom providers in Pi
  • use Claude-compatible proxy/provider endpoints
  • run into incompatible headers, incompatible paths, or unexpected API responses
  • want to handle these compatibility rules through a standalone package
  • get 403 responses from the proxy provider

What it does

When enabled, the package will:

  • read configured providers from the official ~/.pi/agent/models.json
  • read compatibility settings from ~/.pi/agent/claude-code-headers-compat.json
  • only apply compatibility handling to matched provider requests
  • automatically rewrite request paths and adjust headers before sending the request

Installation

Install it in Pi with:

pi install npm:@aizigao/pi-claude-code-headers-compat

If you are developing this project locally, use:

npm install

Configuration

1. Configure the provider

Using my current proxy provider aicoding.sh as an example: when used in Pi, Cloudflare treats the request as crawler traffic because of the headers and returns 403. My configuration is:

File: ~/.pi/agent/models.json. Keep using Pi's default format for this file. No structure changes are needed.

{
  "providers": {
    "aicoding-sh-anthropic": {
      "baseUrl": "https://api.aicoding.sh",
      "api": "anthropic-messages",
      "apiKey": "$AI_CODING_SH_API_KEY",
      "models": [
        {
          "id": "claude-sonnet-4-6",
          "name": "azg-claude-sonnet-4-6",
          "reasoning": true,
          "input": ["text", "image"],
          "cost": {
            "input": 3,
            "output": 15,
            "cacheRead": 0.3,
            "cacheWrite": 3.75
          },
          "contextWindow": 1000000,
          "maxTokens": 128000
        },
        {
          "id": "claude-opus-4-7",
          "name": "azg-claude-opus-4-7",
          "reasoning": true,
          "input": ["text", "image"],
          "cost": {
            "input": 15,
            "output": 75,
            "cacheRead": 1.5,
            "cacheWrite": 18.75
          },
          "contextWindow": 1000000,
          "maxTokens": 128000
        },
        {
          "id": "gpt-5.5",
          "name": "azg-gpt-5.5",
          "reasoning": true,
          "input": ["text", "image"],
          "cost": {
            "input": 5,
            "output": 30,
            "cacheRead": 0.5,
            "cacheWrite": 6.25
          },
          "contextWindow": 1000000,
          "maxTokens": 128000
        },
        {
          "id": "gpt-5.4",
          "name": "azg-gpt-5.4",
          "reasoning": true,
          "input": ["text", "image"],
          "cost": {
            "input": 2.5,
            "output": 15,
            "cacheRead": 0.25,
            "cacheWrite": 3.125
          },
          "contextWindow": 1000000,
          "maxTokens": 128000
        },
        {
          "id": "gpt-5.3-codex",
          "name": "azg-gpt-5.3-codex",
          "reasoning": true,
          "input": ["text", "image"],
          "cost": {
            "input": 1.75,
            "output": 14,
            "cacheRead": 0.175,
            "cacheWrite": 2.1875
          },
          "contextWindow": 1000000,
          "maxTokens": 128000
        }
      ]
    }
  }
}

Notes:

  • the key under providers is the provider name
  • apiKey follows the latest official value-resolution rules: it supports !command, $ENV, ${ENV}, composite interpolation like ${KEY_PREFIX}_${KEY_SUFFIX}, escapes like $$ / $!, and literal values
  • legacy uppercase env-style values such as AI_CODING_SH_API_KEY are still treated as env var names for backward compatibility
  • the plugin matches compatibility logic using the provider name and baseUrl

2. Configure compatibility rules

File: ~/.pi/agent/claude-code-headers-compat.json

Your current configuration example:

{
  "enable": true,
  "matchedProviders": ["aicoding-sh-anthropic"],
  "modifyHeaders": {
    "USER_AGENT": "2.1.110 (Claude Code)",
    "ANTHROPIC_VERSION": "2023-06-01"
  }
}

Field reference:

enable

Whether to enable it.

matchedProviders

The list of provider names that should use compatibility handling. Configure it as needed.

Requirements:

  • the names must match the provider keys in models.json
  • only providers listed here will get compatibility handling

Compatibility note:

  • matchedProviders is the current field name
  • legacy matchedProvidersUrl is still supported for backward compatibility

modifyHeaders

Used to override default header values.

Currently supported:

  • USER_AGENT
  • ANTHROPIC_VERSION
  • ACCEPT
  • CONTENT_TYPE
  • AUTHORIZATION

Notes:

  • if a field is omitted, it falls back to the default value
  • AUTHORIZATION defaults to Bearer ${API_KEY}
  • ${API_KEY} will be replaced with the final resolved key value for that provider
  • apiKey resolution follows Pi's latest rules:
    • !command executes the command and uses stdout
    • $ENV / ${ENV} interpolates environment variables, including composite values like ${KEY_PREFIX}_${KEY_SUFFIX}
    • $$ emits a literal $, and $! emits a literal !
    • other values are treated as literals
  • for backward compatibility, legacy uppercase env-style values are still resolved as environment variable names when present

How it works

This package uses @aizigao/pi-fetch-pipeline to register a fetch middleware that intercepts outgoing requests. When a request matches a configured provider, the middleware rewrites the URL and adjusts headers before forwarding the request down the pipeline. This approach avoids directly patching globalThis.fetch, making it safer to coexist with other extensions that also modify fetch behavior.

Usage

After configuration:

  1. make sure the provider is already defined in models.json
  2. make sure that provider is enabled in claude-code-headers-compat.json
  3. start Pi
  4. when that provider sends requests, the plugin will automatically apply the compatibility handling

Notes

  • provider names in matchedProviders must stay consistent with models.json
  • legacy matchedProvidersUrl is still accepted, but new configs should use matchedProviders
  • apiKey in models.json supports the latest Pi value-resolution syntax, including !command, $ENV, ${ENV}, composite interpolation, escapes, and literals
  • this package is currently aimed at Claude-compatible provider scenarios
  • if a provider does not match, requests will not be rewritten

Request header change list (added/removed/modified)

Note: the following changes only apply when a provider matches the compatibility rules.

Removed headers

Removed by default:

  • x-api-key
  • anthropic-dangerous-direct-browser-access
  • accept-language
  • x-app
  • x-pi-provider-marker
  • x-stainless-* (prefix match)
  • sec-fetch-* (prefix match)

Added headers

Added by default when missing:

  • authorization: Bearer ${API_KEY}
  • user-agent: 2.1.110 (Claude Code)
  • anthropic-version: 2023-06-01
  • accept: application/json
  • content-type: application/json

In addition, the plugin injects a marker header during provider registration for request matching:

  • x-pi-provider-marker: <providerName>

Headers you can modify in config

These headers are force-overridden by default (if already present, values are rewritten):

  • authorization
  • user-agent
  • anthropic-version
  • accept
  • content-type

You can override the defaults through modifyHeaders (legacy style) or providers.<name>.setHeaders (new style).