aiterm-mcp 0.21.3 → 0.21.4

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/README.ja.md CHANGED
@@ -1,17 +1,18 @@
1
1
  > **Claude Code から Codex CLI の対話 TUI を操作する——スラッシュコマンドや [`$imagegen`](https://learn.chatgpt.com/docs/image-generation#generate-or-edit-an-image) のようなスキルまで、MCP越しに使える。**
2
2
 
3
3
  <p align="center">
4
- <img src=".github/og.svg" alt="aiterm-mcp — AI が握る 1 本の永続 MCP 端末。その中へ他のコーディングエージェント(Claude/Codex/Grok/Composer)を起動する(tmux ベースの stdio MCP サーバ)" width="100%">
4
+ <img src=".github/og.png" alt="Aiterm — 異なる知性が一つの持続する実行現場を共有する森の観測拠点" width="100%">
5
+ <br>
6
+ <sub><em>この画像は、異なる知性がひとつの持続する実行現場を共有し、それぞれの視点から同じ仕事を前へ進める姿を表しています。</em></sub>
5
7
  </p>
6
8
 
7
- # aiterm-mcp
9
+ # Aiterm
8
10
 
9
11
  [![CI](https://github.com/kitepon-rgb/aiterm-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/kitepon-rgb/aiterm-mcp/actions/workflows/ci.yml)
10
12
  [![npm](https://img.shields.io/npm/v/aiterm-mcp.svg)](https://www.npmjs.com/package/aiterm-mcp)
11
13
  [![週間ダウンロード](https://img.shields.io/npm/dw/aiterm-mcp.svg)](https://www.npmjs.com/package/aiterm-mcp)
12
14
  [![node](https://img.shields.io/node/v/aiterm-mcp)](https://nodejs.org)
13
15
  [![license: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
14
- [![install size](https://packagephobia.com/badge?p=aiterm-mcp)](https://packagephobia.com/result?p=aiterm-mcp)
15
16
 
16
17
  > *(English: [README.md](README.md))*
17
18
 
@@ -95,6 +96,10 @@ host統合は、kitepon.devの製品開発を支える内部基盤
95
96
 
96
97
  13 ツール: 6 つの **PTY ツール**(`pty_open` / `pty_send` / `pty_read` / `pty_key` / `pty_close` / `pty_list`)で 1 本の永続端末を開き・操作し・読む。加えて 4 つの **エージェント起動ツール**(`claude_agent` / `codex_agent` / `grok_agent` / `composer_agent`)が別のコーディングエージェントの TUI を新しい端末の中に起動し、`claude_turn`がdurable caller向けの構造化issue/recoveryを、`claude_approval`がmanaged Claudeの相関済み承認UI中継を、`diagnostics`が安全なfactory readinessを返す。バックエンドは **tmux** なので、MCP サーバや AI クライアントが再起動してもセッションは生き残る。
97
98
 
99
+ **v0.21.4ではfresh managed Claudeからuser scope MCPを復元。** 通常hook、plugin、permission、
100
+ project/local MCPの隔離は維持し、`~/.claude.json`で既にuser scope登録された`mcpServers`だけを
101
+ owner-onlyのlaunch設定へsnapshotする。破損したuser MCP設定は、toolなしで黙って起動せずsession作成前に失敗する。
102
+
98
103
  **v0.21.3ではCodexの完了経路からStop hookを撤去。** Codexの完了通知と最終回答の帰属は、
99
104
  root rollout transcriptへ永続化される`task_complete.turn_id`をdispatch byte境界以後から観測する。
100
105
  hookの実行ファイルが壊れたり消えたりしても`aiterm-wait`は座礁しない。v0.21.0では外部agent launcherへ
@@ -162,7 +167,7 @@ $ aiterm-wait --session codex1 --cursor <event_cursor> # exit 0=done / 3=timeo
162
167
  | `grok_agent` | Grok Build(xAI、既定`grok-4.5`、`model?`で上書き) | `prompt?`, `model?`, `reasoning_effort?`は非対応(指定時は明示エラー), `cwd?`, `session_name?`, `write_scope?` |
163
168
  | `composer_agent` | Grok Build(xAI、既定`grok-composer-2.5-fast`、`model?`で上書き) | `prompt?`, `model?`, `reasoning_effort?`は非対応(指定時は明示エラー), `cwd?`, `session_name?`, `write_scope?` |
164
169
 
165
- 各ベンダーの CLI が導入・認証済みであること(`claude_agent` は `claude`、`codex_agent` は `codex`、Grok 系は `grok`)。バイナリは `CLAUDE_BIN` / `CODEX_BIN` / `GROK_BIN`、各既定path、`PATH` の順で解決する。CLI不在・不正なmodel/effort・実在しない`cwd`はsession作成前に失敗し、残骸を残さない。Claudeはさらに、PTY作成前に同じCLIの`auth status --json`が`loggedIn:true`を返すことを要求する。未認証・malformed・失敗exit・timeoutは残骸ゼロで失敗し、正常なvendor所有の共有認証は複数sessionから利用する。managed Claudeへのexact `/login`・`/logout`は通常dispatchとforce送信の双方で副作用前に拒否するため、認証は通常端末で一度だけ修理する。Claudeは通常settingsを継承しないlaunch専用settingsとStop hookを使い、本文なしeventとowner-only bounded resultを分離する。`pty_read({ agent_transcript:true })`はdigestとbyte数を検証したresultだけを返し、Claude private transcriptを読まない。後着resultは同じsessionからprompt再送なしで回収できる。managed Claudeのactive turn中はC-c以外の`pty_key`と素送信を拒否する。Claudeが`Do you want to proceed?`を表示したら、`claude_approval(action:"inspect", ...)`で画面digestを取得し、表示内容を判断してから、そのdigestと`approve_once`または`deny`を`respond`へ渡す。同じoperation・同じ画面が維持されている時だけ入力し、任意文字列や恒久許可選択肢は中継しない。中断は`C-c`、解除は`pty_close`。
170
+ 各ベンダーの CLI が導入・認証済みであること(`claude_agent` は `claude`、`codex_agent` は `codex`、Grok 系は `grok`)。バイナリは `CLAUDE_BIN` / `CODEX_BIN` / `GROK_BIN`、各既定path、`PATH` の順で解決する。CLI不在・不正なmodel/effort・実在しない`cwd`はsession作成前に失敗し、残骸を残さない。Claudeはさらに、PTY作成前に同じCLIの`auth status --json`が`loggedIn:true`を返すことを要求する。未認証・malformed・失敗exit・timeoutは残骸ゼロで失敗し、正常なvendor所有の共有認証は複数sessionから利用する。managed Claudeへのexact `/login`・`/logout`は通常dispatchとforce送信の双方で副作用前に拒否するため、認証は通常端末で一度だけ修理する。Claudeは通常settingsを継承しないlaunch専用settingsとStop hookを使う一方、user scopeの`mcpServers`だけは`~/.claude.json`から別のowner-only launch configへsnapshotし`--mcp-config`で渡す。project/local MCPは継承しない。本文なしeventとowner-only bounded resultを分離し、`pty_read({ agent_transcript:true })`はdigestとbyte数を検証したresultだけを返してClaude private transcriptを読まない。後着resultは同じsessionからprompt再送なしで回収できる。managed Claudeのactive turn中はC-c以外の`pty_key`と素送信を拒否する。Claudeが`Do you want to proceed?`を表示したら、`claude_approval(action:"inspect", ...)`で画面digestを取得し、表示内容を判断してから、そのdigestと`approve_once`または`deny`を`respond`へ渡す。同じoperation・同じ画面が維持されている時だけ入力し、任意文字列や恒久許可選択肢は中継しない。中断は`C-c`、解除は`pty_close`。
166
171
 
167
172
  エージェント間の隠れたプロトコルは無い。起動したClaude/Codex/Grok/Composerは利用者がattachできるもう1本の永続sessionであり、MCPクライアントが通常のPTY操作で駆動する。
168
173
 
package/README.md CHANGED
@@ -1,17 +1,18 @@
1
1
  > **Drive Codex CLI's interactive TUI from Claude Code — including slash commands and skills such as [`$imagegen`](https://learn.chatgpt.com/docs/image-generation#generate-or-edit-an-image) — through MCP.**
2
2
 
3
3
  <p align="center">
4
- <img src=".github/og.svg" alt="aiterm-mcp — one persistent MCP terminal your AI drives, and launches other coding agents (Claude/Codex/Grok/Composer) into (tmux-backed stdio MCP server)" width="100%">
4
+ <img src=".github/og.png" alt="Aiterm — a shared forest observatory where different intelligences work in one persistent execution space" width="100%">
5
+ <br>
6
+ <sub><em>This image represents different intelligences sharing one persistent workspace and advancing the same work from their own perspectives.</em></sub>
5
7
  </p>
6
8
 
7
- # aiterm-mcp
9
+ # Aiterm
8
10
 
9
11
  [![CI](https://github.com/kitepon-rgb/aiterm-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/kitepon-rgb/aiterm-mcp/actions/workflows/ci.yml)
10
12
  [![npm](https://img.shields.io/npm/v/aiterm-mcp.svg)](https://www.npmjs.com/package/aiterm-mcp)
11
13
  [![weekly downloads](https://img.shields.io/npm/dw/aiterm-mcp.svg)](https://www.npmjs.com/package/aiterm-mcp)
12
14
  [![node](https://img.shields.io/node/v/aiterm-mcp)](https://nodejs.org)
13
15
  [![license: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
14
- [![install size](https://packagephobia.com/badge?p=aiterm-mcp)](https://packagephobia.com/result?p=aiterm-mcp)
15
16
 
16
17
  > *(日本語: [README.ja.md](README.ja.md))*
17
18
 
@@ -95,6 +96,12 @@ toolchain behind kitepon.dev's products.
95
96
 
96
97
  Thirteen tools: six **PTY tools** — `pty_open` / `pty_send` / `pty_read` / `pty_key` / `pty_close` / `pty_list` — to open, drive, and read one persistent terminal, four **agent launchers** — `claude_agent` / `codex_agent` / `grok_agent` / `composer_agent` — that each start another coding agent's TUI inside a fresh one, `claude_turn` for durable structured issue/recovery, `claude_approval` for correlated managed-Claude approval prompts, and `diagnostics` for safe factory readiness. The backend is **tmux**, so sessions survive even if the MCP server or the AI client restarts.
97
98
 
99
+ **v0.21.4 restores user-scoped MCPs in fresh managed Claude sessions.** Aiterm keeps
100
+ normal hooks, plugins, permissions, and project/local MCPs isolated, while snapshotting only
101
+ the already user-scoped `mcpServers` from `~/.claude.json` into an owner-only launch config.
102
+ Malformed user MCP config fails before a session is created instead of silently launching
103
+ Claude without its tools.
104
+
98
105
  **v0.21.3 removes Codex Stop hooks from the completion path.** Codex completion and
99
106
  final-message attribution now come from the root rollout transcript's durable
100
107
  `task_complete.turn_id`, observed after the dispatch byte boundary. A broken or stale
@@ -178,7 +185,7 @@ One call per model, so the tool name itself tells you which model you get:
178
185
  | `grok_agent` | Grok Build, model `grok-4.5` by default (`model?` overrides) (xAI) | `prompt?`, `model?`, `reasoning_effort?` unsupported (an explicit value is an error; Grok CLI `--effort` is headless-only), `cwd?`, `session_name?`, `write_scope?` |
179
186
  | `composer_agent` | Grok Build, model `grok-composer-2.5-fast` by default (`model?` overrides) (xAI) | `prompt?`, `model?`, `reasoning_effort?` unsupported (an explicit value is an error), `cwd?`, `session_name?`, `write_scope?` |
180
187
 
181
- The vendor CLI must be installed and authenticated (`claude` for `claude_agent`; `codex` for `codex_agent`; `grok` for both Grok tools). aiterm resolves the binary via `CLAUDE_BIN` / `CODEX_BIN` / `GROK_BIN`, then `~/.local/bin/claude` / `~/.local/bin/codex` / `~/.grok/bin/grok`, then `PATH`. Prerequisites are checked **before** a session exists: empty `model` values and unsupported effort values are rejected up front; a missing CLI binary or a nonexistent `cwd` fails for all four. Before creating a Claude session, aiterm also requires a successful structured `claude auth status --json` result with `loggedIn: true`; unavailable, malformed, or failed authentication leaves **zero leftover session**. Claude sessions share the vendor-owned credential store rather than copying credentials per launch, so multiple sessions can reuse one healthy login. Managed Claude rejects exact `/login` and `/logout` dispatches, including forced sends: repair authentication once in a normal terminal, then relaunch any stale unauthenticated sessions. Claude and Codex launchers forward `model` and `reasoning_effort` through their vendor CLI's public flags; Grok/Composer reject `reasoning_effort` because it is headless-only. Pass an absolute path for `cwd` — `~` is not expanded. Durable callers can make a promptless Claude launch exactly replayable by passing an explicit `session_name` and a `launch_operation_id` formatted as `sha256:<64 lowercase hex>`. Repeating the identical launch returns the same structured session receipt without starting the CLI twice; a different correlation ID or launch argument for that session fails explicitly. Claude uses launch-local managed settings containing only aiterm's Stop hook: normal user/project/local hooks are not inherited, the hook event contains no answer body, and the bounded owner-only result is returned by `pty_read({ agent_transcript:true })` without reading Claude's private transcript. A late result remains recoverable from the same session without re-sending the prompt. While a managed Claude turn is active, raw sends and non-interrupt keys are rejected. If Claude displays `Do you want to proceed?`, call `claude_approval(action:"inspect", ...)`, decide from the visible prompt, then call `respond` with the returned digest and either `approve_once` or `deny`. The response is accepted only while the same operation and screen digest remain current; arbitrary text and persistent-allow choices are never relayed. Use `pty_key("C-c")` to interrupt and `pty_close` to abandon the session. For unconstrained manual key-by-key driving, open a plain `pty_open` session and start the vendor CLI yourself. Codex uses a managed `CODEX_HOME`; Grok/Composer isolate their managed homes and pass validated OAuth state through `GROK_AUTH_PATH`. Before the first unbound dispatch, aiterm waits for the vendor TUI's input prompt and fails before sending if it is not ready. Managed completion requires POSIX filesystem semantics (Linux, WSL2, macOS).
188
+ The vendor CLI must be installed and authenticated (`claude` for `claude_agent`; `codex` for `codex_agent`; `grok` for both Grok tools). aiterm resolves the binary via `CLAUDE_BIN` / `CODEX_BIN` / `GROK_BIN`, then `~/.local/bin/claude` / `~/.local/bin/codex` / `~/.grok/bin/grok`, then `PATH`. Prerequisites are checked **before** a session exists: empty `model` values and unsupported effort values are rejected up front; a missing CLI binary or a nonexistent `cwd` fails for all four. Before creating a Claude session, aiterm also requires a successful structured `claude auth status --json` result with `loggedIn: true`; unavailable, malformed, or failed authentication leaves **zero leftover session**. Claude sessions share the vendor-owned credential store rather than copying credentials per launch, so multiple sessions can reuse one healthy login. Managed Claude rejects exact `/login` and `/logout` dispatches, including forced sends: repair authentication once in a normal terminal, then relaunch any stale unauthenticated sessions. Claude and Codex launchers forward `model` and `reasoning_effort` through their vendor CLI's public flags; Grok/Composer reject `reasoning_effort` because it is headless-only. Pass an absolute path for `cwd` — `~` is not expanded. Durable callers can make a promptless Claude launch exactly replayable by passing an explicit `session_name` and a `launch_operation_id` formatted as `sha256:<64 lowercase hex>`. Repeating the identical launch returns the same structured session receipt without starting the CLI twice; a different correlation ID or launch argument for that session fails explicitly. Claude uses launch-local managed settings containing only aiterm's Stop hook: normal user/project/local hooks are not inherited. User-scoped `mcpServers` are copied separately from `~/.claude.json` into a launch-local owner-only config and passed through `--mcp-config`; project/local MCPs remain isolated. The hook event contains no answer body, and the bounded owner-only result is returned by `pty_read({ agent_transcript:true })` without reading Claude's private transcript. A late result remains recoverable from the same session without re-sending the prompt. While a managed Claude turn is active, raw sends and non-interrupt keys are rejected. If Claude displays `Do you want to proceed?`, call `claude_approval(action:"inspect", ...)`, decide from the visible prompt, then call `respond` with the returned digest and either `approve_once` or `deny`. The response is accepted only while the same operation and screen digest remain current; arbitrary text and persistent-allow choices are never relayed. Use `pty_key("C-c")` to interrupt and `pty_close` to abandon the session. For unconstrained manual key-by-key driving, open a plain `pty_open` session and start the vendor CLI yourself. Codex uses a managed `CODEX_HOME`; Grok/Composer isolate their managed homes and pass validated OAuth state through `GROK_AUTH_PATH`. Before the first unbound dispatch, aiterm waits for the vendor TUI's input prompt and fails before sending if it is not ready. Managed completion requires POSIX filesystem semantics (Linux, WSL2, macOS).
182
189
 
183
190
  The managed Codex home links authentication, privately snapshots `config.toml` and `agents/*.toml` custom-role definitions, and keeps sessions/caches isolated. A symlinked role definition is resolved into a regular-file snapshot rather than shared with the source home. aiterm does not install a Codex Stop hook: the root rollout transcript is the completion source, so a missing hook executable cannot strand `aiterm-wait`.
184
191
 
package/dist/core.js CHANGED
@@ -412,6 +412,12 @@ function agentManagedClaudeSettingsPath(name, launchId) {
412
412
  throw new AitermError(`launch_id が不正です: ${launchId}`, 2);
413
413
  return path.join(agentsDir(), `${name}.${launchId}.claude-settings.json`);
414
414
  }
415
+ function agentManagedClaudeMcpConfigPath(name, launchId) {
416
+ assertSessionName(name);
417
+ if (!LAUNCH_ID_RE.test(launchId))
418
+ throw new AitermError(`launch_id が不正です: ${launchId}`, 2);
419
+ return path.join(agentsDir(), `${name}.${launchId}.claude-mcp.json`);
420
+ }
415
421
  function agentClaudeResultPath(name, launchId) {
416
422
  assertSessionName(name);
417
423
  if (!LAUNCH_ID_RE.test(launchId))
@@ -494,6 +500,7 @@ function cleanupAgentState(name) {
494
500
  f.endsWith(".events.jsonl") ||
495
501
  f.endsWith(".wait.lock") ||
496
502
  f.endsWith(".claude-settings.json") ||
503
+ f.endsWith(".claude-mcp.json") ||
497
504
  f.endsWith(".claude-result.json") ||
498
505
  f.endsWith(".claude-operation.json") ||
499
506
  f.endsWith(".claude-approval.json") ||
@@ -1354,6 +1361,7 @@ export function killAll() {
1354
1361
  f.endsWith(".events.jsonl") ||
1355
1362
  f.endsWith(".wait.lock") ||
1356
1363
  f.endsWith(".claude-settings.json") ||
1364
+ f.endsWith(".claude-mcp.json") ||
1357
1365
  f.endsWith(".claude-result.json") ||
1358
1366
  f.endsWith(".claude-operation.json") ||
1359
1367
  f.endsWith(".claude-dispatch") ||
@@ -1656,6 +1664,52 @@ function createManagedClaudeSettings(name, launchId) {
1656
1664
  });
1657
1665
  return settings;
1658
1666
  }
1667
+ function createManagedClaudeMcpConfig(name, launchId) {
1668
+ const home = process.env.HOME?.trim() || os.homedir();
1669
+ const source = path.join(home, ".claude.json");
1670
+ let canonical;
1671
+ try {
1672
+ canonical = fs.realpathSync(source);
1673
+ }
1674
+ catch (error) {
1675
+ if (error.code === "ENOENT")
1676
+ return null;
1677
+ throw new AitermError(`Claude user MCP設定を読めません: ${error.message}`, 2);
1678
+ }
1679
+ let fd;
1680
+ let parsed;
1681
+ try {
1682
+ fd = fs.openSync(canonical, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW | fs.constants.O_NONBLOCK);
1683
+ const st = fs.fstatSync(fd);
1684
+ if (!st.isFile() || st.uid !== currentUid() || st.size > 16 * 1024 * 1024) {
1685
+ throw new AitermError("Claude user MCP設定の安全検証に失敗しました", 2);
1686
+ }
1687
+ parsed = JSON.parse(fs.readFileSync(fd, "utf8"));
1688
+ }
1689
+ catch (error) {
1690
+ if (error instanceof AitermError)
1691
+ throw error;
1692
+ throw new AitermError(`Claude user MCP設定を読めません: ${error.message}`, 2);
1693
+ }
1694
+ finally {
1695
+ if (fd !== undefined)
1696
+ fs.closeSync(fd);
1697
+ }
1698
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
1699
+ throw new AitermError("Claude user MCP設定のtop-level JSONはobjectである必要があります", 2);
1700
+ }
1701
+ const servers = parsed.mcpServers;
1702
+ if (servers === undefined)
1703
+ return null;
1704
+ if (!servers || typeof servers !== "object" || Array.isArray(servers)) {
1705
+ throw new AitermError("Claude user MCP設定のmcpServersはobjectである必要があります", 2);
1706
+ }
1707
+ if (Object.keys(servers).length === 0)
1708
+ return null;
1709
+ const destination = agentManagedClaudeMcpConfigPath(name, launchId);
1710
+ writeJson0600(destination, { mcpServers: servers });
1711
+ return destination;
1712
+ }
1659
1713
  function realGrokHome() {
1660
1714
  return path.resolve(process.env.GROK_HOME || path.join(process.env.HOME ?? os.homedir(), ".grok"));
1661
1715
  }
@@ -2227,6 +2281,7 @@ function createClaudeAgentMetadata(name, cwd, initialPrompt, launchOperationId,
2227
2281
  createEmpty0600NoFollow(eventFile);
2228
2282
  createEmpty0600NoFollow(resultFile);
2229
2283
  const claudeSettings = createManagedClaudeSettings(name, launchId);
2284
+ const claudeMcpConfig = createManagedClaudeMcpConfig(name, launchId);
2230
2285
  const meta = {
2231
2286
  kind: "claude",
2232
2287
  aiterm_session: name,
@@ -2241,6 +2296,7 @@ function createClaudeAgentMetadata(name, cwd, initialPrompt, launchOperationId,
2241
2296
  hook_route: "managed_claude_settings",
2242
2297
  node_platform: process.platform,
2243
2298
  claude_settings: claudeSettings,
2299
+ claude_mcp_config: claudeMcpConfig,
2244
2300
  result_file: resultFile,
2245
2301
  };
2246
2302
  writeAgentMetadata(meta);
@@ -2325,11 +2381,14 @@ function loadAgentMetadata(name) {
2325
2381
  }
2326
2382
  if (m.kind === "claude") {
2327
2383
  const expectedSettings = agentManagedClaudeSettingsPath(name, m.launch_id);
2384
+ const expectedMcpConfig = agentManagedClaudeMcpConfigPath(name, m.launch_id);
2328
2385
  const expectedResult = agentClaudeResultPath(name, m.launch_id);
2386
+ const claudeMcpConfig = m.claude_mcp_config ?? null;
2329
2387
  const launchOperationId = m.launch_operation_id ?? null;
2330
2388
  const launchRequestDigest = m.launch_request_digest ?? null;
2331
2389
  if (m.hook_route !== "managed_claude_settings" ||
2332
2390
  m.claude_settings !== expectedSettings ||
2391
+ (claudeMcpConfig !== null && claudeMcpConfig !== expectedMcpConfig) ||
2333
2392
  m.result_file !== expectedResult ||
2334
2393
  ((launchOperationId === null) !== (launchRequestDigest === null)) ||
2335
2394
  (launchOperationId !== null && !OPERATION_ID_RE.test(launchOperationId)) ||
@@ -2351,6 +2410,7 @@ function loadAgentMetadata(name) {
2351
2410
  hook_route: "managed_claude_settings",
2352
2411
  node_platform: process.platform,
2353
2412
  claude_settings: expectedSettings,
2413
+ claude_mcp_config: claudeMcpConfig === expectedMcpConfig ? expectedMcpConfig : null,
2354
2414
  result_file: expectedResult,
2355
2415
  };
2356
2416
  }
@@ -3639,6 +3699,8 @@ function buildAgentCmd(kind, bin, model, effort, prompt, meta = null) {
3639
3699
  if (kind === "claude") {
3640
3700
  if (meta?.kind === "claude") {
3641
3701
  parts.push("--setting-sources", shq(""), "--settings", shq(meta.claude_settings ?? ""));
3702
+ if (meta.claude_mcp_config)
3703
+ parts.push("--mcp-config", shq(meta.claude_mcp_config));
3642
3704
  }
3643
3705
  if (model)
3644
3706
  parts.push("--model", shq(model));
@@ -825,13 +825,17 @@ function forceKill(child) {
825
825
  catch { /* already exited */ }
826
826
  return;
827
827
  }
828
+ // taskkill 自体の起動が混雑した Windows runner で遅れても、deadline を越えた
829
+ // worker 本体の副作用を許さない。まず Node のハンドルから即時停止し、その後に
830
+ // taskkill /T で worker が残した子孫だけを回収する。
831
+ try {
832
+ child.kill("SIGKILL");
833
+ }
834
+ catch { /* already exited */ }
828
835
  const killer = spawn("taskkill.exe", ["/pid", String(child.pid), "/T", "/F"], {
829
836
  stdio: "ignore", windowsHide: true,
830
837
  });
831
- killer.once("error", () => { try {
832
- child.kill("SIGKILL");
833
- }
834
- catch { /* already exited */ } });
838
+ killer.once("error", () => { });
835
839
  killer.unref();
836
840
  }
837
841
  function processExists(pid) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aiterm-mcp",
3
- "version": "0.21.3",
3
+ "version": "0.21.4",
4
4
  "mcpName": "io.github.kitepon-rgb/aiterm-mcp",
5
5
  "description": "Persistent tmux terminal MCP that lets Claude Code drive Codex CLI's interactive TUI, including slash commands and $imagegen. Also runs durable PTY sessions for SSH, containers, REPLs, and coding agents.",
6
6
  "keywords": [