OpenAI Integration Guide
Technology: open-ai · Category: ai · Last reviewed: 2026-08-23
Source: https://tech-stack.codeamanilabs.org/guide/open-ai
Insight:
OpenAI is codeAmani's secondary provider — reach for it when structured output or function calling matters (its JSON-schema mode is strong), while Claude stays primary for reasoning. Build new work on the Responses API (
client.responses), now OpenAI's recommended default; Chat Completions still works ("the previous standard, supported indefinitely") and is the shape the Vercel AI SDK swaps undergenerateObject, so moving between providers stays a one-linemodelchange.
██████╗ ██████╗ ███████╗███╗ ██╗ █████╗ ██╗
██╔═══██╗██╔══██╗██╔════╝████╗ ██║██╔══██╗██║
██║ ██║██████╔╝█████╗ ██╔██╗ ██║███████║██║
██║ ██║██╔═══╝ ██╔══╝ ██║╚██╗██║██╔══██║██║
╚██████╔╝██║ ███████╗██║ ╚████║██║ ██║██║
╚═════╝ ╚═╝ ╚══════╝╚═╝ ╚═══╝╚═╝ ╚═╝╚═╝
OpenAI Integration Guide
Focus: Integrating OpenAI models and APIs alongside Claude Code workflows — dual-provider pipelines, the Responses API's built-in MCP tool, and cross-model automation.
Overview
Claude Code and OpenAI are complementary. OpenAI's models — the GPT-5.6 family (gpt-5.6, with the sol / terra / luna variants) plus the older GPT-5, GPT-4.1, GPT-4o and o-series models — are consumed inside Claude Code via the openai SDK. New work should target the Responses API (client.responses), which OpenAI now recommends as the default surface for all new projects; Chat Completions (client.chat.completions) remains fully supported. This lets you build hybrid workflows — e.g., route code and reasoning tasks to Claude, structured-extraction and vision tasks to GPT-5.6 — all from one Claude Code session.
Here is the simple mental model of where OpenAI sits as the secondary provider — reach for it when structured output or function calling matters, while Claude stays primary for reasoning.
flowchart TD
A["Task in Claude Code session"] --> Q1{"Structured output<br/>or function calling?"}
Q1 -->|"yes"| B["Route to OpenAI<br/>Responses API - gpt-5.6"]
Q1 -->|"no - reasoning, code gen"| C["Stay on Claude<br/>primary"]
B --> D["openai SDK - client.responses"]
D --> E["Result back in session"]
C --> E
Official Documentation
| Resource | URL |
|---|---|
| OpenAI API Reference | https://developers.openai.com/api/reference/overview |
| Models overview | https://developers.openai.com/api/docs/models |
| Responses vs. Chat Completions | https://developers.openai.com/api/docs/guides/migrate-to-responses |
| Remote MCP tool (Responses API) | https://developers.openai.com/api/docs/guides/tools-connectors-mcp |
| OpenAI Node SDK | https://github.com/openai/openai-node |
| OpenAI Python SDK | https://github.com/openai/openai-python |
Docs domain note:
platform.openai.com/docs/*links still work but now 301-redirect todevelopers.openai.com. Prefer thedevelopers.openai.comcanonical URLs above.
Models at a glance (verify at the models page)
| Tier | Model ids | Use it for |
|---|---|---|
| Frontier | gpt-5.6-sol (alias gpt-5.6) |
Hardest reasoning/coding — but Claude is codeAmani's primary here |
| Balanced | gpt-5.6-terra |
General structured output at lower cost |
| Budget | gpt-5.6-luna |
Cheap-fast extraction, routing, classification |
| Dedicated reasoning | o3, o4-mini |
Chain-of-thought tasks (GPT-5 also reasons via effort levels) |
| Embeddings | text-embedding-3-small (1536), text-embedding-3-large (3072) |
RAG / semantic search |
GPT-5.6 models take a reasoning: { effort: "low" | "medium" | "high" | ... } control instead of a separate reasoning model. Pin a dated snapshot (e.g. gpt-5.6-2026-…) for reproducible CI; use the floating alias for product code.
MCP integration — the Responses API mcp tool
OpenAI adopted the Model Context Protocol; the integration point is a built-in tool on the Responses API. You give a GPT-5 model the URL of a remote MCP server and it calls that server's tools itself — no separate account-bridge package needed.
import OpenAI from "openai";
const client = new OpenAI();
const resp = await client.responses.create({
model: "gpt-5.6",
tools: [
{
type: "mcp",
server_label: "dmcp",
server_description: "A dice-rolling MCP server.",
server_url: "https://dmcp-server.deno.dev/mcp",
require_approval: "never", // approve tool calls in production instead
},
],
input: "Roll 2d4+1",
});
console.log(resp.output_text);
Direction of travel: this makes the OpenAI model an MCP client. To go the other way — expose your own resources to Claude Code as MCP tools — build a standard MCP server (see the repo's
mcp-server/guide), not an OpenAI-specific bridge. Never hand a third-party remote MCP server yourOPENAI_API_KEY; the key stays server-side andrequire_approvalgates tool execution.
Full guide: https://developers.openai.com/api/docs/guides/tools-connectors-mcp
Calling OpenAI from a Claude Code session
The openai Python package's legacy openai api … CLI has been removed from the docs; the SDK is the supported interface. For a quick one-off from a Claude Code shell, hit the Responses endpoint directly:
# Second opinion from GPT-5.6 without leaving the session
curl https://api.openai.com/v1/responses \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"gpt-5.6","input":"Review this code for memory leaks: ..."}'
The reply text is at .output_text in the JSON response.
OpenAI SDK Integration
You are about to wire up the core request flow — here is how a Responses call travels from your app through the SDK to the model and back.
sequenceDiagram
participant App as "Your app"
participant SDK as "openai SDK"
participant API as "OpenAI Responses API"
App->>SDK: "client.responses.create - gpt-5.6"
SDK->>API: "send input with OPENAI_API_KEY"
API->>API: "run model on input"
API-->>SDK: "response with output items"
SDK-->>App: "response.output_text"
Node.js / TypeScript
npm install openai # v7.x — requires Node >= 22, zod ^3.25 || ^4
import OpenAI from "openai";
const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
// Responses API (recommended default)
const response = await client.responses.create({
model: "gpt-5.6",
instructions: "You are a TypeScript expert.",
input: "Convert this class to use composition over inheritance.",
});
console.log(response.output_text);
Chat Completions is still supported if you need that shape (e.g. cross-provider code via the Vercel AI SDK):
const completion = await client.chat.completions.create({
model: "gpt-5.6",
messages: [
{ role: "system", content: "You are a TypeScript expert." },
{ role: "user", content: "Convert this class to use composition over inheritance." },
],
});
console.log(completion.choices[0].message.content);
Python
pip install openai # v3.x — requires Python >= 3.10
from openai import OpenAI
client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
response = client.responses.create(
model="gpt-5.6",
input="Generate unit tests for this function.",
)
print(response.output_text)
Structured output & function calling
This is the reason OpenAI earns its place as the secondary provider. When you need the model to return data your code can trust — not prose you have to regex — reach for Structured Outputs. With a strict JSON schema the model is guaranteed to emit JSON that matches, and the SDK hands you a fully typed object — no JSON.parse, no validation boilerplate. Structured Outputs work on GPT-4o (2024-08-06+) and every GPT-5 model, so with gpt-5.6 the old snapshot caveat is a non-issue.
Here is the flow: you define a Zod schema, the SDK ships it as a strict JSON schema, the model is constrained to match, and you get a typed object back.
flowchart TD
A["Zod schema in your app"] --> B["zodTextFormat<br/>(Responses API)"]
B --> C["Strict JSON schema<br/>sent to model"]
C --> D["Model constrained<br/>to schema"]
D --> E["response.output_parsed<br/>typed object"]
(a) Structured output — Responses API (recommended)
Use client.responses.parse() with zodTextFormat() from openai/helpers/zod. The parsed object arrives on response.output_parsed, typed as z.infer<typeof Schema>.
import OpenAI from "openai";
import { zodTextFormat } from "openai/helpers/zod";
import { z } from "zod";
const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
// e.g. extract structured order data from a free-text M-Pesa SMS
const OrderExtraction = z.object({
amount_kes: z.number().int(), // Daraja amounts are integer KES
phone: z.string(), // normalise to 254XXXXXXXXX downstream
reference: z.string(),
confidence: z.enum(["high", "medium", "low"]),
});
const response = await client.responses.parse({
model: "gpt-5.6",
input: [
{ role: "system", content: "Extract the payment fields from the message." },
{ role: "user", content: "Got KES 1500 from 0712345678 ref INV-204" },
],
text: { format: zodTextFormat(OrderExtraction, "order_extraction") },
});
const order = response.output_parsed;
if (order) {
console.log(order.amount_kes, order.phone, order.confidence); // fully typed
}
(b) Tool / function call — Responses API
Declare function tools with the flat Responses shape (type: "function" at the top level, strict: true). The model returns function_call items in response.output; each carries name, a call_id, and arguments as a JSON string.
import OpenAI from "openai";
const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
const response = await client.responses.create({
model: "gpt-5.6",
input: "Charge 0712345678 KES 500 for order INV-9",
tools: [
{
type: "function",
name: "initiate_stk_push",
description: "Start an M-Pesa STK push.",
strict: true,
parameters: {
type: "object",
additionalProperties: false,
required: ["amount_kes", "phone", "account_ref"],
properties: {
amount_kes: { type: "integer" },
phone: { type: "string" },
account_ref: { type: "string" },
},
},
},
],
});
for (const item of response.output) {
if (item.type === "function_call" && item.name === "initiate_stk_push") {
const args = JSON.parse(item.arguments) as {
amount_kes: number; phone: string; account_ref: string;
};
// now call your real lib/mpesa-stk.ts with typed, schema-validated args
console.log(args.amount_kes, args.phone, args.account_ref);
}
}
(c) Chat Completions equivalent (still supported)
If you're on the Chat Completions shape, the helpers are zodResponseFormat() (whole-response schema) and zodFunction() (tool arguments) via client.chat.completions.parse(); results land on choices[0].message.parsed and tool_calls[].function.parsed_arguments. See examples/structured-output.ts.
Gotcha: every field in a strict schema is required by default. To make a field optional, model it as
z.union([T, z.null()])(nullable) rather than.optional()— strict mode does not allow omitted keys — and keepadditionalProperties: false.
Environment Variables
# Required
OPENAI_API_KEY=sk-proj-...
# Optional
OPENAI_ORG_ID=org-...
OPENAI_PROJECT_ID=proj_...
OPENAI_BASE_URL=https://api.openai.com/v1 # default; change for Azure OpenAI
Store in .env and load with dotenv or python-dotenv. The key is server-side only — never ship it to the browser.
Automation Workflows
Dual-Provider Review Hook
Use a Claude Code Stop hook to send the session summary to OpenAI for a cross-model review:
.claude/settings.json:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "node scripts/openai-second-opinion.js"
}
]
}
]
}
}
scripts/openai-second-opinion.js:
import OpenAI from "openai";
import { readFileSync } from "fs";
const client = new OpenAI();
const sessionLog = readFileSync(".claude/session.log", "utf-8");
const result = await client.responses.create({
model: "gpt-5.6",
instructions: "Review this Claude Code session for potential issues.",
input: sessionLog,
});
console.log("GPT-5.6 review:", result.output_text);
Slash Command: Route to GPT-5.6
.claude/commands/gpt.md:
Use the Bash tool to call OpenAI's Responses API with this prompt: $ARGUMENTS
Command:
```bash
curl https://api.openai.com/v1/responses \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"gpt-5.6","input":"'"$ARGUMENTS"'"}'
Usage: `/project:gpt "What are the tradeoffs of this architecture?"`
### CI/CD: OpenAI Code Quality Gate
```yaml
# .github/workflows/ai-review.yml
name: AI Code Review
on: [pull_request]
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Get diff
run: git diff origin/main...HEAD > diff.txt
- name: OpenAI review
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
run: |
pip install openai
python scripts/review.py diff.txt
Common Use Cases
| Use Case | Approach |
|---|---|
| Vision / image understanding | Pass image inputs to gpt-5.6 via the Responses API (input_image) |
| Embeddings for code search | text-embedding-3-small on your codebase |
| Fine-tuning for style | Fine-tune a small model (e.g. gpt-5.6-luna) on your code patterns |
| Structured extraction | responses.parse + zodTextFormat on gpt-5.6-terra / luna |
| Cross-model validation | Claude drafts, GPT-5.6 validates |
Troubleshooting
| Issue | Fix |
|---|---|
| 401 Unauthorized | Check OPENAI_API_KEY value and project permissions |
| Model not available | Check model access at developers.openai.com/api/docs/models and your project tier |
output_parsed is null |
The model refused or the schema was invalid — inspect response.output for a refusal item |
| Strict-schema error | Every property must be in required; use z.null() unions for optional fields, additionalProperties: false |
| Rate limits | Use exponential backoff; check tier limits at platform.openai.com/settings/limits |
| Azure OpenAI endpoint | Set OPENAI_BASE_URL=https://<resource>.openai.azure.com |
Official docs:
- https://developers.openai.com/api/reference/overview
- https://developers.openai.com/api/docs/models
- https://developers.openai.com/api/docs/guides/structured-outputs
- https://developers.openai.com/api/docs/guides/migrate-to-responses
- https://developers.openai.com/api/docs/guides/tools-connectors-mcp
- https://github.com/openai/openai-node
- https://github.com/openai/openai-python