Content
# OpenAI MCP Server
**The complete OpenAI API surface as Model Context Protocol tools for Claude Code, Cursor, Windsurf, and any MCP client.**
[](https://www.npmjs.com/package/openai-mcp-server)
[](https://opensource.org/licenses/MIT)
[](https://nodejs.org)
[](https://modelcontextprotocol.io)
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.
## Features
- **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.5` generate + edit with mask, save-to-disk, base64 streaming, input fidelity.
- **Audio**: `gpt-4o-mini-tts` text-to-speech, `gpt-4o-transcribe` + `whisper-1` transcription with word/segment timestamps, English translation.
- **Embeddings**: `text-embedding-3-large` at 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 for `max_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.
## Installation
### From npm
```bash
npm install -g openai-mcp-server
```
Binary `openai-mcp-server` is placed on your `PATH`.
### From source
```bash
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 stdio
```
Requires **Node.js >= 20**. Works on Linux, macOS, Windows (WSL), and Android/Termux.
## API key
The server resolves your OpenAI API key from three sources, in this order — the first match wins:
### 1. Environment variable (simplest)
```bash
export OPENAI_API_KEY="sk-proj-..."
openai-mcp-server
```
### 2. File reference (good for secret managers, 1Password CLI, `pass`, etc.)
```bash
echo -n "sk-proj-..." > ~/.config/openai/api_key
chmod 600 ~/.config/openai/api_key
export OPENAI_API_KEY_FILE="~/.config/openai/api_key"
openai-mcp-server
```
Path is `~`-expanded. Only the first line is read, whitespace trimmed.
### 3. Google Cloud Secret Manager fallback
If neither of the above is set and `gcloud` is available, the server will attempt:
```bash
gcloud secrets versions access latest \
--secret="$OPENAI_MCP_GCP_SECRET" \
--project="$OPENAI_MCP_GCP_PROJECT"
```
Set the project and secret name yourself:
```bash
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-server
```
The gcloud subprocess runs with a scrubbed environment (no inherited `PATH` tricks).
## Register with Claude Code
Add to `~/.claude.json` (user scope) or `~/.claude/settings.json` under `mcpServers`:
```json
{
"mcpServers": {
"openai": {
"command": "openai-mcp-server",
"env": {
"OPENAI_API_KEY": "sk-proj-..."
}
}
}
}
```
If installed from source, point at the absolute path:
```json
{
"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.
## Register with Cursor / Windsurf / other MCP clients
### Cursor (`~/.cursor/mcp.json`)
```json
{
"mcpServers": {
"openai": {
"command": "openai-mcp-server",
"env": { "OPENAI_API_KEY": "sk-proj-..." }
}
}
}
```
### Windsurf (`~/.codeium/windsurf/mcp_config.json`)
```json
{
"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 catalog
### Responses (5)
| 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. |
### Chat (1)
| Tool | Description |
|------|-------------|
| `openai_chat` | Chat Completions endpoint. Prefer `openai_responses` for new models and built-in tools. |
### Images (2)
| 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`. |
### Audio (3)
| 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). |
### Files (5)
| 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. |
### Batch (4)
| 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. |
### Vector Stores (9)
| 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. |
### Fine-tuning (6)
| 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. |
### Large Uploads (2)
| 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. |
### Embeddings, Moderation, Models, Health (5)
| 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.**
## Environment variables
| 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) |
## Security model
This server is designed to be safe by default when connected to an LLM that can issue arbitrary tool calls on your behalf.
### Path deny-list
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 `realpathSync` so 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_PATHS` is permitted even if it would otherwise be denied.
### Write-basename deny
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.
### Hosted-tool block
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.
### Destructive-action gate
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.
### 401 cache invalidation
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.
### SSRF guard
`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.
## Defaults
| 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.
## Examples
All examples show the JSON `input` the LLM passes to the tool.
### 1. Quick reasoning call
```json
{
"tool": "openai_responses",
"input": {
"input": "Explain the CAP theorem to a junior engineer in three paragraphs.",
"reasoning_effort": "high"
}
}
```
### 2. Analyze a local PDF
```json
{
"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"
}
}
```
### 3. Generate an image to disk
```json
{
"tool": "openai_image_generate",
"input": {
"prompt": "A minimalist watercolor illustration of a lighthouse at dawn.",
"size": "1024x1536",
"quality": "high",
"save_to": "~/images/lighthouse.png"
}
}
```
### 4. Background reasoning + poll
Kick off a long job:
```json
{
"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:
```json
{
"tool": "openai_response_get",
"input": { "response_id": "resp_abc123", "response_format": "text" }
}
```
### 5. Upload a large dataset and fine-tune
```json
{
"tool": "openai_upload_large_file",
"input": {
"path": "~/datasets/corpus.jsonl",
"purpose": "fine-tune"
}
}
```
```json
{
"tool": "openai_fine_tune_create",
"input": {
"model": "gpt-4.1-nano",
"training_file": "file-xyz789",
"suffix": "my-domain-v1",
"method": { "type": "supervised" }
}
}
```
### 6. Embed a batch of documents
```json
{
"tool": "openai_embed",
"input": {
"input": ["first doc", "second doc", "third doc"],
"dimensions": 3072,
"include_vectors": false
}
}
```
## Troubleshooting
### "OpenAI API key not found"
None of the three resolution paths returned a key. Verify with:
```bash
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"
```
### "Path is on the deny-list"
You're trying to read or write a protected location. Either move the file, or whitelist its parent directory:
```bash
export OPENAI_MCP_ALLOW_PATHS="/home/me/workdir:/tmp/safe"
```
### "Hosted tool type ... is disabled by default"
You passed `tools: [{type: "mcp"}]` (or `shell` / `computer_use`). Set `OPENAI_MCP_ALLOW_HOSTED_TOOLS=1` only if you trust the payload.
### "Destructive actions disabled"
Set `OPENAI_MCP_ALLOW_DESTRUCTIVE=1` to enable deletion tools.
### "File is N bytes; exceeds ... cap"
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.
### `status=failed` in `openai_responses` output
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.
### HTTP 401 on every call
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 model ignores `max_tokens`
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.
## Contributing
Pull requests welcome.
1. Fork the repo on GitHub.
2. Create a branch: `git checkout -b fix/my-issue`.
3. Syntax-check every file you touch: `node --check tools/your_file.js`.
4. Manually smoke-test via an MCP client before pushing.
5. Open a PR against `main` with 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`/`messages` arrays through `validateTools` / `validateInputMessages` to keep the hosted-tool block in force.
- Route writes through `resolvePath(path, { forWrite: true })`.
- Use `ok`, `fail`, `asJson` from `client.js` for consistent response shape.
## License
MIT. See `LICENSE`.
MCP Config
Below is the configuration for this MCP Server. You can copy it directly to Cursor or other MCP clients.
mcp.json
Connection Info
You Might Also Like
No recommendations available