How the OpenClaude OpenAI Shim Layer Normalizes API Calls for Multi-Provider Support

The OpenClaude OpenAI shim layer normalizes API calls through a three-stage pipeline—request planning, message conversion, and schema normalization—that transforms Anthropic-style SDK inputs into provider-compliant OpenAI payloads while handling transport selection, role mapping, and strict JSON schema enforcement.

The OpenClaude project utilizes an OpenAI shim layer to bridge its internal Anthropic-style SDK architecture with diverse OpenAI-compatible backends including OpenAI, Azure OpenAI, Ollama, Groq, and DeepSeek. This normalization system ensures that regardless of the original Anthropic input structure, every provider receives a correctly formatted request payload that adheres strictly to the OpenAI API contract.

The Three-Stage Normalization Pipeline

The shim implements normalization through three tightly-coupled stages that progressively transform the request from Anthropic format to provider-specific OpenAI compatibility.

1. Request Planning with prepareOpenAIRequest

The prepareOpenAIRequest function in src/services/api/openaiShim/requestPreparation.ts serves as the primary orchestrator for building the request body. This function determines the appropriate transport mechanism through the effectiveTransport calculation at lines 50-57, selecting between chat-completions, responses, or Gemini protocols based on the target endpoint path. It injects default parameters such as stream and temperature while stripping incompatible fields—such as removing the store parameter for Gemini—using the shimConfig.removeBodyFields loop around line 80.

2. Message and Tool Conversion

The conversion stage transforms Anthropic-specific data structures into OpenAI-compatible formats through two specialized functions. convertMessages in src/services/api/openaiShim/messageConversion.ts maps Anthropic roles (assistant, user, tool) to OpenAI equivalents and handles image block processing. Simultaneously, convertTools in src/services/api/openaiShim.ts (lines 42-48) delegates to src/services/api/openaiShim/toolConversion.ts to transform Anthropic tool descriptors into OpenAI-compatible function objects.

3. Schema Normalization and Strict Mode

The final stage ensures JSON schema compliance through normalizeSchemaForOpenAI in src/services/api/openaiShim/toolConversion.ts (lines 12-20). This function recursively sanitizes schemas to guarantee that every property defined in properties is explicitly listed in the required array, preventing the "required must be a superset of properties" 400 errors common on OpenAI-compatible back-ends. When strict mode is enabled—the default unless the provider is Gemini or the user sets OPENCLAUDE_DISABLE_STRICT_TOOLS—the function also enforces additionalProperties: false to prevent unexpected property injection.

Transport Selection and Provider-Specific Optimization

The shim dynamically adapts requests based on provider capabilities through intelligent transport selection and field filtering. The effectiveTransport logic identifies whether the target endpoint uses standard chat-completions, the newer /responses format, or Gemini-specific paths, determining which request body fields are preserved or removed.

For providers that implement implicit prefix caching—such as OpenAI and DeepSeek—the shim invokes providerUsesImplicitPrefixCaching (lines 92-107 in requestPreparation.ts) to disable tool-history compression. This preserves cache efficiency by avoiding modifications to request prefixes that would otherwise invalidate cached context windows.

Converting Anthropic Messages to OpenAI Format

The convertMessages function exposed via __test.convertMessages in src/services/api/openaiShim.ts (lines 38-44) constructs an array of OpenAIMessage objects defined at lines 90-102. This conversion handles complex content types including image blocks and reasoning content preservation, ensuring that multimodal Anthropic inputs translate correctly to OpenAI's message schema without data loss or role misalignment.

Normalizing Tool Schemas for OpenAI Compatibility

Tool normalization involves filtering specific tools—such as ignoring ToolSearchTool—and passing remaining tool schemas through sanitizeSchemaForOpenAICompat. The convertTools implementation in src/services/api/openaiShim/toolConversion.ts (lines 59-71) applies strict schema enforcement when enabled, ensuring that all function parameters are properly constrained and required fields are explicitly declared. This prevents runtime validation errors on strict OpenAI-compatible endpoints while maintaining flexibility for providers like Gemini that require relaxed schemas.

Usage Example: Creating a Normalized Request

The following implementation demonstrates how OpenClaude uses the shim client to normalize Anthropic-style requests for OpenAI-compatible providers:

// 1️⃣ Create a shim client (the entry point used throughout OpenClaude)
import { createOpenAIShimClient } from './services/api/openaiShim.js';
const client = createOpenAIShimClient({ defaultHeaders: { 'x-api-key': process.env.OPENAI_API_KEY } });

// 2️⃣ Build an Anthropic‑style request
const anthropicPayload = {
  model: 'claude-2',
  messages: [
    { role: 'user', content: 'Explain recursion.' },
    { role: 'assistant', content: 'Sure!', tool_calls: [] },
  ],
  tools: [
    { name: 'Calculator', description: 'Simple math', input_schema: { type: 'object', properties: { a: {type: 'number'}, b: {type: 'number'} } } },
  ],
  stream: true,
};

// 3️⃣ Let the shim normalize everything and send the request
client.messages.create(anthropicPayload).then(response => {
  console.log('Anthropic‑compatible streamed response:', response);
});

Under the hood, client.messages.create invokes createShimRequest → _doRequest → prepareOpenAIRequest, which coordinates convertMessages and convertTools to produce a normalized payload. The resulting body is dispatched via executeOpenAIRequest, ensuring provider-ready formatting regardless of the original Anthropic SDK structure.

Summary

  • The OpenClaude OpenAI shim layer implements three-stage normalization: request planning, message/tool conversion, and schema sanitization.
  • prepareOpenAIRequest in src/services/api/openaiShim/requestPreparation.ts handles transport selection and provider-specific field filtering through effectiveTransport calculations and providerUsesImplicitPrefixCaching logic.
  • Message conversion via convertMessages maps Anthropic roles and content types to OpenAI equivalents, while tool conversion via convertTools handles schema transformation.
  • normalizeSchemaForOpenAI ensures JSON schema compliance by auto-populating the required array with all properties keys and enforcing additionalProperties: false in strict mode.
  • The shim automatically removes incompatible fields—such as store for Gemini—while preserving cache-efficient request structures for providers like OpenAI and DeepSeek.

Frequently Asked Questions

How does the shim determine which transport protocol to use?

The shim calculates the effectiveTransport in src/services/api/openaiShim/requestPreparation.ts (lines 50-57) by analyzing the target endpoint path. Endpoints containing /messages trigger chat-completions transport, /responses triggers the responses API format, and paths containing models/gemini activate Gemini-specific handling. This determination controls which request body fields are preserved or stripped before transmission.

Why does the shim modify JSON schemas to add required fields?

OpenAI-compatible backends reject schemas where the required array does not include all keys defined in properties. The normalizeSchemaForOpenAI function in src/services/api/openaiShim/toolConversion.ts (lines 12-20) recursively traverses schemas to ensure every property is listed in required, preventing 400 validation errors while maintaining the intended structural constraints of the original Anthropic tool definitions.

How does the shim handle provider-specific restrictions like Gemini's limitations?

After building the normalized request body, the shim executes a cleanup loop using shimConfig.removeBodyFields around line 80 of requestPreparation.ts to strip incompatible parameters. For Gemini specifically, it removes the store field and disables strict schema mode (which would enforce additionalProperties: false) since Gemini does not support these OpenAI-specific extensions. This ensures maximum compatibility across diverse backend implementations.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →