ai-cli-mcp 2.4.0 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gemini/settings.json +11 -0
- package/.mcp.json +2 -1
- package/CHANGELOG.md +16 -0
- package/README.ja.md +5 -7
- package/README.md +5 -145
- package/dist/__tests__/parsers.test.js +98 -0
- package/dist/__tests__/server.test.js +1 -1
- package/dist/parsers.js +90 -4
- package/dist/server.js +32 -19
- package/docs/development.md +85 -0
- package/package.json +1 -1
- package/src/__tests__/parsers.test.ts +108 -0
- package/src/__tests__/server.test.ts +1 -1
- package/src/parsers.ts +96 -4
- package/src/server.ts +31 -18
- package/AGENT.md +0 -57
- package/RELEASE.md +0 -74
- package/print-eslint-config.js +0 -3
- package/start.bat +0 -9
- package/start.sh +0 -21
- package/test-standalone.js +0 -5877
package/.mcp.json
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
# [2.6.0](https://github.com/mkXultra/claude-code-mcp/compare/v2.5.0...v2.6.0) (2026-02-09)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* update model support (gpt-5.3-codex, sonnet[1m], opusplan) and refactor README ([eb6574d](https://github.com/mkXultra/claude-code-mcp/commit/eb6574d3760269d4be96cd934bc03d94ccb3801f))
|
|
7
|
+
|
|
8
|
+
# [2.5.0](https://github.com/mkXultra/claude-code-mcp/compare/v2.4.0...v2.5.0) (2026-01-24)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* enhance output parsers for Codex and Claude with tool usage extraction ([b6410a1](https://github.com/mkXultra/claude-code-mcp/commit/b6410a104666eca592735acea093b877c0f03f64))
|
|
14
|
+
* track command execution in Codex output and include .gemini config ([91f7f06](https://github.com/mkXultra/claude-code-mcp/commit/91f7f067a1d453fd8e3a5a95bb90f21b7df0af8a))
|
|
15
|
+
* update Claude CLI args to stream-json and add verbose option to get_result ([b7f9abc](https://github.com/mkXultra/claude-code-mcp/commit/b7f9abc11c56ad0c8c95e90a614d1d869d8a3bfa))
|
|
16
|
+
|
|
1
17
|
# [2.4.0](https://github.com/mkXultra/claude-code-mcp/compare/v2.3.3...v2.4.0) (2026-01-24)
|
|
2
18
|
|
|
3
19
|
|
package/README.ja.md
CHANGED
|
@@ -9,8 +9,6 @@ AI CLIツール(Claude, Codex, Gemini)をバックグラウンドプロセ
|
|
|
9
9
|
|
|
10
10
|
Cursorなどのエディタが、複雑な手順を伴う編集や操作に苦戦していることに気づいたことはありませんか?このサーバーは、強力な統合 `run` ツールを提供し、複数のAIエージェントを活用してコーディングタスクをより効果的に処理できるようにします。
|
|
11
11
|
|
|
12
|
-
<img src="assets/screenshot.png" width="300" alt="Screenshot">
|
|
13
|
-
|
|
14
12
|
## 概要
|
|
15
13
|
|
|
16
14
|
このMCPサーバーは、LLMがAI CLIツールと対話するためのツールを提供します。MCPクライアントと統合することで、LLMは以下のことが可能になります:
|
|
@@ -19,8 +17,8 @@ Cursorなどのエディタが、複雑な手順を伴う編集や操作に苦
|
|
|
19
17
|
- 自動承認モードでCodex CLIを実行(`--full-auto` を使用)
|
|
20
18
|
- 自動承認モードでGemini CLIを実行(`-y` を使用)
|
|
21
19
|
- 複数のAIモデルのサポート:
|
|
22
|
-
- Claude (sonnet, opus, haiku)
|
|
23
|
-
- Codex (gpt-5.2-codex, gpt-5.1-codex-mini, gpt-5.1-codex-max, など)
|
|
20
|
+
- Claude (sonnet, sonnet[1m], opus, opusplan, haiku)
|
|
21
|
+
- Codex (gpt-5.3-codex, gpt-5.2-codex, gpt-5.1-codex-mini, gpt-5.1-codex-max, など)
|
|
24
22
|
- Gemini (gemini-2.5-pro, gemini-2.5-flash, gemini-3-pro-preview, gemini-3-flash-preview)
|
|
25
23
|
- PID追跡によるバックグラウンドプロセスの管理
|
|
26
24
|
- ツールからの構造化された出力の解析と返却
|
|
@@ -134,10 +132,10 @@ Claude CLI、Codex CLI、またはGemini CLIを使用してプロンプトを実
|
|
|
134
132
|
- `workFolder` (string, 必須): CLIを実行する作業ディレクトリ。絶対パスである必要があります。
|
|
135
133
|
- **モデル (Models):**
|
|
136
134
|
- **Ultra エイリアス:** `claude-ultra`, `codex-ultra` (自動的に high-reasoning に設定), `gemini-ultra`
|
|
137
|
-
- Claude: `sonnet`, `opus`, `haiku`
|
|
138
|
-
- Codex: `gpt-5.2-codex`, `gpt-5.1-codex-mini`, `gpt-5.1-codex-max`, `gpt-5.2`, `gpt-5.1`, `gpt-5`
|
|
135
|
+
- Claude: `sonnet`, `sonnet[1m]`, `opus`, `opusplan`, `haiku`
|
|
136
|
+
- Codex: `gpt-5.3-codex`, `gpt-5.2-codex`, `gpt-5.1-codex-mini`, `gpt-5.1-codex-max`, `gpt-5.2`, `gpt-5.1`, `gpt-5`
|
|
139
137
|
- Gemini: `gemini-2.5-pro`, `gemini-2.5-flash`, `gemini-3-pro-preview`, `gemini-3-flash-preview`
|
|
140
|
-
- `reasoning_effort` (string, 任意): Codex専用。`model_reasoning_effort` を設定します(許容値: "low", "medium", "high")。
|
|
138
|
+
- `reasoning_effort` (string, 任意): Codex専用。`model_reasoning_effort` を設定します(許容値: "low", "medium", "high", "xhigh")。
|
|
141
139
|
- `session_id` (string, 任意): 以前のセッションを再開するためのセッションID。対応モデル: haiku, sonnet, opus, gemini-2.5-pro, gemini-2.5-flash, gemini-3-pro-preview, gemini-3-flash-preview。
|
|
142
140
|
|
|
143
141
|
### `wait`
|
package/README.md
CHANGED
|
@@ -11,8 +11,6 @@ An MCP (Model Context Protocol) server that allows running AI CLI tools (Claude,
|
|
|
11
11
|
|
|
12
12
|
Did you notice that Cursor sometimes struggles with complex, multi-step edits or operations? This server, with its powerful unified `run` tool, enables multiple AI agents to handle your coding tasks more effectively.
|
|
13
13
|
|
|
14
|
-
<img src="assets/screenshot.png" width="300" alt="Screenshot">
|
|
15
|
-
|
|
16
14
|
## Overview
|
|
17
15
|
|
|
18
16
|
This MCP server provides tools that can be used by LLMs to interact with AI CLI tools. When integrated with MCP clients, it allows LLMs to:
|
|
@@ -20,7 +18,7 @@ This MCP server provides tools that can be used by LLMs to interact with AI CLI
|
|
|
20
18
|
- Run Claude CLI with all permissions bypassed (using `--dangerously-skip-permissions`)
|
|
21
19
|
- Execute Codex CLI with automatic approval mode (using `--full-auto`)
|
|
22
20
|
- Execute Gemini CLI with automatic approval mode (using `-y`)
|
|
23
|
-
- Support multiple AI models: Claude (sonnet, opus, haiku), Codex (gpt-5.2-codex, gpt-5.1-codex-mini, gpt-5.1-codex-max, gpt-5.2, gpt-5.1, gpt-5.1-codex, gpt-5-codex, gpt-5-codex-mini, gpt-5), and Gemini (gemini-2.5-pro, gemini-2.5-flash, gemini-3-pro-preview, gemini-3-flash-preview)
|
|
21
|
+
- Support multiple AI models: Claude (sonnet, sonnet[1m], opus, opusplan, haiku), Codex (gpt-5.3-codex, gpt-5.2-codex, gpt-5.1-codex-mini, gpt-5.1-codex-max, gpt-5.2, gpt-5.1, gpt-5.1-codex, gpt-5-codex, gpt-5-codex-mini, gpt-5), and Gemini (gemini-2.5-pro, gemini-2.5-flash, gemini-3-pro-preview, gemini-3-flash-preview)
|
|
24
22
|
- Manage background processes with PID tracking
|
|
25
23
|
- Parse and return structured outputs from both tools
|
|
26
24
|
|
|
@@ -133,10 +131,10 @@ Executes a prompt using Claude CLI, Codex CLI, or Gemini CLI. The appropriate CL
|
|
|
133
131
|
- `workFolder` (string, required): The working directory for the CLI execution. Must be an absolute path.
|
|
134
132
|
**Models:**
|
|
135
133
|
- **Ultra Aliases:** `claude-ultra`, `codex-ultra` (defaults to high-reasoning), `gemini-ultra`
|
|
136
|
-
- Claude: `sonnet`, `opus`, `haiku`
|
|
137
|
-
- Codex: `gpt-5.2-codex`, `gpt-5.1-codex-mini`, `gpt-5.1-codex-max`, `gpt-5.2`, `gpt-5.1`, `gpt-5`
|
|
134
|
+
- Claude: `sonnet`, `sonnet[1m]`, `opus`, `opusplan`, `haiku`
|
|
135
|
+
- Codex: `gpt-5.3-codex`, `gpt-5.2-codex`, `gpt-5.1-codex-mini`, `gpt-5.1-codex-max`, `gpt-5.2`, `gpt-5.1`, `gpt-5`
|
|
138
136
|
- Gemini: `gemini-2.5-pro`, `gemini-2.5-flash`, `gemini-3-pro-preview`, `gemini-3-flash-preview`
|
|
139
|
-
- `reasoning_effort` (string, optional): Codex only. Sets `model_reasoning_effort` (allowed: "low", "medium", "high").
|
|
137
|
+
- `reasoning_effort` (string, optional): Codex only. Sets `model_reasoning_effort` (allowed: "low", "medium", "high", "xhigh").
|
|
140
138
|
- `session_id` (string, optional): Optional session ID to resume a previous session. Supported for: haiku, sonnet, opus, gemini-2.5-pro, gemini-2.5-flash, gemini-3-pro-preview, gemini-3-flash-preview.
|
|
141
139
|
|
|
142
140
|
### `wait`
|
|
@@ -165,77 +163,6 @@ Terminates a running AI agent process by PID.
|
|
|
165
163
|
**Arguments:**
|
|
166
164
|
- `pid` (number, required): The process ID to terminate.
|
|
167
165
|
|
|
168
|
-
### Examples
|
|
169
|
-
|
|
170
|
-
Here are some visual examples of the server in action:
|
|
171
|
-
|
|
172
|
-
<img src="assets/claude_tool_git_example.png" alt="Claude Tool Git Example" width="50%">
|
|
173
|
-
|
|
174
|
-
<img src="assets/additional_claude_screenshot.png" alt="Additional Claude Screenshot" width="50%">
|
|
175
|
-
|
|
176
|
-
<img src="assets/cursor-screenshot.png" alt="Cursor Screenshot" width="50%">
|
|
177
|
-
|
|
178
|
-
### Fixing ESLint Setup
|
|
179
|
-
|
|
180
|
-
Here's an example of using the Claude Code MCP tool to interactively fix an ESLint setup by deleting old configuration files and creating a new one:
|
|
181
|
-
|
|
182
|
-
<img src="assets/eslint_example.png" alt="ESLint file operations example" width="50%">
|
|
183
|
-
|
|
184
|
-
### Listing Files Example
|
|
185
|
-
|
|
186
|
-
Here's an example of the Claude Code tool listing files in a directory:
|
|
187
|
-
|
|
188
|
-
<img src="assets/file_list_example.png" alt="File listing example" width="50%">
|
|
189
|
-
|
|
190
|
-
## Key Use Cases
|
|
191
|
-
|
|
192
|
-
This server, through its unified `run` tool, unlocks a wide range of powerful capabilities by giving your AI direct access to both Claude and Codex CLI tools. Here are some examples of what you can achieve:
|
|
193
|
-
|
|
194
|
-
1. **Code Generation, Analysis & Refactoring:**
|
|
195
|
-
- `"Generate a Python script to parse CSV data and output JSON."`
|
|
196
|
-
- `"Analyze my_script.py for potential bugs and suggest improvements."`
|
|
197
|
-
|
|
198
|
-
2. **File System Operations (Create, Read, Edit, Manage):**
|
|
199
|
-
- **Creating Files:** `"Your work folder is /Users/steipete/my_project\n\nCreate a new file named 'config.yml' in the 'app/settings' directory with the following content:\nport: 8080\ndatabase: main_db"`
|
|
200
|
-
- **Editing Files:** `"Your work folder is /Users/steipete/my_project\n\nEdit file 'public/css/style.css': Add a new CSS rule at the end to make all 'h2' elements have a 'color: navy'."`
|
|
201
|
-
- **Moving/Copying/Deleting:** `"Your work folder is /Users/steipete/my_project\n\nMove the file 'report.docx' from the 'drafts' folder to the 'final_reports' folder and rename it to 'Q1_Report_Final.docx'."`
|
|
202
|
-
|
|
203
|
-
3. **Version Control (Git):**
|
|
204
|
-
- `"Your work folder is /Users/steipete/my_project\n\n1. Stage the file 'src/main.java'.\n2. Commit the changes with the message 'feat: Implement user authentication'.\n3. Push the commit to the 'develop' branch on origin."`
|
|
205
|
-
|
|
206
|
-
4. **Running Terminal Commands:**
|
|
207
|
-
- `"Your work folder is /Users/steipete/my_project/frontend\n\nRun the command 'npm run build'."`
|
|
208
|
-
- `"Open the URL https://developer.mozilla.org in my default web browser."`
|
|
209
|
-
|
|
210
|
-
5. **Web Search & Summarization:**
|
|
211
|
-
- `"Search the web for 'benefits of server-side rendering' and provide a concise summary."`
|
|
212
|
-
|
|
213
|
-
6. **Complex Multi-Step Workflows:**
|
|
214
|
-
- Automate version bumps, update changelogs, and tag releases: `"Your work folder is /Users/steipete/my_project\n\nFollow these steps: 1. Update the version in package.json to 2.5.0. 2. Add a new section to CHANGELOG.md for version 2.5.0 with the heading '### Added' and list 'New feature X'. 3. Stage package.json and CHANGELOG.md. 4. Commit with message 'release: version 2.5.0'. 5. Push the commit. 6. Create and push a git tag v2.5.0."`
|
|
215
|
-
|
|
216
|
-
<img src="assets/multistep_example.png" alt="Complex multi-step operation example" width="50%">
|
|
217
|
-
|
|
218
|
-
7. **Repairing Files with Syntax Errors:**
|
|
219
|
-
- `"Your work folder is /path/to/project\n\nThe file 'src/utils/parser.js' has syntax errors after a recent complex edit that broke its structure. Please analyze it, identify the syntax errors, and correct the file to make it valid JavaScript again, ensuring the original logic is preserved as much as possible."`
|
|
220
|
-
|
|
221
|
-
8. **Interacting with GitHub (e.g., Creating a Pull Request):**
|
|
222
|
-
- `"Your work folder is /Users/steipete/my_project\n\nCreate a GitHub Pull Request in the repository 'owner/repo' from the 'feature-branch' to the 'main' branch. Title: 'feat: Implement new login flow'. Body: 'This PR adds a new and improved login experience for users.'"`
|
|
223
|
-
|
|
224
|
-
9. **Interacting with GitHub (e.g., Checking PR CI Status):**
|
|
225
|
-
- `"Your work folder is /Users/steipete/my_project\n\nCheck the status of CI checks for Pull Request #42 in the GitHub repository 'owner/repo'. Report if they have passed, failed, or are still running."`
|
|
226
|
-
|
|
227
|
-
### Correcting GitHub Actions Workflow
|
|
228
|
-
|
|
229
|
-
<img src="assets/github_actions_fix_example.png" alt="GitHub Actions workflow fix example" width="50%">
|
|
230
|
-
|
|
231
|
-
### Complex Multi-Step Operations
|
|
232
|
-
|
|
233
|
-
This example illustrates the AI agent handling a more complex, multi-step task, such as preparing a release by creating a branch, updating multiple files (`package.json`, `CHANGELOG.md`), committing changes, and initiating a pull request, all within a single, coherent operation.
|
|
234
|
-
|
|
235
|
-
<img src="assets/claude_code_multistep_example.png" alt="AI agent multi-step example" width="50%">
|
|
236
|
-
|
|
237
|
-
**CRITICAL: Remember to provide Current Working Directory (CWD) context in your prompts for file system or git operations (e.g., `"Your work folder is /path/to/project\n\n...your command..."`).**
|
|
238
|
-
|
|
239
166
|
## Troubleshooting
|
|
240
167
|
|
|
241
168
|
- **"Command not found" (claude-code-mcp):** If installed globally, ensure the npm global bin directory is in your system's PATH. If using `npx`, ensure `npx` itself is working.
|
|
@@ -244,76 +171,9 @@ This example illustrates the AI agent handling a more complex, multi-step task,
|
|
|
244
171
|
- **JSON Errors from Server:** If `MCP_CLAUDE_DEBUG` is `true`, error messages or logs might interfere with MCP's JSON parsing. Set to `false` for normal operation.
|
|
245
172
|
- **ESM/Import Errors:** Ensure you are using Node.js v20 or later.
|
|
246
173
|
|
|
247
|
-
**For Developers: Local Setup & Contribution**
|
|
248
|
-
|
|
249
|
-
If you want to develop or contribute to this server, or run it from a cloned repository for testing, please see our [Local Installation & Development Setup Guide](./docs/local_install.md).
|
|
250
|
-
|
|
251
|
-
## Testing
|
|
252
|
-
|
|
253
|
-
The project includes comprehensive test suites:
|
|
254
|
-
|
|
255
|
-
```bash
|
|
256
|
-
# Run all tests
|
|
257
|
-
npm test
|
|
258
|
-
|
|
259
|
-
# Run unit tests only
|
|
260
|
-
npm run test:unit
|
|
261
|
-
|
|
262
|
-
# Run e2e tests (with mocks)
|
|
263
|
-
npm run test:e2e
|
|
264
|
-
|
|
265
|
-
# Run e2e tests locally (requires Claude CLI)
|
|
266
|
-
npm run test:e2e:local
|
|
267
|
-
|
|
268
|
-
# Watch mode for development
|
|
269
|
-
npm run test:watch
|
|
270
|
-
|
|
271
|
-
# Coverage report
|
|
272
|
-
npm run test:coverage
|
|
273
|
-
```
|
|
274
|
-
|
|
275
|
-
For detailed testing documentation, see our [E2E Testing Guide](./docs/e2e-testing.md).
|
|
276
|
-
|
|
277
|
-
## Manual Testing with MCP Inspector
|
|
278
|
-
|
|
279
|
-
You can manually test the MCP server using the Model Context Protocol Inspector:
|
|
280
|
-
|
|
281
|
-
```bash
|
|
282
|
-
# Build the project first
|
|
283
|
-
npm run build
|
|
284
|
-
|
|
285
|
-
# Start the MCP Inspector with the server
|
|
286
|
-
npx @modelcontextprotocol/inspector node dist/server.js
|
|
287
|
-
```
|
|
288
|
-
|
|
289
|
-
This will open a web interface where you can:
|
|
290
|
-
1. View all available tools (`run`, `list_processes`, `get_result`, `kill_process`)
|
|
291
|
-
2. Test each tool with different parameters
|
|
292
|
-
3. Test different AI models including:
|
|
293
|
-
- Claude models: `sonnet`, `opus`, `haiku`
|
|
294
|
-
- Codex models: `gpt-5.2-codex`, `gpt-5.1-codex-mini`, `gpt-5.1-codex-max`, `gpt-5.2`, `gpt-5.1`, `gpt-5.1-codex`, `gpt-5-codex`, `gpt-5-codex-mini`, `gpt-5`
|
|
295
|
-
- Gemini models: `gemini-2.5-pro`, `gemini-2.5-flash`, `gemini-3-pro-preview`, `gemini-3-flash-preview`
|
|
296
|
-
|
|
297
|
-
Example test: Select the `run` tool and provide:
|
|
298
|
-
- `prompt`: "What is 2+2?"
|
|
299
|
-
- `workFolder`: "/tmp"
|
|
300
|
-
- `model`: "gemini-2.5-flash"
|
|
301
|
-
|
|
302
|
-
## Configuration via Environment Variables
|
|
303
|
-
|
|
304
|
-
The server's behavior can be customized using these environment variables:
|
|
305
|
-
|
|
306
|
-
- `CLAUDE_CLI_PATH`: Absolute path to the Claude CLI executable.
|
|
307
|
-
- Default: Checks `~/.claude/local/claude`, then falls back to `claude` (expecting it in PATH).
|
|
308
|
-
- `MCP_CLAUDE_DEBUG`: Set to `true` for verbose debug logging from this MCP server. Default: `false`.
|
|
309
|
-
|
|
310
|
-
These can be set in your shell environment or within the `env` block of your `mcp.json` server configuration (though the `env` block in `mcp.json` examples was removed for simplicity, it's still a valid way to set them for the server process if needed).
|
|
311
|
-
|
|
312
174
|
## Contributing
|
|
313
175
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
Submit issues and pull requests to the [GitHub repository](https://github.com/mkXultra/claude-code-mcp).
|
|
176
|
+
For development setup, testing, and contribution guidelines, see the [Development Guide](./docs/development.md).
|
|
317
177
|
|
|
318
178
|
## Advanced Configuration (Optional)
|
|
319
179
|
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { parseCodexOutput, parseClaudeOutput } from '../parsers.js';
|
|
3
|
+
describe('parseCodexOutput', () => {
|
|
4
|
+
it('should parse basic Codex output with message and session_id', () => {
|
|
5
|
+
const output = `
|
|
6
|
+
{"type":"thread.started","thread_id":"test-session-id"}
|
|
7
|
+
{"type":"turn.started"}
|
|
8
|
+
{"type":"item.completed","item":{"type":"agent_message","text":"Hello world"}}
|
|
9
|
+
{"type":"turn.completed"}
|
|
10
|
+
`;
|
|
11
|
+
const result = parseCodexOutput(output);
|
|
12
|
+
expect(result).toEqual({
|
|
13
|
+
message: "Hello world",
|
|
14
|
+
session_id: "test-session-id",
|
|
15
|
+
token_count: null,
|
|
16
|
+
tools: undefined
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
it('should extract MCP tool calls', () => {
|
|
20
|
+
const output = `
|
|
21
|
+
{"type":"thread.started","thread_id":"tool-test-id"}
|
|
22
|
+
{"type":"turn.started"}
|
|
23
|
+
{"type":"item.completed","item":{"id":"item_1","type":"mcp_tool_call","server":"acm","tool":"run","arguments":{"model":"gemini-2.5-flash","prompt":"hi"},"result":{"content":[{"text":"started","type":"text"}]},"status":"completed"}}
|
|
24
|
+
{"type":"item.completed","item":{"type":"agent_message","text":"Tool executed"}}
|
|
25
|
+
{"type":"turn.completed"}
|
|
26
|
+
`;
|
|
27
|
+
const result = parseCodexOutput(output);
|
|
28
|
+
expect(result.message).toBe("Tool executed");
|
|
29
|
+
expect(result.session_id).toBe("tool-test-id");
|
|
30
|
+
expect(result.tools).toHaveLength(1);
|
|
31
|
+
expect(result.tools[0]).toEqual({
|
|
32
|
+
tool: "run",
|
|
33
|
+
server: "acm",
|
|
34
|
+
input: { model: "gemini-2.5-flash", prompt: "hi" },
|
|
35
|
+
output: { content: [{ text: "started", type: "text" }] }
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
it('should handle multiple tool calls', () => {
|
|
39
|
+
const output = `
|
|
40
|
+
{"type":"item.completed","item":{"type":"mcp_tool_call","tool":"tool1","arguments":{"arg":1},"result":"res1"}}
|
|
41
|
+
{"type":"item.completed","item":{"type":"mcp_tool_call","tool":"tool2","arguments":{"arg":2},"result":"res2"}}
|
|
42
|
+
`;
|
|
43
|
+
const result = parseCodexOutput(output);
|
|
44
|
+
expect(result.tools).toHaveLength(2);
|
|
45
|
+
expect(result.tools[0].tool).toBe("tool1");
|
|
46
|
+
expect(result.tools[1].tool).toBe("tool2");
|
|
47
|
+
});
|
|
48
|
+
it('should return null for empty input', () => {
|
|
49
|
+
expect(parseCodexOutput("")).toBeNull();
|
|
50
|
+
});
|
|
51
|
+
it('should handle invalid JSON gracefully', () => {
|
|
52
|
+
const output = `
|
|
53
|
+
{"type":"valid"}
|
|
54
|
+
INVALID_JSON
|
|
55
|
+
{"type":"item.completed","item":{"type":"agent_message","text":"Still parses valid lines"}}
|
|
56
|
+
`;
|
|
57
|
+
const result = parseCodexOutput(output);
|
|
58
|
+
expect(result.message).toBe("Still parses valid lines");
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
describe('parseClaudeOutput', () => {
|
|
62
|
+
it('should parse legacy JSON output', () => {
|
|
63
|
+
const output = JSON.stringify({
|
|
64
|
+
content: [{ type: 'text', text: 'Hello' }]
|
|
65
|
+
});
|
|
66
|
+
const result = parseClaudeOutput(output);
|
|
67
|
+
expect(result).toEqual({
|
|
68
|
+
content: [{ type: 'text', text: 'Hello' }]
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
it('should parse stream-json (NDJSON) output', () => {
|
|
72
|
+
const output = `
|
|
73
|
+
{"type":"system","session_id":"test-claude-session"}
|
|
74
|
+
{"type":"assistant","message":{"content":[{"type":"text","text":"Thinking..."}]}}
|
|
75
|
+
{"type":"assistant","message":{"content":[{"type":"tool_use","id":"call_1","name":"mcp__acm__run","input":{"prompt":"hi"}}]}}
|
|
76
|
+
{"type":"user","message":{"content":[{"type":"tool_result","tool_use_id":"call_1","content":"done"}]}}
|
|
77
|
+
{"type":"result","result":"Final Answer","is_error":false}
|
|
78
|
+
`;
|
|
79
|
+
const result = parseClaudeOutput(output);
|
|
80
|
+
expect(result.message).toBe("Final Answer");
|
|
81
|
+
expect(result.session_id).toBe("test-claude-session");
|
|
82
|
+
expect(result.tools).toHaveLength(1);
|
|
83
|
+
expect(result.tools[0]).toEqual({
|
|
84
|
+
tool: "mcp__acm__run",
|
|
85
|
+
input: { prompt: "hi" },
|
|
86
|
+
output: "done"
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
it('should handle invalid NDJSON lines gracefully', () => {
|
|
90
|
+
const output = `
|
|
91
|
+
{"type":"system"}
|
|
92
|
+
INVALID_LINE
|
|
93
|
+
{"type":"result","result":"Success"}
|
|
94
|
+
`;
|
|
95
|
+
const result = parseClaudeOutput(output);
|
|
96
|
+
expect(result.message).toBe("Success");
|
|
97
|
+
});
|
|
98
|
+
});
|
|
@@ -652,7 +652,7 @@ describe('ClaudeCodeServer Unit Tests', () => {
|
|
|
652
652
|
}
|
|
653
653
|
});
|
|
654
654
|
// Verify spawn was called with resolved model name
|
|
655
|
-
expect(mockSpawn).toHaveBeenCalledWith(expect.any(String), expect.arrayContaining(['--model', '
|
|
655
|
+
expect(mockSpawn).toHaveBeenCalledWith(expect.any(String), expect.arrayContaining(['--model', 'haiku']), expect.any(Object));
|
|
656
656
|
// Verify PID is returned
|
|
657
657
|
expect(result.content[0].text).toContain('"pid": 12345');
|
|
658
658
|
});
|
package/dist/parsers.js
CHANGED
|
@@ -10,6 +10,7 @@ export function parseCodexOutput(stdout) {
|
|
|
10
10
|
let lastMessage = null;
|
|
11
11
|
let tokenCount = null;
|
|
12
12
|
let threadId = null;
|
|
13
|
+
const tools = [];
|
|
13
14
|
for (const line of lines) {
|
|
14
15
|
if (line.trim()) {
|
|
15
16
|
try {
|
|
@@ -29,6 +30,22 @@ export function parseCodexOutput(stdout) {
|
|
|
29
30
|
else if (parsed.msg?.type === 'token_count') {
|
|
30
31
|
tokenCount = parsed.msg;
|
|
31
32
|
}
|
|
33
|
+
else if (parsed.type === 'item.completed' && parsed.item?.type === 'mcp_tool_call') {
|
|
34
|
+
tools.push({
|
|
35
|
+
server: parsed.item.server,
|
|
36
|
+
tool: parsed.item.tool,
|
|
37
|
+
input: parsed.item.arguments, // Map arguments to input to match common patterns
|
|
38
|
+
output: parsed.item.result
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
else if (parsed.type === 'item.completed' && parsed.item?.type === 'command_execution') {
|
|
42
|
+
tools.push({
|
|
43
|
+
tool: 'command_execution',
|
|
44
|
+
input: { command: parsed.item.command },
|
|
45
|
+
output: parsed.item.aggregated_output,
|
|
46
|
+
exit_code: parsed.item.exit_code
|
|
47
|
+
});
|
|
48
|
+
}
|
|
32
49
|
}
|
|
33
50
|
catch (e) {
|
|
34
51
|
// Skip invalid JSON lines
|
|
@@ -36,11 +53,12 @@ export function parseCodexOutput(stdout) {
|
|
|
36
53
|
}
|
|
37
54
|
}
|
|
38
55
|
}
|
|
39
|
-
if (lastMessage || tokenCount || threadId) {
|
|
56
|
+
if (lastMessage || tokenCount || threadId || tools.length > 0) {
|
|
40
57
|
return {
|
|
41
58
|
message: lastMessage,
|
|
42
59
|
token_count: tokenCount,
|
|
43
|
-
session_id: threadId
|
|
60
|
+
session_id: threadId,
|
|
61
|
+
tools: tools.length > 0 ? tools : undefined
|
|
44
62
|
};
|
|
45
63
|
}
|
|
46
64
|
}
|
|
@@ -50,18 +68,86 @@ export function parseCodexOutput(stdout) {
|
|
|
50
68
|
return null;
|
|
51
69
|
}
|
|
52
70
|
/**
|
|
53
|
-
* Parse Claude JSON
|
|
71
|
+
* Parse Claude Output (supports both JSON and stream-json/NDJSON)
|
|
54
72
|
*/
|
|
55
73
|
export function parseClaudeOutput(stdout) {
|
|
56
74
|
if (!stdout)
|
|
57
75
|
return null;
|
|
76
|
+
// First try parsing as a single JSON object (backward compatibility)
|
|
58
77
|
try {
|
|
59
78
|
return JSON.parse(stdout);
|
|
60
79
|
}
|
|
61
80
|
catch (e) {
|
|
62
|
-
|
|
81
|
+
// If not valid single JSON, proceed to parse as NDJSON
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
const lines = stdout.trim().split('\n');
|
|
85
|
+
let lastMessage = null;
|
|
86
|
+
let sessionId = null;
|
|
87
|
+
const toolsMap = new Map(); // Map by tool_use id for matching results
|
|
88
|
+
for (const line of lines) {
|
|
89
|
+
if (!line.trim())
|
|
90
|
+
continue;
|
|
91
|
+
try {
|
|
92
|
+
const parsed = JSON.parse(line);
|
|
93
|
+
// Extract session ID from any message that has it
|
|
94
|
+
if (parsed.session_id) {
|
|
95
|
+
sessionId = parsed.session_id;
|
|
96
|
+
}
|
|
97
|
+
// Extract final result message
|
|
98
|
+
if (parsed.type === 'result' && parsed.result) {
|
|
99
|
+
lastMessage = parsed.result;
|
|
100
|
+
}
|
|
101
|
+
// Extract tool usage from assistant messages
|
|
102
|
+
if (parsed.type === 'assistant' && parsed.message?.content) {
|
|
103
|
+
for (const content of parsed.message.content) {
|
|
104
|
+
if (content.type === 'tool_use') {
|
|
105
|
+
toolsMap.set(content.id, {
|
|
106
|
+
tool: content.name,
|
|
107
|
+
input: content.input,
|
|
108
|
+
output: null // Will be filled when tool_result is found
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// Match tool results from user messages
|
|
114
|
+
if (parsed.type === 'user' && parsed.message?.content) {
|
|
115
|
+
for (const content of parsed.message.content) {
|
|
116
|
+
if (content.type === 'tool_result' && content.tool_use_id) {
|
|
117
|
+
const tool = toolsMap.get(content.tool_use_id);
|
|
118
|
+
if (tool) {
|
|
119
|
+
// Extract text from content array
|
|
120
|
+
if (Array.isArray(content.content)) {
|
|
121
|
+
const textContent = content.content.find((c) => c.type === 'text');
|
|
122
|
+
tool.output = textContent?.text || null;
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
tool.output = content.content;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
catch (e) {
|
|
133
|
+
debugLog(`[Debug] Skipping invalid JSON line in Claude output: ${line}`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
// Convert Map to array
|
|
137
|
+
const tools = Array.from(toolsMap.values());
|
|
138
|
+
if (lastMessage || sessionId || tools.length > 0) {
|
|
139
|
+
return {
|
|
140
|
+
message: lastMessage, // This is the final result text
|
|
141
|
+
session_id: sessionId,
|
|
142
|
+
tools: tools.length > 0 ? tools : undefined
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
catch (e) {
|
|
147
|
+
debugLog(`[Debug] Failed to parse Claude NDJSON output: ${e}`);
|
|
63
148
|
return null;
|
|
64
149
|
}
|
|
150
|
+
return null;
|
|
65
151
|
}
|
|
66
152
|
/**
|
|
67
153
|
* Parse Gemini JSON output
|
package/dist/server.js
CHANGED
|
@@ -12,12 +12,11 @@ import { parseCodexOutput, parseClaudeOutput, parseGeminiOutput } from './parser
|
|
|
12
12
|
const SERVER_VERSION = "2.2.0";
|
|
13
13
|
// Model alias mappings for user-friendly model names
|
|
14
14
|
const MODEL_ALIASES = {
|
|
15
|
-
'haiku': 'claude-3-5-haiku-20241022',
|
|
16
15
|
'claude-ultra': 'opus',
|
|
17
|
-
'codex-ultra': 'gpt-5.
|
|
16
|
+
'codex-ultra': 'gpt-5.3-codex',
|
|
18
17
|
'gemini-ultra': 'gemini-3-pro-preview'
|
|
19
18
|
};
|
|
20
|
-
const ALLOWED_REASONING_EFFORTS = new Set(['low', 'medium', 'high']);
|
|
19
|
+
const ALLOWED_REASONING_EFFORTS = new Set(['low', 'medium', 'high', 'xhigh']);
|
|
21
20
|
function getReasoningEffort(model, rawValue) {
|
|
22
21
|
if (typeof rawValue !== 'string') {
|
|
23
22
|
return '';
|
|
@@ -28,7 +27,7 @@ function getReasoningEffort(model, rawValue) {
|
|
|
28
27
|
}
|
|
29
28
|
const normalized = trimmed.toLowerCase();
|
|
30
29
|
if (!ALLOWED_REASONING_EFFORTS.has(normalized)) {
|
|
31
|
-
throw new McpError(ErrorCode.InvalidParams, `Invalid reasoning_effort: ${rawValue}. Allowed values: low, medium, high.`);
|
|
30
|
+
throw new McpError(ErrorCode.InvalidParams, `Invalid reasoning_effort: ${rawValue}. Allowed values: low, medium, high, xhigh.`);
|
|
32
31
|
}
|
|
33
32
|
if (!model.startsWith('gpt-')) {
|
|
34
33
|
throw new McpError(ErrorCode.InvalidParams, 'reasoning_effort is only supported for Codex models (gpt-*).');
|
|
@@ -269,7 +268,7 @@ export class ClaudeCodeServer {
|
|
|
269
268
|
**IMPORTANT**: This tool now returns immediately with a PID. Use other tools to check status and get results.
|
|
270
269
|
|
|
271
270
|
**Supported models**:
|
|
272
|
-
"claude-ultra", "codex-ultra", "gemini-ultra", "sonnet", "opus", "haiku", "gpt-5.2-codex", "gpt-5.1-codex-mini", "gpt-5.1-codex-max", "gpt-5.2", "gpt-5.1", "gpt-5.1-codex", "gpt-5-codex", "gpt-5-codex-mini", "gpt-5", "gemini-2.5-pro", "gemini-2.5-flash", "gemini-3-pro-preview", "gemini-3-flash-preview"
|
|
271
|
+
"claude-ultra", "codex-ultra", "gemini-ultra", "sonnet", "sonnet[1m]", "opus", "opusplan", "haiku", "gpt-5.3-codex", "gpt-5.2-codex", "gpt-5.1-codex-mini", "gpt-5.1-codex-max", "gpt-5.2", "gpt-5.1", "gpt-5.1-codex", "gpt-5-codex", "gpt-5-codex-mini", "gpt-5", "gemini-2.5-pro", "gemini-2.5-flash", "gemini-3-pro-preview", "gemini-3-flash-preview"
|
|
273
272
|
|
|
274
273
|
**Prompt input**: You must provide EITHER prompt (string) OR prompt_file (file path), but not both.
|
|
275
274
|
|
|
@@ -297,11 +296,11 @@ export class ClaudeCodeServer {
|
|
|
297
296
|
},
|
|
298
297
|
model: {
|
|
299
298
|
type: 'string',
|
|
300
|
-
description: 'The model to use. Aliases: "claude-ultra", "codex-ultra" (auto high-reasoning), "gemini-ultra". Standard: "sonnet", "opus", "haiku", "gpt-5.2-codex", "gpt-5.1-codex-mini", "gpt-5.1", "gemini-2.5-pro", "gemini-3-pro-preview", "gemini-3-flash-preview", etc.',
|
|
299
|
+
description: 'The model to use. Aliases: "claude-ultra", "codex-ultra" (auto high-reasoning), "gemini-ultra". Standard: "sonnet", "sonnet[1m]", "opus", "opusplan", "haiku", "gpt-5.3-codex", "gpt-5.2-codex", "gpt-5.1-codex-mini", "gpt-5.1", "gemini-2.5-pro", "gemini-3-pro-preview", "gemini-3-flash-preview", etc.',
|
|
301
300
|
},
|
|
302
301
|
reasoning_effort: {
|
|
303
302
|
type: 'string',
|
|
304
|
-
description: 'Codex only. Sets model_reasoning_effort. Allowed: "low", "medium", "high".',
|
|
303
|
+
description: 'Codex only. Sets model_reasoning_effort. Allowed: "low", "medium", "high", "xhigh".',
|
|
305
304
|
},
|
|
306
305
|
session_id: {
|
|
307
306
|
type: 'string',
|
|
@@ -329,6 +328,10 @@ export class ClaudeCodeServer {
|
|
|
329
328
|
type: 'number',
|
|
330
329
|
description: 'The process ID returned by run tool.',
|
|
331
330
|
},
|
|
331
|
+
verbose: {
|
|
332
|
+
type: 'boolean',
|
|
333
|
+
description: 'Optional: If true, returns detailed execution information including tool usage history. Defaults to false.',
|
|
334
|
+
}
|
|
332
335
|
},
|
|
333
336
|
required: ['pid'],
|
|
334
337
|
},
|
|
@@ -454,7 +457,7 @@ export class ClaudeCodeServer {
|
|
|
454
457
|
// Special handling for codex-ultra: default to high reasoning effort if not specified
|
|
455
458
|
let reasoningEffortArg = toolArguments.reasoning_effort;
|
|
456
459
|
if (rawModel === 'codex-ultra' && !reasoningEffortArg) {
|
|
457
|
-
reasoningEffortArg = '
|
|
460
|
+
reasoningEffortArg = 'xhigh';
|
|
458
461
|
}
|
|
459
462
|
const reasoningEffort = getReasoningEffort(resolvedModel, reasoningEffortArg);
|
|
460
463
|
let agent;
|
|
@@ -506,7 +509,7 @@ export class ClaudeCodeServer {
|
|
|
506
509
|
else {
|
|
507
510
|
// Handle Claude (default)
|
|
508
511
|
cliPath = this.claudeCliPath;
|
|
509
|
-
processArgs = ['--dangerously-skip-permissions', '--output-format', 'json'];
|
|
512
|
+
processArgs = ['--dangerously-skip-permissions', '--output-format', 'stream-json', '--verbose'];
|
|
510
513
|
// Add session_id if provided (Claude only)
|
|
511
514
|
if (toolArguments.session_id && typeof toolArguments.session_id === 'string') {
|
|
512
515
|
processArgs.push('-r', toolArguments.session_id);
|
|
@@ -604,18 +607,20 @@ export class ClaudeCodeServer {
|
|
|
604
607
|
/**
|
|
605
608
|
* Helper to get process result object
|
|
606
609
|
*/
|
|
607
|
-
getProcessResultHelper(pid) {
|
|
610
|
+
getProcessResultHelper(pid, verbose = false) {
|
|
608
611
|
const process = processManager.get(pid);
|
|
609
612
|
if (!process) {
|
|
610
613
|
throw new McpError(ErrorCode.InvalidParams, `Process with PID ${pid} not found`);
|
|
611
614
|
}
|
|
612
615
|
// Parse output based on agent type
|
|
613
616
|
let agentOutput = null;
|
|
614
|
-
if (process.
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
617
|
+
if (process.toolType === 'codex') {
|
|
618
|
+
// Codex may output structured logs to stderr
|
|
619
|
+
const combinedOutput = (process.stdout || '') + '\n' + (process.stderr || '');
|
|
620
|
+
agentOutput = parseCodexOutput(combinedOutput);
|
|
621
|
+
}
|
|
622
|
+
else if (process.stdout) {
|
|
623
|
+
if (process.toolType === 'claude') {
|
|
619
624
|
agentOutput = parseClaudeOutput(process.stdout);
|
|
620
625
|
}
|
|
621
626
|
else if (process.toolType === 'gemini') {
|
|
@@ -635,7 +640,14 @@ export class ClaudeCodeServer {
|
|
|
635
640
|
};
|
|
636
641
|
// If we have valid output from agent, include it
|
|
637
642
|
if (agentOutput) {
|
|
638
|
-
|
|
643
|
+
// Filter out tools if not verbose
|
|
644
|
+
if (!verbose && agentOutput.tools) {
|
|
645
|
+
const { tools, ...rest } = agentOutput;
|
|
646
|
+
response.agentOutput = rest;
|
|
647
|
+
}
|
|
648
|
+
else {
|
|
649
|
+
response.agentOutput = agentOutput;
|
|
650
|
+
}
|
|
639
651
|
// Extract session_id if available
|
|
640
652
|
if (agentOutput.session_id) {
|
|
641
653
|
response.session_id = agentOutput.session_id;
|
|
@@ -656,7 +668,8 @@ export class ClaudeCodeServer {
|
|
|
656
668
|
throw new McpError(ErrorCode.InvalidParams, 'Missing or invalid required parameter: pid');
|
|
657
669
|
}
|
|
658
670
|
const pid = toolArguments.pid;
|
|
659
|
-
const
|
|
671
|
+
const verbose = !!toolArguments.verbose;
|
|
672
|
+
const response = this.getProcessResultHelper(pid, verbose);
|
|
660
673
|
return {
|
|
661
674
|
content: [{
|
|
662
675
|
type: 'text',
|
|
@@ -706,8 +719,8 @@ export class ClaudeCodeServer {
|
|
|
706
719
|
catch (error) {
|
|
707
720
|
throw new McpError(ErrorCode.InternalError, error.message);
|
|
708
721
|
}
|
|
709
|
-
// Collect results
|
|
710
|
-
const results = pids.map(pid => this.getProcessResultHelper(pid));
|
|
722
|
+
// Collect results (verbose=false for wait)
|
|
723
|
+
const results = pids.map(pid => this.getProcessResultHelper(pid, false));
|
|
711
724
|
return {
|
|
712
725
|
content: [{
|
|
713
726
|
type: 'text',
|