Codex Gemini MCP
**๐ Language: ํ๊ตญ์ด | [English](README.en.md)**
# codex-gemini-mcp
AI ์์ด์ ํธ(Claude, Cursor ๋ฑ)๊ฐ **OpenAI Codex CLI**์ **Google Gemini CLI**๋ฅผ MCP ๋๊ตฌ๋ก ์ง์ ํธ์ถํ ์ ์๊ฒ ํด์ฃผ๋ ํ๋ก์ ์๋ฒ์
๋๋ค.
## ์ฃผ์ ๊ธฐ๋ฅ
- **`ask_codex`** โ ์์ด์ ํธ๊ฐ Codex์๊ฒ ์ฝ๋ ์์ฑยท๋ฆฌํฉํฐ๋งยท๋๋ฒ๊น
์ ์์ฒญ
- **`ask_gemini`** โ ์์ด์ ํธ๊ฐ Gemini์๊ฒ ๋ถ์ยท์์ฝยท์ฝ๋ ๋ฆฌ๋ทฐ๋ฅผ ์์ฒญ
- **๋ฐฑ๊ทธ๋ผ์ด๋ ์คํ** โ ์ค๋ ๊ฑธ๋ฆฌ๋ ์์
์ ๋ฐฑ๊ทธ๋ผ์ด๋๋ก ๋๋ฆฌ๊ณ , ์ํ ํ์ธ(`check_job_status`)ยท๋๊ธฐ(`wait_for_job`)ยท์ค๋จ(`kill_job`)ยท๋ชฉ๋ก ์กฐํ(`list_jobs`)๋ก ๊ด๋ฆฌ
- **๋ฉํฐ๋ชจ๋ธ ์ค์ผ์คํธ๋ ์ด์
** โ ํ๋์ ์์ด์ ํธ๊ฐ Codex์ Gemini๋ฅผ ๋์์ ํ์ฉํ์ฌ ์์
๋ถ๋ด ๊ฐ๋ฅ
ํ๋์ ํจํค์ง์์ `codex-mcp`์ `gemini-mcp` ๋ ๊ฐ์ MCP ์๋ฒ ๋ฐ์ด๋๋ฆฌ๋ฅผ ์ ๊ณตํ๋ฉฐ, stdio transport ๊ธฐ๋ฐ์ผ๋ก ๋์ํฉ๋๋ค.
## Requirements
- Node.js 20+
- `codex` CLI ์ค์น (`npm i -g @openai/codex`)
- `gemini` CLI ์ค์น (`npm i -g @google/gemini-cli`)
MCP ์๋ฒ๋ ๊ฐ๊ฐ์ CLI๋ฅผ ๊ทธ๋๋ก ์คํํ๋ฏ๋ก, ๋จผ์ ๋ก์ปฌ ํฐ๋ฏธ๋์์ ๋ก๊ทธ์ธ/์ธ์ฆ์ด ์๋ฃ๋์ด `codex` / `gemini` CLI๋ฅผ ๋ฐ๋ก ์คํํ ์ ์๋ ์ํ์ธ์ง ํ์ธํ์ธ์.
## Install
npm์์ ์ค์น(๋ฐฐํฌ๋ ๊ฒฝ์ฐ):
```bash
npm i -g @donghae0414/codex-gemini-mcp
```
์ ์ญ ์ค์น ์์ด npx ์ฌ์ฉ:
```bash
npx -y -p @donghae0414/codex-gemini-mcp codex-mcp
npx -y -p @donghae0414/codex-gemini-mcp gemini-mcp
```
์์ค์์ ์ค์น(๊ฐ๋ฐ/ํ
์คํธ):
```bash
npm install
npm run build
npm link
```
## ํด๋ผ์ด์ธํธ๋ณ MCP ์ค์ ์์
์ ์ญ ์ค์น ๊ธฐ์ค:
```json
{
"mcpServers": {
"codex-mcp": {
"command": "codex-mcp",
"args": []
},
"gemini-mcp": {
"command": "gemini-mcp",
"args": []
}
}
}
```
์ ์ญ ์ค์น ์์ด npx ๊ธฐ์ค:
```json
{
"mcpServers": {
"codex-mcp": {
"command": "npx",
"args": ["-y", "-p", "@donghae0414/codex-gemini-mcp", "codex-mcp"]
},
"gemini-mcp": {
"command": "npx",
"args": ["-y", "-p", "@donghae0414/codex-gemini-mcp", "gemini-mcp"]
}
}
}
```
opencode (`opencode.json`):
```json
{
"mcp": {
"codex-mcp": {
"type": "local",
"command": ["npx", "-y", "-p", "@donghae0414/codex-gemini-mcp", "codex-mcp"]
},
"gemini-mcp": {
"type": "local",
"command": ["npx", "-y", "-p", "@donghae0414/codex-gemini-mcp", "gemini-mcp"]
}
}
}
```
ํด๋ผ์ด์ธํธ๋ณ ์ค์ ํ์ผ ์์น(์ฐธ๊ณ ):
- Claude Code: ํ๋ก์ ํธ ๋ฃจํธ `.mcp.json` (ํ๋ก์ ํธ๋ณ) ๋๋ `~/.claude.json` (์ ์ญ)
- Claude Desktop (macOS): `~/Library/Application Support/Claude/claude_desktop_config.json`
- Claude Desktop (Windows): `%APPDATA%\Claude\claude_desktop_config.json`
- Claude Desktop (Linux): `~/.config/Claude/claude_desktop_config.json`
- opencode: `~/.config/opencode/opencode.json`
ํ๊ฒฝ ๋ณ์๋ ์
ธ ํ๋กํ(`.zshrc` ๋ฑ)์์ ์๋์ผ๋ก ์ฃผ์
๋์ง ์์ ์ ์์ผ๋ฏ๋ก, ๊ฐ๋ฅํ๋ฉด ์ค์ ํ์ผ์ `env` ๋ธ๋ก์ผ๋ก ์ ๋ฌํ์ธ์.
## Default Models
๊ธฐ๋ณธ ๋ชจ๋ธ์ `src/config.ts`์ ํ๋์ฝ๋ฉ๋์ด ์์ผ๋ฉฐ, ํ๊ฒฝ ๋ณ์๋ก overrideํ ์ ์์ต๋๋ค.
| Provider | ๊ธฐ๋ณธ ๋ชจ๋ธ | ํ๊ฒฝ ๋ณ์ override |
|----------|-----------|-------------------|
| codex | `gpt-5.3-codex` | `MCP_CODEX_DEFAULT_MODEL` |
| gemini | `gemini-3-pro-preview` | `MCP_GEMINI_DEFAULT_MODEL` |
๋ชจ๋ธ ์ ํ ์ฐ์ ์์: **์์ฒญ ํ๋ผ๋ฏธํฐ `model`** > **ํ๊ฒฝ ๋ณ์** > **ํ๋์ฝ๋ฉ ๊ธฐ๋ณธ๊ฐ**
## Local development
```bash
npm install
npm run build
npm run start:codex
npm run start:gemini
```
๊ฐ๋ฐ ๋ชจ๋:
```bash
npm run dev:codex
npm run dev:gemini
```
## Runtime Files
- ๊ธฐ๋ณธ ๋ฐํ์ ๋๋ ํ ๋ฆฌ: `<cwd>/.codex-gemini-mcp/`
- background job ์ํ: `jobs/`
- background job ์
์ถ๋ ฅ(content): `prompts/`
- ๊ตฌ์กฐํ ๋ก๊น
(JSONL): `logs/`
- ๋ฐํ์ ๊ฒฝ๋ก override:
- `MCP_RUNTIME_DIR`: ๋ฐํ์ ๋ฃจํธ ๋๋ ํ ๋ฆฌ
- `MCP_LOG_DIR`: ๋ก๊ทธ ๋๋ ํ ๋ฆฌ
์ ๋ฆฌ(๊ธฐ๋ณธ ๊ฒฝ๋ก ์ฌ์ฉ ์):
```bash
rm -rf .codex-gemini-mcp
```
## Security / Privacy Notes
- `background: true`(๊ธฐ๋ณธ๊ฐ) ์์ฒญ์ `.codex-gemini-mcp/prompts/*content*.json`์ prompt/response๋ฅผ ์ ์ฅํฉ๋๋ค.
- ํ๋กฌํํธ์ ์ํฌ๋ฆฟ(ํ ํฐ, ๋น๋ฐ๋ฒํธ, ๊ฐ์ธ ์ ๋ณด ๋ฑ)์ ๋ฃ์ผ๋ฉด ๋ก์ปฌ ํ์ผ์ ๋จ์ ์ ์์ต๋๋ค.
- ๋ก๊น
์ ๊ธฐ๋ณธ์ ์ผ๋ก ๋ณธ๋ฌธ ๋ฏธ์ ์ฅ์ด์ง๋ง, ์๋ ํ๋๊ทธ๋ฅผ ์ผ๋ฉด ๋ก๊ทธ์ ํ
์คํธ๊ฐ ํฌํจ๋ ์ ์์ต๋๋ค:
- `MCP_LOG_PREVIEW=1`
- `MCP_LOG_FULL_TEXT=1`
## Tool Schemas
### ask_codex
- `prompt` (string, required)
- `model` (string, optional)
- `model`์ `[A-Za-z0-9][A-Za-z0-9._:-]*` ํจํด(์ต๋ 128์)๋ง ํ์ฉ
- `working_directory` (string, optional): CLI ํ๋ก์ธ์ค์ ์คํ ๋๋ ํ ๋ฆฌ(cwd)
- `background` (boolean, optional, default `true`)
- `reasoning_effort` (string, optional: `minimal` | `low` | `medium` | `high` | `xhigh`)
### ask_gemini
- `prompt` (string, required)
- `model` (string, optional)
- `model`์ `[A-Za-z0-9][A-Za-z0-9._:-]*` ํจํด(์ต๋ 128์)๋ง ํ์ฉ
- `working_directory` (string, optional): CLI ํ๋ก์ธ์ค์ ์คํ ๋๋ ํ ๋ฆฌ(cwd)
- `background` (boolean, optional, default `true`)
### wait_for_job
- `job_id` (string, required, 8์๋ฆฌ hex)
- `timeout_ms` (number, optional, default 3600000, max 3600000; 3600000 ์ด๊ณผ ๊ฐ์ 3600000์ผ๋ก cap)
### check_job_status
- `job_id` (string, required, 8์๋ฆฌ hex)
### kill_job
- `job_id` (string, required, 8์๋ฆฌ hex)
- `signal` (string, optional: `SIGTERM` | `SIGINT`, default `SIGTERM`)
### list_jobs
- `status_filter` (string, optional: `active`(spawned/running) | `completed` | `failed`(failed/timeout) | `all`, default `active`)
- `limit` (number, optional, default `50`)
## Runtime Notes
- `ask_codex`: `codex exec --ephemeral` ํธ์ถ (`reasoning_effort` ์ง์ ์ `-c model_reasoning_effort=...` ์ถ๊ฐ)
- `ask_gemini`: `gemini --prompt <text>` ํธ์ถ
- `ask_*`๋ `background` ๋ฏธ์ง์ ์ ๊ธฐ๋ณธ `true`๋ก ์คํ
- `background: true` ํธ์ถ ์ `.codex-gemini-mcp/jobs`, `.codex-gemini-mcp/prompts`์ ์ํ/์
์ถ๋ ฅ(content) ํ์ผ ์ ์ฅ
- ๊ตฌ์กฐํ ๋ก๊น
(JSONL): `.codex-gemini-mcp/logs/mcp-YYYY-MM-DD.jsonl`
- ๊ธฐ๋ณธ: ๋ฉํ๋ฐ์ดํฐ๋ง ์ ์ฅ (๋ณธ๋ฌธ ๋ฏธ์ ์ฅ)
- `MCP_LOG_PREVIEW=1`: preview ์ ์ฅ
- `MCP_LOG_FULL_TEXT=1`: full text ์ ์ฅ
- ๋ก๊ทธ ์ด๋ฒคํธ๋ JSONL ํ์ผ ์ ์ฅ๊ณผ ํจ๊ป `stderr`์๋ ๋ฏธ๋ฌ๋ง๋จ
- ๋ชจ๋ธ ์ ํ ์ฐ์ ์์: `request.model > env default > hardcoded default`
- codex env: `MCP_CODEX_DEFAULT_MODEL` (๊ธฐ๋ณธ๊ฐ: `gpt-5.3-codex`)
- gemini env: `MCP_GEMINI_DEFAULT_MODEL` (๊ธฐ๋ณธ๊ฐ: `gemini-3-pro-preview`)
- CLI timeout ๊ธฐ๋ณธ๊ฐ์ `MCP_CLI_TIMEOUT_MS` ๋๋ 3600000ms(60๋ถ)
- `stdout + stderr` ํฉ์ฐ ์ถ๋ ฅ์ด `MCP_MAX_OUTPUT_BYTES`๋ฅผ ๋์ผ๋ฉด `CLI_OUTPUT_LIMIT_EXCEEDED`๋ก ์ข
๋ฃ
- ์ถ๋ ฅ์ ์์ ์ ์ธ ํ
์คํธ ํ์ดํ๋ฅผ ์ํด ์์/TTY๋ฅผ ๋นํ์ฑํํ์ฌ ์คํํฉ๋๋ค (`NO_COLOR=1`, `FORCE_COLOR=0`, `TERM=dumb`)
## Logging by `background`
- ๊ณตํต(`background` true/false ๋ชจ๋): JSONL์ `request` ์ด๋ฒคํธ์ terminal(`response` ๋๋ `error`) ์ด๋ฒคํธ๊ฐ ๊ธฐ๋ก๋๊ณ , `request_id`๋ก 1์ฐจ ์ถ์ ๊ฐ๋ฅ
- `background: false` (foreground): ๋ก๊ทธ ์ด๋ฒคํธ์ `job_id`๊ฐ ์์. `jobs/`, `prompts/` ํ์ผ์ ์์ฑ๋์ง ์์
- `background: true` (background):
- MCP ์๋ต์ `jobId`, `contentFile`, `statusFile` ๋ฐํ
- JSONL `response`/`error` ์ด๋ฒคํธ์ `job_id` ๊ธฐ๋ก
- `jobs/*status*.json`, `prompts/*content*.json`์ `requestId` ์ ์ฅ
- ๋ฐ๋ผ์ `request_id` <-> `job_id`๋ฅผ ๋ก๊ทธ/์ํํ์ผ ์๋ฐฉํฅ์ผ๋ก ๋งคํ ๊ฐ๋ฅ
## Environment Variables
- `MCP_CODEX_DEFAULT_MODEL`: codex ๊ธฐ๋ณธ ๋ชจ๋ธ
- `MCP_GEMINI_DEFAULT_MODEL`: gemini ๊ธฐ๋ณธ ๋ชจ๋ธ
- `MCP_CLI_TIMEOUT_MS`: ๊ธฐ๋ณธ CLI timeout(ms)
- `MCP_MAX_OUTPUT_BYTES`: ์ต๋ ์ถ๋ ฅ ๋ฐ์ดํธ(cap, ๊ธฐ๋ณธ 1048576 = 1MiB)
- `MCP_RUNTIME_DIR`: ๋ฐํ์ ํ์ผ ๊ธฐ๋ณธ ๋ฃจํธ(`.codex-gemini-mcp`)
- `MCP_LOG_DIR`: ๋ก๊ทธ ๊ฒฝ๋ก override
- `MCP_LOG_PREVIEW`: ๋ก๊ทธ preview ์ ์ฅ ์ฌ๋ถ (`1`์ด๋ฉด ํ์ฑํ)
- `MCP_LOG_FULL_TEXT`: ์ ์ฒด ํ
์คํธ ๋ก๊ทธ ์ฌ๋ถ (`1`์ด๋ฉด ํ์ฑํ)
## Current Status
- ๋ฐ์ด๋๋ฆฌ ์ํธ๋ฆฌ: `codex-mcp`, `gemini-mcp`
- ๊ฒ์ฆ ์๋ฃ: `ask_codex`, `ask_gemini` foreground/background ์คํธ์ถ ์ฑ๊ณต
- ๊ฒ์ฆ ์๋ฃ: `wait_for_job`, `check_job_status`, `kill_job`, `list_jobs` ์คํธ์ถ ์ฑ๊ณต
- ๊ตฌํ ์๋ฃ: ๊ตฌ์กฐํ ๋ก๊น
(Phase D)
- ๊ตฌํ ์๋ฃ: output cap ๊ฐ์ + model regex validation
## Scope (deliberately minimal)
์ด ์ํ์๋ ์๋ ๊ธฐ๋ฅ์ด ์์ต๋๋ค:
- ๋ชจ๋ธ fallback chain
- standalone bridge ๋ฒ๋ค๋ง
## Troubleshooting
- `CLI_NOT_FOUND`:
- `codex` ๋๋ `gemini` CLI๊ฐ PATH์ ์์ ๋ ๋ฐ์ํฉ๋๋ค.
- `npm i -g @openai/codex` / `npm i -g @google/gemini-cli` ์ค์น ํ ์ฌ์๋ํ์ธ์.
- output์ด ์๋ฆผ(`CLI_OUTPUT_LIMIT_EXCEEDED`):
- `MCP_MAX_OUTPUT_BYTES`๋ฅผ ๋๋ฆฌ๊ฑฐ๋, ํ๋กฌํํธ/์ถ๋ ฅ์ ์ค์ด์ธ์.
- background ํ์ผ์ด ๋๋ฌด ์์:
- ํ์ ์ `.codex-gemini-mcp/`๋ฅผ ์ง์ ์ ๋ฆฌํ์ธ์.
## Acknowledgements
์ด ํ๋ก์ ํธ๋ [oh-my-claudecode](https://github.com/Yeachan-Heo/oh-my-claudecode)์ ๊ตฌํ๋ Codex/Gemini MCP ์๋ฒ๋ฅผ ์ฐธ๊ณ ํ์ฌ ์ฌ๊ตฌํํ ๊ฒ์
๋๋ค.
## License
[MIT](LICENSE)
TDQS
Scored across 1 tool
With only one tool, there is no possibility of ambiguity or overlap between tools. The tool's purpose is clearly defined as sending prompts to a local Codex CLI, making it impossible for an agent to misselect between non-existent alternatives.
A single tool inherently exhibits perfect naming consistency, as there are no other tools to compare it against. The name 'ask_codex' follows a clear verb_noun pattern and stands alone without any conflicting conventions.
One tool is too few for a server named 'Codex Gemini MCP', which suggests a broader scope or multiple functionalities. A single tool feels thin and limited, failing to provide a comprehensive surface for interacting with a Codex CLI beyond basic prompting.
The tool surface is severely incomplete for the implied domain of a Codex CLI interface. While 'ask_codex' covers basic prompting, there are obvious gaps such as configuration management, session handling, or advanced querying capabilities that would be expected in a more robust integration.