ai-cli-mcp 2.7.0 → 2.8.1
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/CHANGELOG.md +20 -0
- package/CONTRIBUTING.md +2 -2
- package/README.ja.md +3 -3
- package/README.md +3 -3
- package/dist/__tests__/cli-builder.test.js +4 -3
- package/dist/__tests__/server.test.js +1 -1
- package/dist/cli-builder.js +2 -2
- package/dist/cli-utils.js +125 -0
- package/dist/cli.js +1 -1
- package/dist/parsers.js +1 -1
- package/dist/server.js +7 -130
- package/docs/RELEASE_CHECKLIST.md +1 -1
- package/docs/development.md +3 -3
- package/package.json +4 -4
- package/src/__tests__/cli-builder.test.ts +4 -3
- package/src/__tests__/server.test.ts +1 -1
- package/src/cli-builder.ts +2 -2
- package/src/cli-utils.ts +148 -0
- package/src/cli.ts +1 -1
- package/src/parsers.ts +1 -1
- package/src/server.ts +8 -154
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,23 @@
|
|
|
1
|
+
## [2.8.1](https://github.com/mkXultra/ai-cli-mcp/compare/v2.8.0...v2.8.1) (2026-03-02)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* リポジトリ名変更に伴うURL・参照の更新 ([909cab7](https://github.com/mkXultra/ai-cli-mcp/commit/909cab77cc1e03f5880bd699ed680c32d990adb5))
|
|
7
|
+
* リポジトリ名変更に伴う再リリース ([c6a165b](https://github.com/mkXultra/ai-cli-mcp/commit/c6a165b101eae5cbc1b9d139227dfd074172a070))
|
|
8
|
+
|
|
9
|
+
# [2.8.0](https://github.com/mkXultra/claude-code-mcp/compare/v2.7.0...v2.8.0) (2026-03-01)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
### Bug Fixes
|
|
13
|
+
|
|
14
|
+
* セッション再開時に--fork-sessionフラグを追加 ([44b359a](https://github.com/mkXultra/claude-code-mcp/commit/44b359a3acf6112339424efc703c1693df1ce1e9))
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### Features
|
|
18
|
+
|
|
19
|
+
* Gemini 3.1 Pro Previewモデルを追加 ([1c314bb](https://github.com/mkXultra/claude-code-mcp/commit/1c314bba3019aea791117a974bfc5ee809e15eee))
|
|
20
|
+
|
|
1
21
|
# [2.7.0](https://github.com/mkXultra/claude-code-mcp/compare/v2.6.0...v2.7.0) (2026-03-01)
|
|
2
22
|
|
|
3
23
|
|
package/CONTRIBUTING.md
CHANGED
package/README.ja.md
CHANGED
|
@@ -19,7 +19,7 @@ Cursorなどのエディタが、複雑な手順を伴う編集や操作に苦
|
|
|
19
19
|
- 複数のAIモデルのサポート:
|
|
20
20
|
- Claude (sonnet, sonnet[1m], opus, opusplan, haiku)
|
|
21
21
|
- Codex (gpt-5.3-codex, gpt-5.2-codex, gpt-5.1-codex-mini, gpt-5.1-codex-max, など)
|
|
22
|
-
- Gemini (gemini-2.5-pro, gemini-2.5-flash, gemini-3-pro-preview, gemini-3-flash-preview)
|
|
22
|
+
- Gemini (gemini-2.5-pro, gemini-2.5-flash, gemini-3.1-pro-preview, gemini-3-pro-preview, gemini-3-flash-preview)
|
|
23
23
|
- PID追跡によるバックグラウンドプロセスの管理
|
|
24
24
|
- ツールからの構造化された出力の解析と返却
|
|
25
25
|
|
|
@@ -134,9 +134,9 @@ Claude CLI、Codex CLI、またはGemini CLIを使用してプロンプトを実
|
|
|
134
134
|
- **Ultra エイリアス:** `claude-ultra`, `codex-ultra` (自動的に high-reasoning に設定), `gemini-ultra`
|
|
135
135
|
- Claude: `sonnet`, `sonnet[1m]`, `opus`, `opusplan`, `haiku`
|
|
136
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`
|
|
137
|
-
- Gemini: `gemini-2.5-pro`, `gemini-2.5-flash`, `gemini-3-pro-preview`, `gemini-3-flash-preview`
|
|
137
|
+
- Gemini: `gemini-2.5-pro`, `gemini-2.5-flash`, `gemini-3.1-pro-preview`, `gemini-3-pro-preview`, `gemini-3-flash-preview`
|
|
138
138
|
- `reasoning_effort` (string, 任意): Codex専用。`model_reasoning_effort` を設定します(許容値: "low", "medium", "high", "xhigh")。
|
|
139
|
-
- `session_id` (string, 任意): 以前のセッションを再開するためのセッションID。対応モデル: haiku, sonnet, opus, gemini-2.5-pro, gemini-2.5-flash, gemini-3-pro-preview, gemini-3-flash-preview。
|
|
139
|
+
- `session_id` (string, 任意): 以前のセッションを再開するためのセッションID。対応モデル: haiku, sonnet, opus, gemini-2.5-pro, gemini-2.5-flash, gemini-3.1-pro-preview, gemini-3-pro-preview, gemini-3-flash-preview。
|
|
140
140
|
|
|
141
141
|
### `wait`
|
|
142
142
|
|
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@ This MCP server provides tools that can be used by LLMs to interact with AI CLI
|
|
|
18
18
|
- Run Claude CLI with all permissions bypassed (using `--dangerously-skip-permissions`)
|
|
19
19
|
- Execute Codex CLI with automatic approval mode (using `--full-auto`)
|
|
20
20
|
- Execute Gemini CLI with automatic approval mode (using `-y`)
|
|
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)
|
|
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.1-pro-preview, gemini-3-pro-preview, gemini-3-flash-preview)
|
|
22
22
|
- Manage background processes with PID tracking
|
|
23
23
|
- Parse and return structured outputs from both tools
|
|
24
24
|
|
|
@@ -133,9 +133,9 @@ Executes a prompt using Claude CLI, Codex CLI, or Gemini CLI. The appropriate CL
|
|
|
133
133
|
- **Ultra Aliases:** `claude-ultra`, `codex-ultra` (defaults to high-reasoning), `gemini-ultra`
|
|
134
134
|
- Claude: `sonnet`, `sonnet[1m]`, `opus`, `opusplan`, `haiku`
|
|
135
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`
|
|
136
|
-
- Gemini: `gemini-2.5-pro`, `gemini-2.5-flash`, `gemini-3-pro-preview`, `gemini-3-flash-preview`
|
|
136
|
+
- Gemini: `gemini-2.5-pro`, `gemini-2.5-flash`, `gemini-3.1-pro-preview`, `gemini-3-pro-preview`, `gemini-3-flash-preview`
|
|
137
137
|
- `reasoning_effort` (string, optional): Codex only. Sets `model_reasoning_effort` (allowed: "low", "medium", "high", "xhigh").
|
|
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.
|
|
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.1-pro-preview, gemini-3-pro-preview, gemini-3-flash-preview.
|
|
139
139
|
|
|
140
140
|
### `wait`
|
|
141
141
|
|
|
@@ -28,8 +28,8 @@ describe('cli-builder', () => {
|
|
|
28
28
|
it('should resolve codex-ultra to gpt-5.3-codex', () => {
|
|
29
29
|
expect(resolveModelAlias('codex-ultra')).toBe('gpt-5.3-codex');
|
|
30
30
|
});
|
|
31
|
-
it('should resolve gemini-ultra to gemini-3-pro-preview', () => {
|
|
32
|
-
expect(resolveModelAlias('gemini-ultra')).toBe('gemini-3-pro-preview');
|
|
31
|
+
it('should resolve gemini-ultra to gemini-3.1-pro-preview', () => {
|
|
32
|
+
expect(resolveModelAlias('gemini-ultra')).toBe('gemini-3.1-pro-preview');
|
|
33
33
|
});
|
|
34
34
|
it('should pass through non-alias model names', () => {
|
|
35
35
|
expect(resolveModelAlias('sonnet')).toBe('sonnet');
|
|
@@ -160,6 +160,7 @@ describe('cli-builder', () => {
|
|
|
160
160
|
});
|
|
161
161
|
expect(cmd.args).toContain('-r');
|
|
162
162
|
expect(cmd.args).toContain('ses-123');
|
|
163
|
+
expect(cmd.args).toContain('--fork-session');
|
|
163
164
|
});
|
|
164
165
|
it('should resolve claude-ultra alias to opus', () => {
|
|
165
166
|
const cmd = buildCliCommand({
|
|
@@ -271,7 +272,7 @@ describe('cli-builder', () => {
|
|
|
271
272
|
cliPaths: DEFAULT_CLI_PATHS,
|
|
272
273
|
});
|
|
273
274
|
expect(cmd.agent).toBe('gemini');
|
|
274
|
-
expect(cmd.resolvedModel).toBe('gemini-3-pro-preview');
|
|
275
|
+
expect(cmd.resolvedModel).toBe('gemini-3.1-pro-preview');
|
|
275
276
|
});
|
|
276
277
|
});
|
|
277
278
|
});
|
|
@@ -499,7 +499,7 @@ describe('ClaudeCodeServer Unit Tests', () => {
|
|
|
499
499
|
}
|
|
500
500
|
});
|
|
501
501
|
// Verify spawn was called with -r flag
|
|
502
|
-
expect(mockSpawn).toHaveBeenCalledWith(expect.any(String), expect.arrayContaining(['-r', 'test-session-123', '-p', 'test prompt']), expect.any(Object));
|
|
502
|
+
expect(mockSpawn).toHaveBeenCalledWith(expect.any(String), expect.arrayContaining(['-r', 'test-session-123', '--fork-session', '-p', 'test prompt']), expect.any(Object));
|
|
503
503
|
});
|
|
504
504
|
it('should handle session_id parameter for Codex using exec resume', async () => {
|
|
505
505
|
mockHomedir.mockReturnValue('/home/user');
|
package/dist/cli-builder.js
CHANGED
|
@@ -4,7 +4,7 @@ import { resolve as pathResolve, isAbsolute } from 'node:path';
|
|
|
4
4
|
export const MODEL_ALIASES = {
|
|
5
5
|
'claude-ultra': 'opus',
|
|
6
6
|
'codex-ultra': 'gpt-5.3-codex',
|
|
7
|
-
'gemini-ultra': 'gemini-3-pro-preview'
|
|
7
|
+
'gemini-ultra': 'gemini-3.1-pro-preview'
|
|
8
8
|
};
|
|
9
9
|
export const ALLOWED_REASONING_EFFORTS = new Set(['low', 'medium', 'high', 'xhigh']);
|
|
10
10
|
/**
|
|
@@ -134,7 +134,7 @@ export function buildCliCommand(options) {
|
|
|
134
134
|
cliPath = options.cliPaths.claude;
|
|
135
135
|
args = ['--dangerously-skip-permissions', '--output-format', 'stream-json', '--verbose'];
|
|
136
136
|
if (options.session_id && typeof options.session_id === 'string') {
|
|
137
|
-
args.push('-r', options.session_id);
|
|
137
|
+
args.push('-r', options.session_id, '--fork-session');
|
|
138
138
|
}
|
|
139
139
|
args.push('-p', prompt);
|
|
140
140
|
if (resolvedModel) {
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
// Define debugMode globally using const
|
|
6
|
+
const debugMode = process.env.MCP_CLAUDE_DEBUG === 'true';
|
|
7
|
+
// Dedicated debug logging function
|
|
8
|
+
export function debugLog(message, ...optionalParams) {
|
|
9
|
+
if (debugMode) {
|
|
10
|
+
console.error(message, ...optionalParams);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Determine the Gemini CLI command/path.
|
|
15
|
+
* Similar to findClaudeCli but for Gemini
|
|
16
|
+
*/
|
|
17
|
+
export function findGeminiCli() {
|
|
18
|
+
debugLog('[Debug] Attempting to find Gemini CLI...');
|
|
19
|
+
// Check for custom CLI name from environment variable
|
|
20
|
+
const customCliName = process.env.GEMINI_CLI_NAME;
|
|
21
|
+
if (customCliName) {
|
|
22
|
+
debugLog(`[Debug] Using custom Gemini CLI name from GEMINI_CLI_NAME: ${customCliName}`);
|
|
23
|
+
// If it's an absolute path, use it directly
|
|
24
|
+
if (path.isAbsolute(customCliName)) {
|
|
25
|
+
debugLog(`[Debug] GEMINI_CLI_NAME is an absolute path: ${customCliName}`);
|
|
26
|
+
return customCliName;
|
|
27
|
+
}
|
|
28
|
+
// If it starts with ~ or ./, reject as relative paths are not allowed
|
|
29
|
+
if (customCliName.startsWith('./') || customCliName.startsWith('../') || customCliName.includes('/')) {
|
|
30
|
+
throw new Error(`Invalid GEMINI_CLI_NAME: Relative paths are not allowed. Use either a simple name (e.g., 'gemini') or an absolute path (e.g., '/tmp/gemini-test')`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const cliName = customCliName || 'gemini';
|
|
34
|
+
// Try local install path: ~/.gemini/local/gemini
|
|
35
|
+
const userPath = join(homedir(), '.gemini', 'local', 'gemini');
|
|
36
|
+
debugLog(`[Debug] Checking for Gemini CLI at local user path: ${userPath}`);
|
|
37
|
+
if (existsSync(userPath)) {
|
|
38
|
+
debugLog(`[Debug] Found Gemini CLI at local user path: ${userPath}. Using this path.`);
|
|
39
|
+
return userPath;
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
debugLog(`[Debug] Gemini CLI not found at local user path: ${userPath}.`);
|
|
43
|
+
}
|
|
44
|
+
// Fallback to CLI name (PATH lookup)
|
|
45
|
+
debugLog(`[Debug] Falling back to "${cliName}" command name, relying on spawn/PATH lookup.`);
|
|
46
|
+
console.warn(`[Warning] Gemini CLI not found at ~/.gemini/local/gemini. Falling back to "${cliName}" in PATH. Ensure it is installed and accessible.`);
|
|
47
|
+
return cliName;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Determine the Codex CLI command/path.
|
|
51
|
+
* Similar to findClaudeCli but for Codex
|
|
52
|
+
*/
|
|
53
|
+
export function findCodexCli() {
|
|
54
|
+
debugLog('[Debug] Attempting to find Codex CLI...');
|
|
55
|
+
// Check for custom CLI name from environment variable
|
|
56
|
+
const customCliName = process.env.CODEX_CLI_NAME;
|
|
57
|
+
if (customCliName) {
|
|
58
|
+
debugLog(`[Debug] Using custom Codex CLI name from CODEX_CLI_NAME: ${customCliName}`);
|
|
59
|
+
// If it's an absolute path, use it directly
|
|
60
|
+
if (path.isAbsolute(customCliName)) {
|
|
61
|
+
debugLog(`[Debug] CODEX_CLI_NAME is an absolute path: ${customCliName}`);
|
|
62
|
+
return customCliName;
|
|
63
|
+
}
|
|
64
|
+
// If it starts with ~ or ./, reject as relative paths are not allowed
|
|
65
|
+
if (customCliName.startsWith('./') || customCliName.startsWith('../') || customCliName.includes('/')) {
|
|
66
|
+
throw new Error(`Invalid CODEX_CLI_NAME: Relative paths are not allowed. Use either a simple name (e.g., 'codex') or an absolute path (e.g., '/tmp/codex-test')`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const cliName = customCliName || 'codex';
|
|
70
|
+
// Try local install path: ~/.codex/local/codex
|
|
71
|
+
const userPath = join(homedir(), '.codex', 'local', 'codex');
|
|
72
|
+
debugLog(`[Debug] Checking for Codex CLI at local user path: ${userPath}`);
|
|
73
|
+
if (existsSync(userPath)) {
|
|
74
|
+
debugLog(`[Debug] Found Codex CLI at local user path: ${userPath}. Using this path.`);
|
|
75
|
+
return userPath;
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
debugLog(`[Debug] Codex CLI not found at local user path: ${userPath}.`);
|
|
79
|
+
}
|
|
80
|
+
// Fallback to CLI name (PATH lookup)
|
|
81
|
+
debugLog(`[Debug] Falling back to "${cliName}" command name, relying on spawn/PATH lookup.`);
|
|
82
|
+
console.warn(`[Warning] Codex CLI not found at ~/.codex/local/codex. Falling back to "${cliName}" in PATH. Ensure it is installed and accessible.`);
|
|
83
|
+
return cliName;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Determine the Claude CLI command/path.
|
|
87
|
+
* 1. Checks for CLAUDE_CLI_NAME environment variable:
|
|
88
|
+
* - If absolute path, uses it directly
|
|
89
|
+
* - If relative path, throws error
|
|
90
|
+
* - If simple name, continues with path resolution
|
|
91
|
+
* 2. Checks for Claude CLI at the local user path: ~/.claude/local/claude.
|
|
92
|
+
* 3. If not found, defaults to the CLI name (or 'claude'), relying on the system's PATH for lookup.
|
|
93
|
+
*/
|
|
94
|
+
export function findClaudeCli() {
|
|
95
|
+
debugLog('[Debug] Attempting to find Claude CLI...');
|
|
96
|
+
// Check for custom CLI name from environment variable
|
|
97
|
+
const customCliName = process.env.CLAUDE_CLI_NAME;
|
|
98
|
+
if (customCliName) {
|
|
99
|
+
debugLog(`[Debug] Using custom Claude CLI name from CLAUDE_CLI_NAME: ${customCliName}`);
|
|
100
|
+
// If it's an absolute path, use it directly
|
|
101
|
+
if (path.isAbsolute(customCliName)) {
|
|
102
|
+
debugLog(`[Debug] CLAUDE_CLI_NAME is an absolute path: ${customCliName}`);
|
|
103
|
+
return customCliName;
|
|
104
|
+
}
|
|
105
|
+
// If it starts with ~ or ./, reject as relative paths are not allowed
|
|
106
|
+
if (customCliName.startsWith('./') || customCliName.startsWith('../') || customCliName.includes('/')) {
|
|
107
|
+
throw new Error(`Invalid CLAUDE_CLI_NAME: Relative paths are not allowed. Use either a simple name (e.g., 'claude') or an absolute path (e.g., '/tmp/claude-test')`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
const cliName = customCliName || 'claude';
|
|
111
|
+
// Try local install path: ~/.claude/local/claude (using the original name for local installs)
|
|
112
|
+
const userPath = join(homedir(), '.claude', 'local', 'claude');
|
|
113
|
+
debugLog(`[Debug] Checking for Claude CLI at local user path: ${userPath}`);
|
|
114
|
+
if (existsSync(userPath)) {
|
|
115
|
+
debugLog(`[Debug] Found Claude CLI at local user path: ${userPath}. Using this path.`);
|
|
116
|
+
return userPath;
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
debugLog(`[Debug] Claude CLI not found at local user path: ${userPath}.`);
|
|
120
|
+
}
|
|
121
|
+
// 3. Fallback to CLI name (PATH lookup)
|
|
122
|
+
debugLog(`[Debug] Falling back to "${cliName}" command name, relying on spawn/PATH lookup.`);
|
|
123
|
+
console.warn(`[Warning] Claude CLI not found at ~/.claude/local/claude. Falling back to "${cliName}" in PATH. Ensure it is installed and accessible.`);
|
|
124
|
+
return cliName;
|
|
125
|
+
}
|
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { spawn } from 'node:child_process';
|
|
3
3
|
import { buildCliCommand } from './cli-builder.js';
|
|
4
|
-
import { findClaudeCli, findCodexCli, findGeminiCli } from './
|
|
4
|
+
import { findClaudeCli, findCodexCli, findGeminiCli } from './cli-utils.js';
|
|
5
5
|
/**
|
|
6
6
|
* Minimal argv parser. No external dependencies.
|
|
7
7
|
* Supports: --key value, --key=value
|
package/dist/parsers.js
CHANGED
package/dist/server.js
CHANGED
|
@@ -3,143 +3,20 @@ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
|
3
3
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
4
|
import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from '@modelcontextprotocol/sdk/types.js';
|
|
5
5
|
import { spawn } from 'node:child_process';
|
|
6
|
-
import { existsSync } from 'node:fs';
|
|
7
|
-
import { homedir } from 'node:os';
|
|
8
|
-
import { join } from 'node:path';
|
|
9
|
-
import * as path from 'path';
|
|
10
6
|
import { parseCodexOutput, parseClaudeOutput, parseGeminiOutput } from './parsers.js';
|
|
11
7
|
import { buildCliCommand } from './cli-builder.js';
|
|
8
|
+
import { debugLog, findClaudeCli, findCodexCli, findGeminiCli } from './cli-utils.js';
|
|
9
|
+
// Re-export for backward compatibility
|
|
10
|
+
export { debugLog, findClaudeCli, findCodexCli, findGeminiCli } from './cli-utils.js';
|
|
11
|
+
export { resolveModelAlias } from './cli-builder.js';
|
|
12
12
|
// Server version - update this when releasing new versions
|
|
13
13
|
const SERVER_VERSION = "2.2.0";
|
|
14
|
-
// Define debugMode globally using const
|
|
15
|
-
const debugMode = process.env.MCP_CLAUDE_DEBUG === 'true';
|
|
16
14
|
// Track if this is the first tool use for version printing
|
|
17
15
|
let isFirstToolUse = true;
|
|
18
16
|
// Capture server startup time when the module loads
|
|
19
17
|
const serverStartupTime = new Date().toISOString();
|
|
20
18
|
// Global process manager
|
|
21
19
|
const processManager = new Map();
|
|
22
|
-
// Dedicated debug logging function
|
|
23
|
-
export function debugLog(message, ...optionalParams) {
|
|
24
|
-
if (debugMode) {
|
|
25
|
-
console.error(message, ...optionalParams);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* Determine the Gemini CLI command/path.
|
|
30
|
-
* Similar to findClaudeCli but for Gemini
|
|
31
|
-
*/
|
|
32
|
-
export function findGeminiCli() {
|
|
33
|
-
debugLog('[Debug] Attempting to find Gemini CLI...');
|
|
34
|
-
// Check for custom CLI name from environment variable
|
|
35
|
-
const customCliName = process.env.GEMINI_CLI_NAME;
|
|
36
|
-
if (customCliName) {
|
|
37
|
-
debugLog(`[Debug] Using custom Gemini CLI name from GEMINI_CLI_NAME: ${customCliName}`);
|
|
38
|
-
// If it's an absolute path, use it directly
|
|
39
|
-
if (path.isAbsolute(customCliName)) {
|
|
40
|
-
debugLog(`[Debug] GEMINI_CLI_NAME is an absolute path: ${customCliName}`);
|
|
41
|
-
return customCliName;
|
|
42
|
-
}
|
|
43
|
-
// If it starts with ~ or ./, reject as relative paths are not allowed
|
|
44
|
-
if (customCliName.startsWith('./') || customCliName.startsWith('../') || customCliName.includes('/')) {
|
|
45
|
-
throw new Error(`Invalid GEMINI_CLI_NAME: Relative paths are not allowed. Use either a simple name (e.g., 'gemini') or an absolute path (e.g., '/tmp/gemini-test')`);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
const cliName = customCliName || 'gemini';
|
|
49
|
-
// Try local install path: ~/.gemini/local/gemini
|
|
50
|
-
const userPath = join(homedir(), '.gemini', 'local', 'gemini');
|
|
51
|
-
debugLog(`[Debug] Checking for Gemini CLI at local user path: ${userPath}`);
|
|
52
|
-
if (existsSync(userPath)) {
|
|
53
|
-
debugLog(`[Debug] Found Gemini CLI at local user path: ${userPath}. Using this path.`);
|
|
54
|
-
return userPath;
|
|
55
|
-
}
|
|
56
|
-
else {
|
|
57
|
-
debugLog(`[Debug] Gemini CLI not found at local user path: ${userPath}.`);
|
|
58
|
-
}
|
|
59
|
-
// Fallback to CLI name (PATH lookup)
|
|
60
|
-
debugLog(`[Debug] Falling back to "${cliName}" command name, relying on spawn/PATH lookup.`);
|
|
61
|
-
console.warn(`[Warning] Gemini CLI not found at ~/.gemini/local/gemini. Falling back to "${cliName}" in PATH. Ensure it is installed and accessible.`);
|
|
62
|
-
return cliName;
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* Determine the Codex CLI command/path.
|
|
66
|
-
* Similar to findClaudeCli but for Codex
|
|
67
|
-
*/
|
|
68
|
-
export function findCodexCli() {
|
|
69
|
-
debugLog('[Debug] Attempting to find Codex CLI...');
|
|
70
|
-
// Check for custom CLI name from environment variable
|
|
71
|
-
const customCliName = process.env.CODEX_CLI_NAME;
|
|
72
|
-
if (customCliName) {
|
|
73
|
-
debugLog(`[Debug] Using custom Codex CLI name from CODEX_CLI_NAME: ${customCliName}`);
|
|
74
|
-
// If it's an absolute path, use it directly
|
|
75
|
-
if (path.isAbsolute(customCliName)) {
|
|
76
|
-
debugLog(`[Debug] CODEX_CLI_NAME is an absolute path: ${customCliName}`);
|
|
77
|
-
return customCliName;
|
|
78
|
-
}
|
|
79
|
-
// If it starts with ~ or ./, reject as relative paths are not allowed
|
|
80
|
-
if (customCliName.startsWith('./') || customCliName.startsWith('../') || customCliName.includes('/')) {
|
|
81
|
-
throw new Error(`Invalid CODEX_CLI_NAME: Relative paths are not allowed. Use either a simple name (e.g., 'codex') or an absolute path (e.g., '/tmp/codex-test')`);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
const cliName = customCliName || 'codex';
|
|
85
|
-
// Try local install path: ~/.codex/local/codex
|
|
86
|
-
const userPath = join(homedir(), '.codex', 'local', 'codex');
|
|
87
|
-
debugLog(`[Debug] Checking for Codex CLI at local user path: ${userPath}`);
|
|
88
|
-
if (existsSync(userPath)) {
|
|
89
|
-
debugLog(`[Debug] Found Codex CLI at local user path: ${userPath}. Using this path.`);
|
|
90
|
-
return userPath;
|
|
91
|
-
}
|
|
92
|
-
else {
|
|
93
|
-
debugLog(`[Debug] Codex CLI not found at local user path: ${userPath}.`);
|
|
94
|
-
}
|
|
95
|
-
// Fallback to CLI name (PATH lookup)
|
|
96
|
-
debugLog(`[Debug] Falling back to "${cliName}" command name, relying on spawn/PATH lookup.`);
|
|
97
|
-
console.warn(`[Warning] Codex CLI not found at ~/.codex/local/codex. Falling back to "${cliName}" in PATH. Ensure it is installed and accessible.`);
|
|
98
|
-
return cliName;
|
|
99
|
-
}
|
|
100
|
-
/**
|
|
101
|
-
* Determine the Claude CLI command/path.
|
|
102
|
-
* 1. Checks for CLAUDE_CLI_NAME environment variable:
|
|
103
|
-
* - If absolute path, uses it directly
|
|
104
|
-
* - If relative path, throws error
|
|
105
|
-
* - If simple name, continues with path resolution
|
|
106
|
-
* 2. Checks for Claude CLI at the local user path: ~/.claude/local/claude.
|
|
107
|
-
* 3. If not found, defaults to the CLI name (or 'claude'), relying on the system's PATH for lookup.
|
|
108
|
-
*/
|
|
109
|
-
export function findClaudeCli() {
|
|
110
|
-
debugLog('[Debug] Attempting to find Claude CLI...');
|
|
111
|
-
// Check for custom CLI name from environment variable
|
|
112
|
-
const customCliName = process.env.CLAUDE_CLI_NAME;
|
|
113
|
-
if (customCliName) {
|
|
114
|
-
debugLog(`[Debug] Using custom Claude CLI name from CLAUDE_CLI_NAME: ${customCliName}`);
|
|
115
|
-
// If it's an absolute path, use it directly
|
|
116
|
-
if (path.isAbsolute(customCliName)) {
|
|
117
|
-
debugLog(`[Debug] CLAUDE_CLI_NAME is an absolute path: ${customCliName}`);
|
|
118
|
-
return customCliName;
|
|
119
|
-
}
|
|
120
|
-
// If it starts with ~ or ./, reject as relative paths are not allowed
|
|
121
|
-
if (customCliName.startsWith('./') || customCliName.startsWith('../') || customCliName.includes('/')) {
|
|
122
|
-
throw new Error(`Invalid CLAUDE_CLI_NAME: Relative paths are not allowed. Use either a simple name (e.g., 'claude') or an absolute path (e.g., '/tmp/claude-test')`);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
const cliName = customCliName || 'claude';
|
|
126
|
-
// Try local install path: ~/.claude/local/claude (using the original name for local installs)
|
|
127
|
-
const userPath = join(homedir(), '.claude', 'local', 'claude');
|
|
128
|
-
debugLog(`[Debug] Checking for Claude CLI at local user path: ${userPath}`);
|
|
129
|
-
if (existsSync(userPath)) {
|
|
130
|
-
debugLog(`[Debug] Found Claude CLI at local user path: ${userPath}. Using this path.`);
|
|
131
|
-
return userPath;
|
|
132
|
-
}
|
|
133
|
-
else {
|
|
134
|
-
debugLog(`[Debug] Claude CLI not found at local user path: ${userPath}.`);
|
|
135
|
-
}
|
|
136
|
-
// 3. Fallback to CLI name (PATH lookup)
|
|
137
|
-
debugLog(`[Debug] Falling back to "${cliName}" command name, relying on spawn/PATH lookup.`);
|
|
138
|
-
console.warn(`[Warning] Claude CLI not found at ~/.claude/local/claude. Falling back to "${cliName}" in PATH. Ensure it is installed and accessible.`);
|
|
139
|
-
return cliName;
|
|
140
|
-
}
|
|
141
|
-
// Re-export resolveModelAlias for backward compatibility
|
|
142
|
-
export { resolveModelAlias } from './cli-builder.js';
|
|
143
20
|
// Ensure spawnAsync is defined correctly *before* the class
|
|
144
21
|
export async function spawnAsync(command, args, options) {
|
|
145
22
|
return new Promise((resolve, reject) => {
|
|
@@ -239,7 +116,7 @@ export class ClaudeCodeServer {
|
|
|
239
116
|
**IMPORTANT**: This tool now returns immediately with a PID. Use other tools to check status and get results.
|
|
240
117
|
|
|
241
118
|
**Supported models**:
|
|
242
|
-
"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"
|
|
119
|
+
"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.1-pro-preview", "gemini-3-pro-preview", "gemini-3-flash-preview"
|
|
243
120
|
|
|
244
121
|
**Prompt input**: You must provide EITHER prompt (string) OR prompt_file (file path), but not both.
|
|
245
122
|
|
|
@@ -267,7 +144,7 @@ export class ClaudeCodeServer {
|
|
|
267
144
|
},
|
|
268
145
|
model: {
|
|
269
146
|
type: 'string',
|
|
270
|
-
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.',
|
|
147
|
+
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.1-pro-preview", "gemini-3-pro-preview", "gemini-3-flash-preview", etc.',
|
|
271
148
|
},
|
|
272
149
|
reasoning_effort: {
|
|
273
150
|
type: 'string',
|
|
@@ -275,7 +152,7 @@ export class ClaudeCodeServer {
|
|
|
275
152
|
},
|
|
276
153
|
session_id: {
|
|
277
154
|
type: 'string',
|
|
278
|
-
description: '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.',
|
|
155
|
+
description: 'Optional session ID to resume a previous session. Supported for: haiku, sonnet, opus, gemini-2.5-pro, gemini-2.5-flash, gemini-3.1-pro-preview, gemini-3-pro-preview, gemini-3-flash-preview.',
|
|
279
156
|
},
|
|
280
157
|
},
|
|
281
158
|
required: ['workFolder'],
|
package/docs/development.md
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
6
|
# Clone the repository
|
|
7
|
-
git clone https://github.com/mkXultra/
|
|
8
|
-
cd
|
|
7
|
+
git clone https://github.com/mkXultra/ai-cli-mcp.git
|
|
8
|
+
cd ai-cli-mcp
|
|
9
9
|
|
|
10
10
|
# Install dependencies
|
|
11
11
|
npm install
|
|
@@ -153,4 +153,4 @@ These can be set in your shell environment or within the `env` block of your `mc
|
|
|
153
153
|
|
|
154
154
|
Contributions are welcome!
|
|
155
155
|
|
|
156
|
-
Submit issues and pull requests to the [GitHub repository](https://github.com/mkXultra/
|
|
156
|
+
Submit issues and pull requests to the [GitHub repository](https://github.com/mkXultra/ai-cli-mcp).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-cli-mcp",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.8.1",
|
|
4
4
|
"description": "MCP server for AI CLI tools (Claude, Codex, and Gemini) with background process management",
|
|
5
5
|
"author": "mkXultra",
|
|
6
6
|
"license": "MIT",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
},
|
|
40
40
|
"repository": {
|
|
41
41
|
"type": "git",
|
|
42
|
-
"url": "git+https://github.com/mkXultra/
|
|
42
|
+
"url": "git+https://github.com/mkXultra/ai-cli-mcp.git"
|
|
43
43
|
},
|
|
44
44
|
"keywords": [
|
|
45
45
|
"mcp",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"gpt5"
|
|
53
53
|
],
|
|
54
54
|
"bugs": {
|
|
55
|
-
"url": "https://github.com/mkXultra/
|
|
55
|
+
"url": "https://github.com/mkXultra/ai-cli-mcp/issues"
|
|
56
56
|
},
|
|
57
|
-
"homepage": "https://github.com/mkXultra/
|
|
57
|
+
"homepage": "https://github.com/mkXultra/ai-cli-mcp#readme"
|
|
58
58
|
}
|
|
@@ -40,8 +40,8 @@ describe('cli-builder', () => {
|
|
|
40
40
|
expect(resolveModelAlias('codex-ultra')).toBe('gpt-5.3-codex');
|
|
41
41
|
});
|
|
42
42
|
|
|
43
|
-
it('should resolve gemini-ultra to gemini-3-pro-preview', () => {
|
|
44
|
-
expect(resolveModelAlias('gemini-ultra')).toBe('gemini-3-pro-preview');
|
|
43
|
+
it('should resolve gemini-ultra to gemini-3.1-pro-preview', () => {
|
|
44
|
+
expect(resolveModelAlias('gemini-ultra')).toBe('gemini-3.1-pro-preview');
|
|
45
45
|
});
|
|
46
46
|
|
|
47
47
|
it('should pass through non-alias model names', () => {
|
|
@@ -209,6 +209,7 @@ describe('cli-builder', () => {
|
|
|
209
209
|
|
|
210
210
|
expect(cmd.args).toContain('-r');
|
|
211
211
|
expect(cmd.args).toContain('ses-123');
|
|
212
|
+
expect(cmd.args).toContain('--fork-session');
|
|
212
213
|
});
|
|
213
214
|
|
|
214
215
|
it('should resolve claude-ultra alias to opus', () => {
|
|
@@ -338,7 +339,7 @@ describe('cli-builder', () => {
|
|
|
338
339
|
});
|
|
339
340
|
|
|
340
341
|
expect(cmd.agent).toBe('gemini');
|
|
341
|
-
expect(cmd.resolvedModel).toBe('gemini-3-pro-preview');
|
|
342
|
+
expect(cmd.resolvedModel).toBe('gemini-3.1-pro-preview');
|
|
342
343
|
});
|
|
343
344
|
});
|
|
344
345
|
});
|
|
@@ -624,7 +624,7 @@ describe('ClaudeCodeServer Unit Tests', () => {
|
|
|
624
624
|
// Verify spawn was called with -r flag
|
|
625
625
|
expect(mockSpawn).toHaveBeenCalledWith(
|
|
626
626
|
expect.any(String),
|
|
627
|
-
expect.arrayContaining(['-r', 'test-session-123', '-p', 'test prompt']),
|
|
627
|
+
expect.arrayContaining(['-r', 'test-session-123', '--fork-session', '-p', 'test prompt']),
|
|
628
628
|
expect.any(Object)
|
|
629
629
|
);
|
|
630
630
|
});
|
package/src/cli-builder.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { resolve as pathResolve, isAbsolute } from 'node:path';
|
|
|
5
5
|
export const MODEL_ALIASES: Record<string, string> = {
|
|
6
6
|
'claude-ultra': 'opus',
|
|
7
7
|
'codex-ultra': 'gpt-5.3-codex',
|
|
8
|
-
'gemini-ultra': 'gemini-3-pro-preview'
|
|
8
|
+
'gemini-ultra': 'gemini-3.1-pro-preview'
|
|
9
9
|
};
|
|
10
10
|
|
|
11
11
|
export const ALLOWED_REASONING_EFFORTS = new Set(['low', 'medium', 'high', 'xhigh']);
|
|
@@ -177,7 +177,7 @@ export function buildCliCommand(options: BuildCliCommandOptions): CliCommand {
|
|
|
177
177
|
args = ['--dangerously-skip-permissions', '--output-format', 'stream-json', '--verbose'];
|
|
178
178
|
|
|
179
179
|
if (options.session_id && typeof options.session_id === 'string') {
|
|
180
|
-
args.push('-r', options.session_id);
|
|
180
|
+
args.push('-r', options.session_id, '--fork-session');
|
|
181
181
|
}
|
|
182
182
|
|
|
183
183
|
args.push('-p', prompt);
|
package/src/cli-utils.ts
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
|
|
6
|
+
// Define debugMode globally using const
|
|
7
|
+
const debugMode = process.env.MCP_CLAUDE_DEBUG === 'true';
|
|
8
|
+
|
|
9
|
+
// Dedicated debug logging function
|
|
10
|
+
export function debugLog(message?: any, ...optionalParams: any[]): void {
|
|
11
|
+
if (debugMode) {
|
|
12
|
+
console.error(message, ...optionalParams);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Determine the Gemini CLI command/path.
|
|
18
|
+
* Similar to findClaudeCli but for Gemini
|
|
19
|
+
*/
|
|
20
|
+
export function findGeminiCli(): string {
|
|
21
|
+
debugLog('[Debug] Attempting to find Gemini CLI...');
|
|
22
|
+
|
|
23
|
+
// Check for custom CLI name from environment variable
|
|
24
|
+
const customCliName = process.env.GEMINI_CLI_NAME;
|
|
25
|
+
if (customCliName) {
|
|
26
|
+
debugLog(`[Debug] Using custom Gemini CLI name from GEMINI_CLI_NAME: ${customCliName}`);
|
|
27
|
+
|
|
28
|
+
// If it's an absolute path, use it directly
|
|
29
|
+
if (path.isAbsolute(customCliName)) {
|
|
30
|
+
debugLog(`[Debug] GEMINI_CLI_NAME is an absolute path: ${customCliName}`);
|
|
31
|
+
return customCliName;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// If it starts with ~ or ./, reject as relative paths are not allowed
|
|
35
|
+
if (customCliName.startsWith('./') || customCliName.startsWith('../') || customCliName.includes('/')) {
|
|
36
|
+
throw new Error(`Invalid GEMINI_CLI_NAME: Relative paths are not allowed. Use either a simple name (e.g., 'gemini') or an absolute path (e.g., '/tmp/gemini-test')`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const cliName = customCliName || 'gemini';
|
|
41
|
+
|
|
42
|
+
// Try local install path: ~/.gemini/local/gemini
|
|
43
|
+
const userPath = join(homedir(), '.gemini', 'local', 'gemini');
|
|
44
|
+
debugLog(`[Debug] Checking for Gemini CLI at local user path: ${userPath}`);
|
|
45
|
+
|
|
46
|
+
if (existsSync(userPath)) {
|
|
47
|
+
debugLog(`[Debug] Found Gemini CLI at local user path: ${userPath}. Using this path.`);
|
|
48
|
+
return userPath;
|
|
49
|
+
} else {
|
|
50
|
+
debugLog(`[Debug] Gemini CLI not found at local user path: ${userPath}.`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Fallback to CLI name (PATH lookup)
|
|
54
|
+
debugLog(`[Debug] Falling back to "${cliName}" command name, relying on spawn/PATH lookup.`);
|
|
55
|
+
console.warn(`[Warning] Gemini CLI not found at ~/.gemini/local/gemini. Falling back to "${cliName}" in PATH. Ensure it is installed and accessible.`);
|
|
56
|
+
return cliName;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Determine the Codex CLI command/path.
|
|
61
|
+
* Similar to findClaudeCli but for Codex
|
|
62
|
+
*/
|
|
63
|
+
export function findCodexCli(): string {
|
|
64
|
+
debugLog('[Debug] Attempting to find Codex CLI...');
|
|
65
|
+
|
|
66
|
+
// Check for custom CLI name from environment variable
|
|
67
|
+
const customCliName = process.env.CODEX_CLI_NAME;
|
|
68
|
+
if (customCliName) {
|
|
69
|
+
debugLog(`[Debug] Using custom Codex CLI name from CODEX_CLI_NAME: ${customCliName}`);
|
|
70
|
+
|
|
71
|
+
// If it's an absolute path, use it directly
|
|
72
|
+
if (path.isAbsolute(customCliName)) {
|
|
73
|
+
debugLog(`[Debug] CODEX_CLI_NAME is an absolute path: ${customCliName}`);
|
|
74
|
+
return customCliName;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// If it starts with ~ or ./, reject as relative paths are not allowed
|
|
78
|
+
if (customCliName.startsWith('./') || customCliName.startsWith('../') || customCliName.includes('/')) {
|
|
79
|
+
throw new Error(`Invalid CODEX_CLI_NAME: Relative paths are not allowed. Use either a simple name (e.g., 'codex') or an absolute path (e.g., '/tmp/codex-test')`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const cliName = customCliName || 'codex';
|
|
84
|
+
|
|
85
|
+
// Try local install path: ~/.codex/local/codex
|
|
86
|
+
const userPath = join(homedir(), '.codex', 'local', 'codex');
|
|
87
|
+
debugLog(`[Debug] Checking for Codex CLI at local user path: ${userPath}`);
|
|
88
|
+
|
|
89
|
+
if (existsSync(userPath)) {
|
|
90
|
+
debugLog(`[Debug] Found Codex CLI at local user path: ${userPath}. Using this path.`);
|
|
91
|
+
return userPath;
|
|
92
|
+
} else {
|
|
93
|
+
debugLog(`[Debug] Codex CLI not found at local user path: ${userPath}.`);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Fallback to CLI name (PATH lookup)
|
|
97
|
+
debugLog(`[Debug] Falling back to "${cliName}" command name, relying on spawn/PATH lookup.`);
|
|
98
|
+
console.warn(`[Warning] Codex CLI not found at ~/.codex/local/codex. Falling back to "${cliName}" in PATH. Ensure it is installed and accessible.`);
|
|
99
|
+
return cliName;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Determine the Claude CLI command/path.
|
|
104
|
+
* 1. Checks for CLAUDE_CLI_NAME environment variable:
|
|
105
|
+
* - If absolute path, uses it directly
|
|
106
|
+
* - If relative path, throws error
|
|
107
|
+
* - If simple name, continues with path resolution
|
|
108
|
+
* 2. Checks for Claude CLI at the local user path: ~/.claude/local/claude.
|
|
109
|
+
* 3. If not found, defaults to the CLI name (or 'claude'), relying on the system's PATH for lookup.
|
|
110
|
+
*/
|
|
111
|
+
export function findClaudeCli(): string {
|
|
112
|
+
debugLog('[Debug] Attempting to find Claude CLI...');
|
|
113
|
+
|
|
114
|
+
// Check for custom CLI name from environment variable
|
|
115
|
+
const customCliName = process.env.CLAUDE_CLI_NAME;
|
|
116
|
+
if (customCliName) {
|
|
117
|
+
debugLog(`[Debug] Using custom Claude CLI name from CLAUDE_CLI_NAME: ${customCliName}`);
|
|
118
|
+
|
|
119
|
+
// If it's an absolute path, use it directly
|
|
120
|
+
if (path.isAbsolute(customCliName)) {
|
|
121
|
+
debugLog(`[Debug] CLAUDE_CLI_NAME is an absolute path: ${customCliName}`);
|
|
122
|
+
return customCliName;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// If it starts with ~ or ./, reject as relative paths are not allowed
|
|
126
|
+
if (customCliName.startsWith('./') || customCliName.startsWith('../') || customCliName.includes('/')) {
|
|
127
|
+
throw new Error(`Invalid CLAUDE_CLI_NAME: Relative paths are not allowed. Use either a simple name (e.g., 'claude') or an absolute path (e.g., '/tmp/claude-test')`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const cliName = customCliName || 'claude';
|
|
132
|
+
|
|
133
|
+
// Try local install path: ~/.claude/local/claude (using the original name for local installs)
|
|
134
|
+
const userPath = join(homedir(), '.claude', 'local', 'claude');
|
|
135
|
+
debugLog(`[Debug] Checking for Claude CLI at local user path: ${userPath}`);
|
|
136
|
+
|
|
137
|
+
if (existsSync(userPath)) {
|
|
138
|
+
debugLog(`[Debug] Found Claude CLI at local user path: ${userPath}. Using this path.`);
|
|
139
|
+
return userPath;
|
|
140
|
+
} else {
|
|
141
|
+
debugLog(`[Debug] Claude CLI not found at local user path: ${userPath}.`);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// 3. Fallback to CLI name (PATH lookup)
|
|
145
|
+
debugLog(`[Debug] Falling back to "${cliName}" command name, relying on spawn/PATH lookup.`);
|
|
146
|
+
console.warn(`[Warning] Claude CLI not found at ~/.claude/local/claude. Falling back to "${cliName}" in PATH. Ensure it is installed and accessible.`);
|
|
147
|
+
return cliName;
|
|
148
|
+
}
|
package/src/cli.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { spawn } from 'node:child_process';
|
|
3
3
|
import { buildCliCommand } from './cli-builder.js';
|
|
4
|
-
import { findClaudeCli, findCodexCli, findGeminiCli } from './
|
|
4
|
+
import { findClaudeCli, findCodexCli, findGeminiCli } from './cli-utils.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Minimal argv parser. No external dependencies.
|
package/src/parsers.ts
CHANGED
package/src/server.ts
CHANGED
|
@@ -9,19 +9,17 @@ import {
|
|
|
9
9
|
type ServerResult,
|
|
10
10
|
} from '@modelcontextprotocol/sdk/types.js';
|
|
11
11
|
import { spawn, ChildProcess } from 'node:child_process';
|
|
12
|
-
import { existsSync } from 'node:fs';
|
|
13
|
-
import { homedir } from 'node:os';
|
|
14
|
-
import { join } from 'node:path';
|
|
15
|
-
import * as path from 'path';
|
|
16
12
|
import { parseCodexOutput, parseClaudeOutput, parseGeminiOutput } from './parsers.js';
|
|
17
13
|
import { buildCliCommand } from './cli-builder.js';
|
|
14
|
+
import { debugLog, findClaudeCli, findCodexCli, findGeminiCli } from './cli-utils.js';
|
|
15
|
+
|
|
16
|
+
// Re-export for backward compatibility
|
|
17
|
+
export { debugLog, findClaudeCli, findCodexCli, findGeminiCli } from './cli-utils.js';
|
|
18
|
+
export { resolveModelAlias } from './cli-builder.js';
|
|
18
19
|
|
|
19
20
|
// Server version - update this when releasing new versions
|
|
20
21
|
const SERVER_VERSION = "2.2.0";
|
|
21
22
|
|
|
22
|
-
// Define debugMode globally using const
|
|
23
|
-
const debugMode = process.env.MCP_CLAUDE_DEBUG === 'true';
|
|
24
|
-
|
|
25
23
|
// Track if this is the first tool use for version printing
|
|
26
24
|
let isFirstToolUse = true;
|
|
27
25
|
|
|
@@ -53,150 +51,6 @@ interface ProcessListItem {
|
|
|
53
51
|
// Global process manager
|
|
54
52
|
const processManager = new Map<number, ClaudeProcess>();
|
|
55
53
|
|
|
56
|
-
// Dedicated debug logging function
|
|
57
|
-
export function debugLog(message?: any, ...optionalParams: any[]): void {
|
|
58
|
-
if (debugMode) {
|
|
59
|
-
console.error(message, ...optionalParams);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Determine the Gemini CLI command/path.
|
|
65
|
-
* Similar to findClaudeCli but for Gemini
|
|
66
|
-
*/
|
|
67
|
-
export function findGeminiCli(): string {
|
|
68
|
-
debugLog('[Debug] Attempting to find Gemini CLI...');
|
|
69
|
-
|
|
70
|
-
// Check for custom CLI name from environment variable
|
|
71
|
-
const customCliName = process.env.GEMINI_CLI_NAME;
|
|
72
|
-
if (customCliName) {
|
|
73
|
-
debugLog(`[Debug] Using custom Gemini CLI name from GEMINI_CLI_NAME: ${customCliName}`);
|
|
74
|
-
|
|
75
|
-
// If it's an absolute path, use it directly
|
|
76
|
-
if (path.isAbsolute(customCliName)) {
|
|
77
|
-
debugLog(`[Debug] GEMINI_CLI_NAME is an absolute path: ${customCliName}`);
|
|
78
|
-
return customCliName;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// If it starts with ~ or ./, reject as relative paths are not allowed
|
|
82
|
-
if (customCliName.startsWith('./') || customCliName.startsWith('../') || customCliName.includes('/')) {
|
|
83
|
-
throw new Error(`Invalid GEMINI_CLI_NAME: Relative paths are not allowed. Use either a simple name (e.g., 'gemini') or an absolute path (e.g., '/tmp/gemini-test')`);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const cliName = customCliName || 'gemini';
|
|
88
|
-
|
|
89
|
-
// Try local install path: ~/.gemini/local/gemini
|
|
90
|
-
const userPath = join(homedir(), '.gemini', 'local', 'gemini');
|
|
91
|
-
debugLog(`[Debug] Checking for Gemini CLI at local user path: ${userPath}`);
|
|
92
|
-
|
|
93
|
-
if (existsSync(userPath)) {
|
|
94
|
-
debugLog(`[Debug] Found Gemini CLI at local user path: ${userPath}. Using this path.`);
|
|
95
|
-
return userPath;
|
|
96
|
-
} else {
|
|
97
|
-
debugLog(`[Debug] Gemini CLI not found at local user path: ${userPath}.`);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// Fallback to CLI name (PATH lookup)
|
|
101
|
-
debugLog(`[Debug] Falling back to "${cliName}" command name, relying on spawn/PATH lookup.`);
|
|
102
|
-
console.warn(`[Warning] Gemini CLI not found at ~/.gemini/local/gemini. Falling back to "${cliName}" in PATH. Ensure it is installed and accessible.`);
|
|
103
|
-
return cliName;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Determine the Codex CLI command/path.
|
|
108
|
-
* Similar to findClaudeCli but for Codex
|
|
109
|
-
*/
|
|
110
|
-
export function findCodexCli(): string {
|
|
111
|
-
debugLog('[Debug] Attempting to find Codex CLI...');
|
|
112
|
-
|
|
113
|
-
// Check for custom CLI name from environment variable
|
|
114
|
-
const customCliName = process.env.CODEX_CLI_NAME;
|
|
115
|
-
if (customCliName) {
|
|
116
|
-
debugLog(`[Debug] Using custom Codex CLI name from CODEX_CLI_NAME: ${customCliName}`);
|
|
117
|
-
|
|
118
|
-
// If it's an absolute path, use it directly
|
|
119
|
-
if (path.isAbsolute(customCliName)) {
|
|
120
|
-
debugLog(`[Debug] CODEX_CLI_NAME is an absolute path: ${customCliName}`);
|
|
121
|
-
return customCliName;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// If it starts with ~ or ./, reject as relative paths are not allowed
|
|
125
|
-
if (customCliName.startsWith('./') || customCliName.startsWith('../') || customCliName.includes('/')) {
|
|
126
|
-
throw new Error(`Invalid CODEX_CLI_NAME: Relative paths are not allowed. Use either a simple name (e.g., 'codex') or an absolute path (e.g., '/tmp/codex-test')`);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
const cliName = customCliName || 'codex';
|
|
131
|
-
|
|
132
|
-
// Try local install path: ~/.codex/local/codex
|
|
133
|
-
const userPath = join(homedir(), '.codex', 'local', 'codex');
|
|
134
|
-
debugLog(`[Debug] Checking for Codex CLI at local user path: ${userPath}`);
|
|
135
|
-
|
|
136
|
-
if (existsSync(userPath)) {
|
|
137
|
-
debugLog(`[Debug] Found Codex CLI at local user path: ${userPath}. Using this path.`);
|
|
138
|
-
return userPath;
|
|
139
|
-
} else {
|
|
140
|
-
debugLog(`[Debug] Codex CLI not found at local user path: ${userPath}.`);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// Fallback to CLI name (PATH lookup)
|
|
144
|
-
debugLog(`[Debug] Falling back to "${cliName}" command name, relying on spawn/PATH lookup.`);
|
|
145
|
-
console.warn(`[Warning] Codex CLI not found at ~/.codex/local/codex. Falling back to "${cliName}" in PATH. Ensure it is installed and accessible.`);
|
|
146
|
-
return cliName;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Determine the Claude CLI command/path.
|
|
151
|
-
* 1. Checks for CLAUDE_CLI_NAME environment variable:
|
|
152
|
-
* - If absolute path, uses it directly
|
|
153
|
-
* - If relative path, throws error
|
|
154
|
-
* - If simple name, continues with path resolution
|
|
155
|
-
* 2. Checks for Claude CLI at the local user path: ~/.claude/local/claude.
|
|
156
|
-
* 3. If not found, defaults to the CLI name (or 'claude'), relying on the system's PATH for lookup.
|
|
157
|
-
*/
|
|
158
|
-
export function findClaudeCli(): string {
|
|
159
|
-
debugLog('[Debug] Attempting to find Claude CLI...');
|
|
160
|
-
|
|
161
|
-
// Check for custom CLI name from environment variable
|
|
162
|
-
const customCliName = process.env.CLAUDE_CLI_NAME;
|
|
163
|
-
if (customCliName) {
|
|
164
|
-
debugLog(`[Debug] Using custom Claude CLI name from CLAUDE_CLI_NAME: ${customCliName}`);
|
|
165
|
-
|
|
166
|
-
// If it's an absolute path, use it directly
|
|
167
|
-
if (path.isAbsolute(customCliName)) {
|
|
168
|
-
debugLog(`[Debug] CLAUDE_CLI_NAME is an absolute path: ${customCliName}`);
|
|
169
|
-
return customCliName;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// If it starts with ~ or ./, reject as relative paths are not allowed
|
|
173
|
-
if (customCliName.startsWith('./') || customCliName.startsWith('../') || customCliName.includes('/')) {
|
|
174
|
-
throw new Error(`Invalid CLAUDE_CLI_NAME: Relative paths are not allowed. Use either a simple name (e.g., 'claude') or an absolute path (e.g., '/tmp/claude-test')`);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
const cliName = customCliName || 'claude';
|
|
179
|
-
|
|
180
|
-
// Try local install path: ~/.claude/local/claude (using the original name for local installs)
|
|
181
|
-
const userPath = join(homedir(), '.claude', 'local', 'claude');
|
|
182
|
-
debugLog(`[Debug] Checking for Claude CLI at local user path: ${userPath}`);
|
|
183
|
-
|
|
184
|
-
if (existsSync(userPath)) {
|
|
185
|
-
debugLog(`[Debug] Found Claude CLI at local user path: ${userPath}. Using this path.`);
|
|
186
|
-
return userPath;
|
|
187
|
-
} else {
|
|
188
|
-
debugLog(`[Debug] Claude CLI not found at local user path: ${userPath}.`);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// 3. Fallback to CLI name (PATH lookup)
|
|
192
|
-
debugLog(`[Debug] Falling back to "${cliName}" command name, relying on spawn/PATH lookup.`);
|
|
193
|
-
console.warn(`[Warning] Claude CLI not found at ~/.claude/local/claude. Falling back to "${cliName}" in PATH. Ensure it is installed and accessible.`);
|
|
194
|
-
return cliName;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// Re-export resolveModelAlias for backward compatibility
|
|
198
|
-
export { resolveModelAlias } from './cli-builder.js';
|
|
199
|
-
|
|
200
54
|
// Ensure spawnAsync is defined correctly *before* the class
|
|
201
55
|
export async function spawnAsync(command: string, args: string[], options?: { timeout?: number, cwd?: string }): Promise<{ stdout: string; stderr: string }> {
|
|
202
56
|
return new Promise((resolve, reject) => {
|
|
@@ -308,7 +162,7 @@ export class ClaudeCodeServer {
|
|
|
308
162
|
**IMPORTANT**: This tool now returns immediately with a PID. Use other tools to check status and get results.
|
|
309
163
|
|
|
310
164
|
**Supported models**:
|
|
311
|
-
"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"
|
|
165
|
+
"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.1-pro-preview", "gemini-3-pro-preview", "gemini-3-flash-preview"
|
|
312
166
|
|
|
313
167
|
**Prompt input**: You must provide EITHER prompt (string) OR prompt_file (file path), but not both.
|
|
314
168
|
|
|
@@ -336,7 +190,7 @@ export class ClaudeCodeServer {
|
|
|
336
190
|
},
|
|
337
191
|
model: {
|
|
338
192
|
type: 'string',
|
|
339
|
-
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.',
|
|
193
|
+
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.1-pro-preview", "gemini-3-pro-preview", "gemini-3-flash-preview", etc.',
|
|
340
194
|
},
|
|
341
195
|
reasoning_effort: {
|
|
342
196
|
type: 'string',
|
|
@@ -344,7 +198,7 @@ export class ClaudeCodeServer {
|
|
|
344
198
|
},
|
|
345
199
|
session_id: {
|
|
346
200
|
type: 'string',
|
|
347
|
-
description: '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.',
|
|
201
|
+
description: 'Optional session ID to resume a previous session. Supported for: haiku, sonnet, opus, gemini-2.5-pro, gemini-2.5-flash, gemini-3.1-pro-preview, gemini-3-pro-preview, gemini-3-flash-preview.',
|
|
348
202
|
},
|
|
349
203
|
},
|
|
350
204
|
required: ['workFolder'],
|