Skip to content

OpenAI SDKs

Point the official OpenAI client at the gateway with your key to use its supported Chat Completions and stateless Responses subset. The Model gateway defines compatibility; SDK capabilities outside that contract remain unsupported.

Prerequisites and setup

Complete Your first governed request: use its scratch directory, SDK installation, and MISSION_CONTROL_URL, MISSION_CONTROL_API_KEY, and MISSION_CONTROL_MODEL environment variables. The TypeScript SDK is pinned to openai@7.10.0; the Python integration uses openai>=2.45,<3. Examples require the capabilities they exercise on the discovered virtual model. An SDK accepting a field does not mean the gateway supports it.

For each recipe below, create a separate file by copying the appropriate setup followed by that recipe's block. Save Python as recipe.py and run python recipe.py in the activated virtual environment. Save TypeScript as recipe.mts and run node recipe.mts with Node.js 24. Do not concatenate the recipes: some intentionally reuse variable names.

import os
from openai import OpenAI

model = os.environ["MISSION_CONTROL_MODEL"]
client = OpenAI(
    base_url=f"{os.environ['MISSION_CONTROL_URL'].rstrip('/')}/v1",
    api_key=os.environ["MISSION_CONTROL_API_KEY"],
    max_retries=0,
)
import OpenAI from "openai";

const origin = process.env.MISSION_CONTROL_URL;
const key = process.env.MISSION_CONTROL_API_KEY;
const model = process.env.MISSION_CONTROL_MODEL;
if (!origin || !key || !model) throw new Error("Set all three MISSION_CONTROL variables.");
const client = new OpenAI({
  baseURL: `${origin.replace(/\/$/, "")}/v1`, apiKey: key, maxRetries: 0,
});

The model is a virtual model ID from GET /v1/models. No restart, new key, or server configuration change is needed to change a request to an already enabled model your identity may invoke. For response headers and Trace verification, use the complete first request recipe.

The recipes explicitly bound output at 128 tokens. Reusable external SDK clients add no global bound; choose a supported finite per-call bound under finite spend caps. Each physical provider attempt is reserved/settled separately.

Streaming

stream = client.chat.completions.create(
    model=model,
    messages=[{"role": "user", "content": "Summarize this case."}],
    max_completion_tokens=128,
    stream=True,
    stream_options={"include_usage": True},
)
for chunk in stream:
    if chunk.choices and chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="")
const stream = await client.chat.completions.create({
  model,
  messages: [{ role: "user", content: "Summarize this case." }],
  max_completion_tokens: 128,
  stream: true,
  stream_options: { include_usage: true },
});
for await (const chunk of stream) {
  process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}

Chat Completions streams standard SSE chunks and [DONE]; Responses streams named Responses events. The exact stream_options: {"include_usage": true} object is accepted; any other stream option is rejected. Usage is recorded before the completed event is exposed, so what you see in Audit → Decisions matches what the stream reported.

Function tools

completion = client.chat.completions.create(
    model=model,
    messages=[{"role": "user", "content": "Look up customer 42."}],
    max_completion_tokens=128,
    tools=[
        {
            "type": "function",
            "function": {
                "name": "lookup_customer",
                "parameters": {
                    "type": "object",
                    "properties": {"query": {"type": "string"}},
                    "required": ["query"],
                },
            },
        }
    ],
    tool_choice="auto",
)
for call in completion.choices[0].message.tool_calls or []:
    if call.type == "function":
        print(call.function.name, call.function.arguments)
const completion = await client.chat.completions.create({
  model,
  messages: [{ role: "user", content: "Look up customer 42." }],
  max_completion_tokens: 128,
  tools: [
    {
      type: "function",
      function: {
        name: "lookup_customer",
        parameters: {
          type: "object",
          properties: { query: { type: "string" } },
          required: ["query"],
        },
      },
    },
  ],
  tool_choice: "auto",
});
const call = completion.choices[0].message.tool_calls?.[0];
if (call?.type === "function") console.log(call.function.name, call.function.arguments);

Function tools are yours to execute: the model returns a call, your code runs it and sends the result back as a tool message. To give the model Mission Control's governed MCP tools as well, opt in on the request as shown next; to call them from your own code, use the MCP TypeScript client or the raw /mcp recipe in Your first MCP tool. tool_choice without tools fails with invalid_request.

JSON-schema output

Use a discovered model with structured-output capability. This requests a JSON object with one required greeting field; inspect the returned content and handle any refusal or incomplete answer before parsing it in application code.

completion = client.chat.completions.create(
    model=model,
    messages=[{"role": "user", "content": "Return a short greeting."}],
    max_completion_tokens=128,
    response_format={
        "type": "json_schema",
        "json_schema": {
            "name": "greeting", "strict": True,
            "schema": {
                "type": "object",
                "properties": {"greeting": {"type": "string"}},
                "required": ["greeting"], "additionalProperties": False,
            },
        },
    },
)
print(completion.choices[0].message.content)
const completion = await client.chat.completions.create({
  model,
  messages: [{ role: "user", content: "Return a short greeting." }],
  max_completion_tokens: 128,
  response_format: {
    type: "json_schema",
    json_schema: {
      name: "greeting", strict: true,
      schema: {
        type: "object", properties: { greeting: { type: "string" } },
        required: ["greeting"], additionalProperties: false,
      },
    },
  },
});
console.log(completion.choices[0].message.content);

Governed tools inside a model call

Follow Use governed tools in a model request for complete opt-in recipes, fixture requirements, automatic execution, pending calls, and continuation. Governed execution requires both model and tool permission. The Model gateway remains the compatibility contract.

The Responses subset

response = client.responses.create(
    model=model,
    instructions="Be concise.",
    input="Summarize this case.",
    max_output_tokens=128,
    store=False,
)
print(response.output_text)
const response = await client.responses.create({
  model,
  instructions: "Be concise.",
  input: "Summarize this case.",
  max_output_tokens: 128,
  store: false,
});
console.log(response.output_text);

Responses is stateless here: store: false is the only accepted value, and previous_response_id, hosted tools and background execution are rejected rather than approximated.

Expected result and verification

Streaming prints text chunks; a function-tool request may return tool calls or ordinary text; a Responses request prints output_text. Function tools in these recipes are declarations only: no local function executes. Inspect calls before executing your application's functions and return their output as tool messages.

Use Observe → Requests and Observe → Traces with observability.read to inspect requests and their decisions. A new trace is generated for each request unless you supply valid W3C context. See Tracing.

Supported fields

Route Supported Rejected with unsupported_feature
POST /v1/chat/completions model, messages, function tools, tool_choice, text or JSON-schema response_format, bounded string metadata, stream, stream_options: {"include_usage": true}, store: false, temperature, one of max_tokens or max_completion_tokens; governed tools through the x-mc-tools, x-mc-auto-execute and x-mc-max-tool-rounds headers Hosted or stateful fields, other stream options, store: true, provider-specific fields, stream with governed tools
POST /v1/responses model, instructions, input, function tools plus one mcp tool object with server_label: "mission-control", tool_choice, text format, bounded string metadata, stream, store: false, temperature, max_output_tokens previous_response_id, other hosted tools or MCP servers, background execution, store: true, stream with governed tools

Only the fields you send reach the provider; the gateway adds no defaults of its own. Whether a deployment accepts metadata or store depends on its vendor profile: a sent field the profile does not accept makes that deployment ineligible, and with no eligible deployment the call fails with capability_unavailable.

Errors

Model routes return the OpenAI-style envelope with a fixed message and a typed code; the SDKs raise their usual exceptions with the status.

{
  "error": {
    "message": "Model request failed",
    "type": "invalid_request_error",
    "param": null,
    "code": "unsupported_feature"
  }
}
Status Code What to do
400 invalid_request Correct the JSON, the model name, the W3C context, or a tool_choice sent without tools.
400 unsupported_feature Remove the hosted, stateful or provider-specific field; do not retry unchanged.
400 capability_unavailable No eligible deployment for the fields you sent; remove the field or ask an admin.
400 governed_tool_conflict One of your function tools has a governed tool's name; rename it.
401 authentication_failed Issue or rotate the key.
403 operation_denied Fix Team allowance, access or state.
429 spend_limit, request_rate_limit, token_rate_limit, concurrency_limit, provider_rate_limited Honor Retry-After or change the limit.
503 provider_unavailable, provider_account_rejected, service_unavailable Retry later; check the Trace before retrying a side effect.

The full table is on the Gateway errors page.