The complete OpenAI API surface as Model Context Protocol tools for Claude Code, Cursor, Windsurf, and any MCP client.
A single stdio MCP server that exposes 42 tools covering every major OpenAI endpoint: Responses, Chat Completions, Images (gpt-image-1.5), Audio (TTS + transcription + translation), Embeddings, Moderation, Files, Batch, Vector Stores, Fine-tuning, and multipart Large Uploads.
- Responses API (primary): reasoning, built-in tools (
web_search,file_search,code_interpreter,image_generation), background mode, stored responses, input-item retrieval. - Chat Completions: legacy endpoint with auto-routing between reasoning-model params (
reasoning_effort,max_completion_tokens) and standard params. - Images:
gpt-image-1.5generate + edit with mask, save-to-disk, base64 streaming, input fidelity. - Audio:
gpt-4o-mini-ttstext-to-speech,gpt-4o-transcribe+whisper-1transcription with word/segment timestamps, English translation. - Embeddings:
text-embedding-3-largeat full 3072 dimensions. - Files: upload, list, retrieve, download, delete (gated).
- Batch: 50%-cost async jobs for Responses/Chat/Embeddings/Moderation/Images.
- Vector Stores: create, list, retrieve, attach files (single + batch), search, delete.
- Fine-tuning: create jobs, list, retrieve, cancel, events, checkpoints.
- Large Uploads: multipart streaming up to 8 GB in 64 MB chunks with MD5 integrity.
- Max-capability defaults:
reasoning_effort=high,verbosity=high, image quality high, 1024x1536 portrait, per-model auto-fill formax_output_tokens. - Security built-in: path deny-list, write-basename deny, hosted-tool block (
mcp/shell/computer_use), destructive-action gate, 401 cache invalidation, SSRF guard on image URLs.
npm install -g openai-mcp-serverBinary openai-mcp-server is placed on your PATH.
git clone https://github.com/salviz/openai-mcp-server.git
cd openai-mcp-server
npm install
node index.js # sanity check — it will wait for an MCP client on stdioRequires Node.js >= 20. Works on Linux, macOS, Windows (WSL), and Android/Termux.
The server resolves your OpenAI API key from three sources, in this order — the first match wins:
export OPENAI_API_KEY="sk-proj-..."
openai-mcp-serverecho -n "sk-proj-..." > ~/.config/openai/api_key
chmod 600 ~/.config/openai/api_key
export OPENAI_API_KEY_FILE="~/.config/openai/api_key"
openai-mcp-serverPath is ~-expanded. Only the first line is read, whitespace trimmed.
If neither of the above is set and gcloud is available, the server will attempt:
gcloud secrets versions access latest \
--secret="$OPENAI_MCP_GCP_SECRET" \
--project="$OPENAI_MCP_GCP_PROJECT"Set the project and secret name yourself:
export OPENAI_MCP_GCP_PROJECT="my-gcp-project"
export OPENAI_MCP_GCP_SECRET="openai-api-key" # default if unset
export OPENAI_MCP_GCLOUD_BIN="/usr/bin/gcloud" # override if non-standard
openai-mcp-serverThe gcloud subprocess runs with a scrubbed environment (no inherited PATH tricks).
Add to ~/.claude.json (user scope) or ~/.claude/settings.json under mcpServers:
{
"mcpServers": {
"openai": {
"command": "openai-mcp-server",
"env": {
"OPENAI_API_KEY": "sk-proj-..."
}
}
}
}If installed from source, point at the absolute path:
{
"mcpServers": {
"openai": {
"command": "node",
"args": ["~/openai-mcp-server/index.js"],
"env": { "OPENAI_API_KEY": "sk-proj-..." }
}
}
}Restart Claude Code. Tools appear as mcp__openai__openai_responses, etc.
{
"mcpServers": {
"openai": {
"command": "openai-mcp-server",
"env": { "OPENAI_API_KEY": "sk-proj-..." }
}
}
}{
"mcpServers": {
"openai": {
"command": "openai-mcp-server",
"env": { "OPENAI_API_KEY": "sk-proj-..." }
}
}
}Any MCP stdio client works — the server speaks standard MCP over stdin/stdout.
| Tool | Description |
|---|---|
openai_responses |
Primary endpoint — create a Response with text, images, files, reasoning effort, and built-in tools. Hosted mcp/shell/computer_use types blocked by default. Auto-fills max_output_tokens per model family. |
openai_response_get |
Retrieve a stored Response by id. |
openai_response_cancel |
Cancel a background Response. |
openai_response_delete |
Delete a stored Response. Gated by OPENAI_MCP_ALLOW_DESTRUCTIVE=1. |
openai_response_input_items |
List input items of a stored Response. |
| Tool | Description |
|---|---|
openai_chat |
Chat Completions endpoint. Prefer openai_responses for new models and built-in tools. |
| Tool | Description |
|---|---|
openai_image_generate |
Generate images. Default gpt-image-1.5 at quality=high, 1024x1536. Save to local path or return base64. |
openai_image_edit |
Edit image(s) with optional mask. Reference-based edit via gpt-image-1.5. |
| Tool | Description |
|---|---|
openai_tts |
Text-to-speech. Default gpt-4o-mini-tts, voice nova, mp3. Writes audio file to save_to. |
openai_transcribe |
Speech-to-text. Default gpt-4o-transcribe (json). Use whisper-1 for verbose_json + word/segment timestamps. |
openai_translate_audio |
Translate audio into English (Whisper translation endpoint). |
| Tool | Description |
|---|---|
openai_file_upload |
Upload local file (up to 500 MB). Returns file_id for Responses/Chat/Assistants/Batch. |
openai_file_list |
List uploaded files, optionally filtered by purpose. |
openai_file_get |
Retrieve metadata for a single file. |
openai_file_delete |
Delete an uploaded file. Gated by OPENAI_MCP_ALLOW_DESTRUCTIVE=1. |
openai_file_content |
Download raw file content to a local path. |
| Tool | Description |
|---|---|
openai_batch_create |
Create a batch job (50% cost, async). Supports Responses, Chat, Completions, Embeddings, Moderations, Images, Videos endpoints. |
openai_batch_retrieve |
Retrieve a batch job by id. |
openai_batch_cancel |
Cancel a batch job. |
openai_batch_list |
List batch jobs. |
| Tool | Description |
|---|---|
openai_vector_store_create |
Create a vector store (used by file_search tool). |
openai_vector_store_list |
List vector stores. |
openai_vector_store_retrieve |
Retrieve a vector store. |
openai_vector_store_delete |
Delete a vector store. Gated by OPENAI_MCP_ALLOW_DESTRUCTIVE=1. |
openai_vector_store_file_add |
Attach an uploaded file to a vector store. |
openai_vector_store_file_list |
List files attached to a vector store. |
openai_vector_store_file_remove |
Detach a file. Gated by OPENAI_MCP_ALLOW_DESTRUCTIVE=1. |
openai_vector_store_file_batch_create |
Bulk-attach multiple files in one batch. |
openai_vector_store_search |
Search a vector store by query. |
| Tool | Description |
|---|---|
openai_fine_tune_create |
Create a fine-tuning job from an uploaded JSONL training file. |
openai_fine_tune_retrieve |
Retrieve a fine-tuning job. |
openai_fine_tune_list |
List fine-tuning jobs. |
openai_fine_tune_cancel |
Cancel a fine-tuning job. |
openai_fine_tune_events |
List events (progress stream) for a job. |
openai_fine_tune_list_checkpoints |
List intermediate checkpoint model ids. |
| Tool | Description |
|---|---|
openai_upload_large_file |
Multipart upload up to 8 GB. Streams 64 MB parts with incremental MD5. |
openai_upload_cancel |
Cancel an in-progress Upload. |
| Tool | Description |
|---|---|
openai_embed |
Create embeddings. Default text-embedding-3-large at 3072 dims. |
openai_moderation |
Run a moderation check via omni-moderation-latest (multimodal). |
openai_list_models |
List all models on the account (dynamic, always current). |
openai_model_retrieve |
Retrieve metadata for a single model. |
openai_ping |
Health check — loads the key and returns redacted fingerprint + visibility probe. |
Total: 42 tools.
| Variable | Purpose | Default |
|---|---|---|
OPENAI_API_KEY |
Primary API key source. | — |
OPENAI_API_KEY_FILE |
Read key from a file path (~ expanded). |
— |
OPENAI_ORG_ID |
Organization id for the OpenAI client. | — |
OPENAI_PROJECT_ID |
Project id for the OpenAI client. | — |
OPENAI_BASE_URL |
Override API base URL (proxies, Azure-compatible gateways). | https://api.openai.com/v1 |
OPENAI_MCP_GCP_PROJECT |
GCP project for Secret Manager fallback. | — |
OPENAI_MCP_GCP_SECRET |
Secret name for Secret Manager fallback. | openai-api-key |
OPENAI_MCP_GCLOUD_BIN |
Absolute path to the gcloud binary. |
— |
OPENAI_MCP_ALLOW_PATHS |
Colon-separated paths to ALLOW (overrides deny-list). | — |
OPENAI_MCP_DENY_PATHS |
Colon-separated extra paths to DENY. | — |
OPENAI_MCP_MAX_FILE_BYTES |
Max bytes for inline file reads (images, attachments). | 52428800 (50 MB) |
OPENAI_MCP_MAX_AUDIO_BYTES |
Max bytes for audio transcription uploads. | 209715200 (200 MB) |
OPENAI_MCP_ALLOW_HOSTED_TOOLS |
1 to permit hosted mcp/shell/computer_use tool types. |
0 |
OPENAI_MCP_ALLOW_DESTRUCTIVE |
1 to permit file/response/vector-store deletion tools. |
0 |
OPENAI_MCP_TIMEOUT_MS |
Per-request timeout for the OpenAI SDK. | 600000 (10 min) |
This server is designed to be safe by default when connected to an LLM that can issue arbitrary tool calls on your behalf.
All tools that read or write local paths (image_paths, file_path, save_to, etc.) route through a resolver that:
- Expands
~to the current user's home. - Calls
realpathSyncso symlink escapes don't bypass the deny-list. - Blocks the following by default (targets a prompt-injected LLM exfiltrating secrets):
~/.ssh,~/.gnupg,~/.aws,~/.config/gcloud,~/.docker,~/.kube~/.claude,~/.claude.json~/.bash_history,~/.zsh_history,~/.netrc,~/.npmrc,~/.pypirc,~/.git-credentials/etc/shadow,/etc/gshadow,/proc- Any path you add via
OPENAI_MCP_DENY_PATHS
- Allow-list is additive: anything under
OPENAI_MCP_ALLOW_PATHSis permitted even if it would otherwise be denied.
Even outside the deny-list, writes to these shell-init / auth basenames are blocked:
.bashrc .bash_profile .bash_login .profile .zshrc .zprofile .zlogin
.zshenv .kshrc .cshrc .tcshrc .inputrc .termux.properties
authorized_keys known_hosts .vimrc .viminfo .config
This prevents an LLM from overwriting your shell rc files with prompt-injected content, regardless of where in the filesystem the file lives.
The Responses API lets you pass hosted tool types like mcp, shell, and computer_use. These let remote infrastructure (OpenAI's sandbox, or a third-party MCP endpoint) execute actions. We block these by default:
mcp— would let a prompt-injected LLM register a malicious MCP server mid-call.shell— arbitrary remote shell execution.computer_use— screen control.
Set OPENAI_MCP_ALLOW_HOSTED_TOOLS=1 only when you explicitly trust the tool payload source.
Deletion tools (openai_file_delete, openai_response_delete, openai_vector_store_delete, openai_vector_store_file_remove) are disabled unless OPENAI_MCP_ALLOW_DESTRUCTIVE=1. This prevents a prompt-injected "please clean up your files" instruction from wiping your dataset, cached responses, or vector store corpus.
The OpenAI client is cached across calls for speed. On any HTTP 401, the cached client is invalidated so a rotated key takes effect immediately without restarting the server.
image_urls are validated before being forwarded:
- Only
http:/https:schemes. - Rejects
localhost,127.0.0.1,0.0.0.0,::1,metadata.google.internal. - Rejects RFC1918 (
10.x,192.168.x,172.16.x-172.31.x), link-local (169.254.x,fe80::), and unique-local (fc00::/7).
Prevents an LLM from coaxing the server into fetching cloud metadata or your internal network on its behalf.
| Knob | Default |
|---|---|
| Text model | gpt-5.4 |
reasoning_effort |
high |
verbosity |
high |
reasoning_summary |
auto |
| Image model | gpt-image-1.5 |
| Image size | 1024x1536 |
| Image quality | high |
| TTS model | gpt-4o-mini-tts |
| TTS voice | nova |
| Transcription model | gpt-4o-transcribe |
| Embedding model | text-embedding-3-large @ 3072 dims |
| Moderation model | omni-moderation-latest |
| Per-request timeout | 10 minutes |
Per-model max_output_tokens |
gpt-5.* 128K, o-series 100K, gpt-4.1* 32K, gpt-4o* 16K, gpt-4* 8K |
Model availability varies by account and date. Run openai_list_models to see what your key actually has access to before wiring a default.
All examples show the JSON input the LLM passes to the tool.
{
"tool": "openai_responses",
"input": {
"input": "Explain the CAP theorem to a junior engineer in three paragraphs.",
"reasoning_effort": "high"
}
}{
"tool": "openai_responses",
"input": {
"input": "Summarize the key findings and list every statistical test used.",
"file_paths": ["~/papers/clinical-trial.pdf"],
"reasoning_effort": "high",
"verbosity": "high"
}
}{
"tool": "openai_image_generate",
"input": {
"prompt": "A minimalist watercolor illustration of a lighthouse at dawn.",
"size": "1024x1536",
"quality": "high",
"save_to": "~/images/lighthouse.png"
}
}Kick off a long job:
{
"tool": "openai_responses",
"input": {
"input": "Produce a 20-page technical design doc for a multi-region Kafka deployment.",
"reasoning_effort": "high",
"background": true,
"store": true
}
}Then poll:
{
"tool": "openai_response_get",
"input": { "response_id": "resp_abc123", "response_format": "text" }
}{
"tool": "openai_upload_large_file",
"input": {
"path": "~/datasets/corpus.jsonl",
"purpose": "fine-tune"
}
}{
"tool": "openai_fine_tune_create",
"input": {
"model": "gpt-4.1-nano",
"training_file": "file-xyz789",
"suffix": "my-domain-v1",
"method": { "type": "supervised" }
}
}{
"tool": "openai_embed",
"input": {
"input": ["first doc", "second doc", "third doc"],
"dimensions": 3072,
"include_vectors": false
}
}None of the three resolution paths returned a key. Verify with:
echo "$OPENAI_API_KEY" | head -c 10
ls -la "$OPENAI_API_KEY_FILE"
gcloud secrets versions access latest --secret="$OPENAI_MCP_GCP_SECRET" --project="$OPENAI_MCP_GCP_PROJECT"You're trying to read or write a protected location. Either move the file, or whitelist its parent directory:
export OPENAI_MCP_ALLOW_PATHS="/home/me/workdir:/tmp/safe"You passed tools: [{type: "mcp"}] (or shell / computer_use). Set OPENAI_MCP_ALLOW_HOSTED_TOOLS=1 only if you trust the payload.
Set OPENAI_MCP_ALLOW_DESTRUCTIVE=1 to enable deletion tools.
Raise OPENAI_MCP_MAX_FILE_BYTES (inline attachments) or OPENAI_MCP_MAX_AUDIO_BYTES (transcription), or use openai_upload_large_file for files up to 8 GB.
The model returned an error state. The tool response is marked isError: true. Switch to response_format: "json" to see the full error payload, or call openai_response_get with the id.
Your key is invalid or rotated. The server will re-load on next call once 401 is observed — you don't need to restart it.
Reasoning models (gpt-5.*, o*) use max_completion_tokens, not max_tokens. The server's openai_chat tool routes this automatically based on the model id.
Pull requests welcome.
- Fork the repo on GitHub.
- Create a branch:
git checkout -b fix/my-issue. - Syntax-check every file you touch:
node --check tools/your_file.js. - Manually smoke-test via an MCP client before pushing.
- Open a PR against
mainwith a description of what changed and why.
When adding a tool, follow the existing pattern in tools/*.js:
- Register via
server.tool(name, description, zodSchema, handler). - Validate any
tools/messagesarrays throughvalidateTools/validateInputMessagesto keep the hosted-tool block in force. - Route writes through
resolvePath(path, { forWrite: true }). - Use
ok,fail,asJsonfromclient.jsfor consistent response shape.
MIT. See LICENSE.