@ctliz/agent-intercom-claude 0.13.0-connect.1 → 0.13.0-connect.3

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 CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.13.0-connect.3 - 2026-08-23
4
+
5
+ - Add complete OpenAI MCP annotations for all nine Intercom tools and verify their exact semantics in tests.
6
+ - Reference every MCP tool by name so tool coverage is complete.
7
+ - Explicitly disable shell execution for production and release-verification child processes.
8
+
9
+ ## 0.13.0-connect.2 - 2026-08-18
10
+
11
+ - Document standard stdio MCP setup for Grok Build and AGY, including host-neutral identity variables, scope requirements, source-checkout configuration, manual `intercom_pending` polling, and the absence of wake-on-message support.
12
+
3
13
  ## 0.12.0-connect.3 - 2026-08-14
4
14
 
5
15
  - Fix package bundling by adding `monitors/**/*` (`monitors/monitors.json`) to `package.json` `files`, ensuring Claude Code plugin monitor configurations resolve properly at runtime when installed from the packaged npm artifact.
package/README.md CHANGED
@@ -149,6 +149,44 @@ claude mcp add claude-intercom -- claude-intercom-mcp
149
149
 
150
150
  With `--transport mcp`, `cci` does this automatically for each normal headless worker. Native headless workers are still woken and replied through the worker daemon's broker connection, but omit the packaged MCP server from the Claude turn. `ccim` intentionally uses Claude's `--safe-mode`, which disables MCP servers along with plugins, hooks, and skills.
151
151
 
152
+ ### Other stdio MCP hosts
153
+
154
+ The `claude-intercom-mcp` executable is a standard stdio MCP server, so Grok Build, AGY, and other compatible local hosts can use it without installing the Claude Code plugin. An npm/global installation provides that executable; for a source checkout, configure `node` with the absolute path to `dist/claude-server.mjs`. Node 20+ is required.
155
+
156
+ Use `AGENT_INTERCOM_SESSION_ID` and `AGENT_INTERCOM_SESSION_NAME` for a host-neutral identity; `CLAUDE_INTERCOM_SESSION_ID` and `CLAUDE_INTERCOM_NAME` remain higher-priority compatibility aliases. Do not share a fixed session ID between concurrent host sessions.
157
+
158
+ A plain MCP host is connected only while its MCP process is alive. Incoming Intercom messages are durable and appear through `intercom_pending`, but this server cannot itself inject a new prompt into Grok or AGY. Use `intercom_send` plus `intercom_pending` for manual-polling workflows; a host-specific relay is required for wake-on-message.
159
+
160
+ For Grok Build, register the server and use a unique static ID for an individual long-lived probe or worker:
161
+
162
+ ```bash
163
+ grok mcp add agent-intercom \
164
+ -e AGENT_INTERCOM_SESSION_ID=grok-probe-01 \
165
+ -e AGENT_INTERCOM_SESSION_NAME=grok-probe \
166
+ -e CLAUDE_INTERCOM_MODEL=grok-build \
167
+ -- claude-intercom-mcp
168
+ ```
169
+
170
+ For AGY, add the equivalent entry to `~/.gemini/config/mcp_config.json` (replace the source-checkout paths when using an installed `claude-intercom-mcp` executable):
171
+
172
+ ```json
173
+ {
174
+ "mcpServers": {
175
+ "agent-intercom": {
176
+ "command": "/absolute/path/to/node",
177
+ "args": ["/absolute/path/to/agent-intercom-claude/dist/claude-server.mjs"],
178
+ "env": {
179
+ "AGENT_INTERCOM_SESSION_ID": "agy-probe-01",
180
+ "AGENT_INTERCOM_SESSION_NAME": "agy-probe",
181
+ "CLAUDE_INTERCOM_MODEL": "agy"
182
+ }
183
+ }
184
+ }
185
+ }
186
+ ```
187
+
188
+ Both hosts must use the same `AGENT_INTERCOM_SCOPE_ID` as their intended peers, or leave it unset for the default local scope. Call `intercom_whoami` once after startup, then use `intercom_list`, `intercom_send`, and `intercom_pending` to verify delivery.
189
+
152
190
  Optional identity variables can be attached at registration time:
153
191
 
154
192
  ```bash
@@ -413,6 +451,8 @@ environment when no config file is given (`CLAUDE_INTERCOM_WORKER_ID`, `…_NAME
413
451
  | `CLAUDE_INTERCOM_NAME` | MCP server | Discoverable session name |
414
452
  | `CLAUDE_INTERCOM_SESSION_ID` | MCP server | Stable intercom id |
415
453
  | `CLAUDE_INTERCOM_MODEL` | MCP server | Model label shown to peers |
454
+ | `AGENT_INTERCOM_SESSION_ID` / `_NAME` | MCP server | Host-neutral identity fallback; Claude-specific values take precedence |
455
+ | `AGENT_INTERCOM_SCOPE_ID` | MCP server | Broker-enforced local routing scope |
416
456
  | `CLAUDE_INTERCOM_EFFORT` | `cci` / `ccim` | Effort level forwarded to every Claude turn |
417
457
  | `CLAUDE_INTERCOM_CWD` / `_INSTRUCTIONS` | `cci` / `ccim` | Defaults for `--cwd` / `--instructions` |
418
458
  | `CLAUDE_INTERCOM_CLAUDE_COMMAND` | workers | Claude Code executable (default `claude`) |
package/broker/spawn.ts CHANGED
@@ -191,6 +191,7 @@ export function getBrokerSpawnOptions(
191
191
  cwd: string;
192
192
  env: NodeJS.ProcessEnv;
193
193
  windowsHide: true;
194
+ shell: false;
194
195
  } {
195
196
  return {
196
197
  detached: true,
@@ -198,6 +199,7 @@ export function getBrokerSpawnOptions(
198
199
  cwd: extensionDir,
199
200
  env: { ...env, PI_CODING_AGENT_DIR: getAgentDirPath(env), NODE_NO_WARNINGS: "1" },
200
201
  windowsHide: true,
202
+ shell: false,
201
203
  };
202
204
  }
203
205
 
package/claude/cci.ts CHANGED
@@ -66,6 +66,7 @@ function gitString(cwd: string, args: string[]): string | null {
66
66
  cwd,
67
67
  encoding: "utf8",
68
68
  stdio: ["ignore", "pipe", "ignore"],
69
+ shell: false,
69
70
  });
70
71
  if (result.status !== 0) return null;
71
72
  const trimmed = result.stdout.trim();
@@ -341,6 +342,7 @@ async function runCciTui(options: CciOptions, id: string, name: string, scopeId:
341
342
  const child = spawn(options.claudeCommand, args, {
342
343
  cwd: options.cwd,
343
344
  stdio: "inherit",
345
+ shell: false,
344
346
  env: {
345
347
  ...process.env,
346
348
  ...scopeEnv,
@@ -116,6 +116,7 @@ export function runClaudeTurn(options: ClaudeTurnOptions): Promise<ClaudeTurnRes
116
116
  cwd: launch.cwd,
117
117
  env: { ...process.env, ...options.env },
118
118
  stdio: ["pipe", "pipe", "pipe"],
119
+ shell: false,
119
120
  });
120
121
  } finally {
121
122
  // Directory descriptors remain open across the synchronous spawn setup,
package/claude/contact.ts CHANGED
@@ -20,14 +20,14 @@ export function copyTextToClipboard(text: string): boolean {
20
20
  ["clip.exe", []],
21
21
  ];
22
22
  for (const [command, args] of candidates) {
23
- if (spawnSync("which", [command], { stdio: "ignore" }).status !== 0) continue;
23
+ if (spawnSync("which", [command], { stdio: "ignore", shell: false }).status !== 0) continue;
24
24
  if (command === "wl-copy") {
25
- const child = spawn(command, args, { stdio: ["pipe", "ignore", "ignore"] });
25
+ const child = spawn(command, args, { stdio: ["pipe", "ignore", "ignore"], shell: false });
26
26
  child.stdin.end(text);
27
27
  child.unref();
28
28
  return true;
29
29
  }
30
- if (spawnSync(command, args, { input: text, stdio: ["pipe", "ignore", "ignore"] }).status === 0) return true;
30
+ if (spawnSync(command, args, { input: text, stdio: ["pipe", "ignore", "ignore"], shell: false }).status === 0) return true;
31
31
  }
32
32
  return false;
33
33
  }
@@ -17,6 +17,12 @@ interface ToolDefinition {
17
17
  name: string;
18
18
  description: string;
19
19
  inputSchema: Record<string, unknown>;
20
+ annotations: {
21
+ readOnlyHint: boolean;
22
+ destructiveHint: boolean;
23
+ idempotentHint: boolean;
24
+ openWorldHint: boolean;
25
+ };
20
26
  handler: ToolHandler;
21
27
  }
22
28
 
@@ -78,18 +84,21 @@ export function buildToolDefinitions(runtime: ClaudeIntercomRuntime): ToolDefini
78
84
  name: "intercom_whoami",
79
85
  description: "Return this Claude session's intercom identity for reliable targeting.",
80
86
  inputSchema: { type: "object", properties: {}, additionalProperties: false },
87
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
81
88
  handler: async () => runtime.whoami(),
82
89
  },
83
90
  {
84
91
  name: "intercom_team",
85
92
  description: "Show your current manager and the live coworkers owned by that manager. No arguments are required.",
86
93
  inputSchema: { type: "object", properties: {}, additionalProperties: false },
94
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
87
95
  handler: async () => runtime.team(),
88
96
  },
89
97
  {
90
98
  name: "intercom_status",
91
99
  description: "Show intercom connection status, active sessions, unread messages, and pending asks.",
92
100
  inputSchema: { type: "object", properties: {}, additionalProperties: false },
101
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
93
102
  handler: async () => runtime.status(),
94
103
  },
95
104
  {
@@ -103,6 +112,7 @@ export function buildToolDefinitions(runtime: ClaudeIntercomRuntime): ToolDefini
103
112
  },
104
113
  additionalProperties: false,
105
114
  },
115
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
106
116
  handler: async (args) => runtime.list(
107
117
  args.scope === "directory" || args.scope === "repo" ? args.scope : "machine",
108
118
  asBoolean(args.include_self, false),
@@ -117,6 +127,7 @@ export function buildToolDefinitions(runtime: ClaudeIntercomRuntime): ToolDefini
117
127
  required: ["summary"],
118
128
  additionalProperties: false,
119
129
  },
130
+ annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false },
120
131
  handler: async (args) => runtime.setSummary(asString(args.summary, "summary")),
121
132
  },
122
133
  {
@@ -132,6 +143,7 @@ export function buildToolDefinitions(runtime: ClaudeIntercomRuntime): ToolDefini
132
143
  required: ["to", "message"],
133
144
  additionalProperties: false,
134
145
  },
146
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
135
147
  handler: async (args) => runtime.send(asString(args.to, "to"), asString(args.message, "message"), asAttachmentArray(args.attachments)),
136
148
  },
137
149
  {
@@ -148,6 +160,7 @@ export function buildToolDefinitions(runtime: ClaudeIntercomRuntime): ToolDefini
148
160
  required: ["to", "message"],
149
161
  additionalProperties: false,
150
162
  },
163
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
151
164
  handler: async (args, signal) => runtime.ask(
152
165
  asString(args.to, "to"),
153
166
  asString(args.message, "message"),
@@ -164,6 +177,7 @@ export function buildToolDefinitions(runtime: ClaudeIntercomRuntime): ToolDefini
164
177
  properties: { mark_read: { type: "boolean", default: false } },
165
178
  additionalProperties: false,
166
179
  },
180
+ annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: true },
167
181
  handler: async (args) => runtime.pending(asBoolean(args.mark_read, false)),
168
182
  },
169
183
  {
@@ -179,6 +193,7 @@ export function buildToolDefinitions(runtime: ClaudeIntercomRuntime): ToolDefini
179
193
  required: ["message"],
180
194
  additionalProperties: false,
181
195
  },
196
+ annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },
182
197
  handler: async (args) => runtime.reply(asString(args.message, "message"), typeof args.to === "string" ? args.to : undefined, args.which === "oldest" || args.which === "latest" ? args.which : undefined),
183
198
  },
184
199
  ];
@@ -222,7 +237,7 @@ export async function handleMcpRequest(request: JsonRpcRequest, runtime: ClaudeI
222
237
  return ok(request.id, {});
223
238
  case "tools/list":
224
239
  return ok(request.id, {
225
- tools: tools.map(({ name, description, inputSchema }) => ({ name, description, inputSchema })),
240
+ tools: tools.map(({ name, description, inputSchema, annotations }) => ({ name, description, inputSchema, annotations })),
226
241
  });
227
242
  case "tools/call": {
228
243
  const name = request.params?.name;
@@ -271,7 +271,7 @@ export async function bindNativeClaudeSocket(socketPath: string, onFrame: (frame
271
271
 
272
272
  function processStart(pid: number): Promise<string | undefined> {
273
273
  return new Promise((resolve) => {
274
- execFile("ps", ["-o", "lstart=", "-p", String(pid)], (error, stdout) => resolve(error ? undefined : stdout.trim() || undefined));
274
+ execFile("ps", ["-o", "lstart=", "-p", String(pid)], { shell: false }, (error, stdout) => resolve(error ? undefined : stdout.trim() || undefined));
275
275
  });
276
276
  }
277
277
 
package/claude/runtime.ts CHANGED
@@ -183,6 +183,7 @@ export function detectGitRoot(cwd: string): string | null {
183
183
  cwd,
184
184
  encoding: "utf8",
185
185
  stdio: ["ignore", "pipe", "ignore"],
186
+ shell: false,
186
187
  });
187
188
  if (result.status !== 0) return null;
188
189
  return result.stdout.trim() || null;
@@ -49,6 +49,7 @@ export function probeClaudeVersion(command = "claude"): ClaudeVersionProbe {
49
49
  const result = spawnSync(command, ["--version"], {
50
50
  encoding: "utf8",
51
51
  stdio: ["ignore", "pipe", "pipe"],
52
+ shell: false,
52
53
  });
53
54
  if (result.error || result.status !== 0) {
54
55
  return {
package/dist/cci.mjs CHANGED
@@ -274,7 +274,8 @@ function runClaudeTurn(options) {
274
274
  child = spawn(command, launch.args, {
275
275
  cwd: launch.cwd,
276
276
  env: { ...process.env, ...options.env },
277
- stdio: ["pipe", "pipe", "pipe"]
277
+ stdio: ["pipe", "pipe", "pipe"],
278
+ shell: false
278
279
  });
279
280
  } finally {
280
281
  launch.release();
@@ -566,7 +567,8 @@ function isNativeClaudeVersionCompatible(version) {
566
567
  function probeClaudeVersion(command = "claude") {
567
568
  const result = spawnSync(command, ["--version"], {
568
569
  encoding: "utf8",
569
- stdio: ["ignore", "pipe", "pipe"]
570
+ stdio: ["ignore", "pipe", "pipe"],
571
+ shell: false
570
572
  });
571
573
  if (result.error || result.status !== 0) {
572
574
  return {
@@ -2063,7 +2065,8 @@ function getBrokerSpawnOptions(extensionDir = EXTENSION_DIR, env = process.env)
2063
2065
  stdio: "ignore",
2064
2066
  cwd: extensionDir,
2065
2067
  env: { ...env, PI_CODING_AGENT_DIR: getAgentDirPath(env), NODE_NO_WARNINGS: "1" },
2066
- windowsHide: true
2068
+ windowsHide: true,
2069
+ shell: false
2067
2070
  };
2068
2071
  }
2069
2072
  function toError2(error) {
@@ -2444,14 +2447,14 @@ function copyTextToClipboard(text) {
2444
2447
  ["clip.exe", []]
2445
2448
  ];
2446
2449
  for (const [command, args] of candidates) {
2447
- if (spawnSync2("which", [command], { stdio: "ignore" }).status !== 0) continue;
2450
+ if (spawnSync2("which", [command], { stdio: "ignore", shell: false }).status !== 0) continue;
2448
2451
  if (command === "wl-copy") {
2449
- const child = spawn3(command, args, { stdio: ["pipe", "ignore", "ignore"] });
2452
+ const child = spawn3(command, args, { stdio: ["pipe", "ignore", "ignore"], shell: false });
2450
2453
  child.stdin.end(text);
2451
2454
  child.unref();
2452
2455
  return true;
2453
2456
  }
2454
- if (spawnSync2(command, args, { input: text, stdio: ["pipe", "ignore", "ignore"] }).status === 0) return true;
2457
+ if (spawnSync2(command, args, { input: text, stdio: ["pipe", "ignore", "ignore"], shell: false }).status === 0) return true;
2455
2458
  }
2456
2459
  return false;
2457
2460
  }
@@ -3018,7 +3021,7 @@ async function bindNativeClaudeSocket(socketPath, onFrame) {
3018
3021
  }
3019
3022
  function processStart(pid) {
3020
3023
  return new Promise((resolve5) => {
3021
- execFile("ps", ["-o", "lstart=", "-p", String(pid)], (error, stdout) => resolve5(error ? void 0 : stdout.trim() || void 0));
3024
+ execFile("ps", ["-o", "lstart=", "-p", String(pid)], { shell: false }, (error, stdout) => resolve5(error ? void 0 : stdout.trim() || void 0));
3022
3025
  });
3023
3026
  }
3024
3027
  async function registerNativeClaudePeer(options) {
@@ -3202,7 +3205,8 @@ function gitString(cwd, args) {
3202
3205
  const result = spawnSync3("git", args, {
3203
3206
  cwd,
3204
3207
  encoding: "utf8",
3205
- stdio: ["ignore", "pipe", "ignore"]
3208
+ stdio: ["ignore", "pipe", "ignore"],
3209
+ shell: false
3206
3210
  });
3207
3211
  if (result.status !== 0) return null;
3208
3212
  const trimmed = result.stdout.trim();
@@ -3446,6 +3450,7 @@ async function runCciTui(options, id, name, scopeId) {
3446
3450
  const child = spawn4(options.claudeCommand, args, {
3447
3451
  cwd: options.cwd,
3448
3452
  stdio: "inherit",
3453
+ shell: false,
3449
3454
  env: {
3450
3455
  ...process.env,
3451
3456
  ...scopeEnv,
package/dist/ccim.mjs CHANGED
@@ -278,7 +278,8 @@ function runClaudeTurn(options) {
278
278
  child = spawn(command, launch.args, {
279
279
  cwd: launch.cwd,
280
280
  env: { ...process.env, ...options.env },
281
- stdio: ["pipe", "pipe", "pipe"]
281
+ stdio: ["pipe", "pipe", "pipe"],
282
+ shell: false
282
283
  });
283
284
  } finally {
284
285
  launch.release();
@@ -570,7 +571,8 @@ function isNativeClaudeVersionCompatible(version) {
570
571
  function probeClaudeVersion(command = "claude") {
571
572
  const result = spawnSync(command, ["--version"], {
572
573
  encoding: "utf8",
573
- stdio: ["ignore", "pipe", "pipe"]
574
+ stdio: ["ignore", "pipe", "pipe"],
575
+ shell: false
574
576
  });
575
577
  if (result.error || result.status !== 0) {
576
578
  return {
@@ -2067,7 +2069,8 @@ function getBrokerSpawnOptions(extensionDir = EXTENSION_DIR, env = process.env)
2067
2069
  stdio: "ignore",
2068
2070
  cwd: extensionDir,
2069
2071
  env: { ...env, PI_CODING_AGENT_DIR: getAgentDirPath(env), NODE_NO_WARNINGS: "1" },
2070
- windowsHide: true
2072
+ windowsHide: true,
2073
+ shell: false
2071
2074
  };
2072
2075
  }
2073
2076
  function toError2(error) {
@@ -2448,14 +2451,14 @@ function copyTextToClipboard(text) {
2448
2451
  ["clip.exe", []]
2449
2452
  ];
2450
2453
  for (const [command, args] of candidates) {
2451
- if (spawnSync2("which", [command], { stdio: "ignore" }).status !== 0) continue;
2454
+ if (spawnSync2("which", [command], { stdio: "ignore", shell: false }).status !== 0) continue;
2452
2455
  if (command === "wl-copy") {
2453
- const child = spawn3(command, args, { stdio: ["pipe", "ignore", "ignore"] });
2456
+ const child = spawn3(command, args, { stdio: ["pipe", "ignore", "ignore"], shell: false });
2454
2457
  child.stdin.end(text);
2455
2458
  child.unref();
2456
2459
  return true;
2457
2460
  }
2458
- if (spawnSync2(command, args, { input: text, stdio: ["pipe", "ignore", "ignore"] }).status === 0) return true;
2461
+ if (spawnSync2(command, args, { input: text, stdio: ["pipe", "ignore", "ignore"], shell: false }).status === 0) return true;
2459
2462
  }
2460
2463
  return false;
2461
2464
  }
@@ -3022,7 +3025,7 @@ async function bindNativeClaudeSocket(socketPath, onFrame) {
3022
3025
  }
3023
3026
  function processStart(pid) {
3024
3027
  return new Promise((resolve5) => {
3025
- execFile("ps", ["-o", "lstart=", "-p", String(pid)], (error, stdout) => resolve5(error ? void 0 : stdout.trim() || void 0));
3028
+ execFile("ps", ["-o", "lstart=", "-p", String(pid)], { shell: false }, (error, stdout) => resolve5(error ? void 0 : stdout.trim() || void 0));
3026
3029
  });
3027
3030
  }
3028
3031
  async function registerNativeClaudePeer(options) {
@@ -3206,7 +3209,8 @@ function gitString(cwd, args) {
3206
3209
  const result = spawnSync3("git", args, {
3207
3210
  cwd,
3208
3211
  encoding: "utf8",
3209
- stdio: ["ignore", "pipe", "ignore"]
3212
+ stdio: ["ignore", "pipe", "ignore"],
3213
+ shell: false
3210
3214
  });
3211
3215
  if (result.status !== 0) return null;
3212
3216
  const trimmed = result.stdout.trim();
@@ -3450,6 +3454,7 @@ async function runCciTui(options, id, name, scopeId) {
3450
3454
  const child = spawn4(options.claudeCommand, args, {
3451
3455
  cwd: options.cwd,
3452
3456
  stdio: "inherit",
3457
+ shell: false,
3453
3458
  env: {
3454
3459
  ...process.env,
3455
3460
  ...scopeEnv,
@@ -1501,7 +1501,8 @@ function getBrokerSpawnOptions(extensionDir = EXTENSION_DIR, env = process.env)
1501
1501
  stdio: "ignore",
1502
1502
  cwd: extensionDir,
1503
1503
  env: { ...env, PI_CODING_AGENT_DIR: getAgentDirPath(env), NODE_NO_WARNINGS: "1" },
1504
- windowsHide: true
1504
+ windowsHide: true,
1505
+ shell: false
1505
1506
  };
1506
1507
  }
1507
1508
  function toError2(error2) {
@@ -2274,7 +2275,8 @@ function detectGitRoot(cwd) {
2274
2275
  const result = spawnSync("git", ["rev-parse", "--show-toplevel"], {
2275
2276
  cwd,
2276
2277
  encoding: "utf8",
2277
- stdio: ["ignore", "pipe", "ignore"]
2278
+ stdio: ["ignore", "pipe", "ignore"],
2279
+ shell: false
2278
2280
  });
2279
2281
  if (result.status !== 0) return null;
2280
2282
  return result.stdout.trim() || null;
@@ -2629,18 +2631,21 @@ function buildToolDefinitions(runtime2) {
2629
2631
  name: "intercom_whoami",
2630
2632
  description: "Return this Claude session's intercom identity for reliable targeting.",
2631
2633
  inputSchema: { type: "object", properties: {}, additionalProperties: false },
2634
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
2632
2635
  handler: async () => runtime2.whoami()
2633
2636
  },
2634
2637
  {
2635
2638
  name: "intercom_team",
2636
2639
  description: "Show your current manager and the live coworkers owned by that manager. No arguments are required.",
2637
2640
  inputSchema: { type: "object", properties: {}, additionalProperties: false },
2641
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
2638
2642
  handler: async () => runtime2.team()
2639
2643
  },
2640
2644
  {
2641
2645
  name: "intercom_status",
2642
2646
  description: "Show intercom connection status, active sessions, unread messages, and pending asks.",
2643
2647
  inputSchema: { type: "object", properties: {}, additionalProperties: false },
2648
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
2644
2649
  handler: async () => runtime2.status()
2645
2650
  },
2646
2651
  {
@@ -2654,6 +2659,7 @@ function buildToolDefinitions(runtime2) {
2654
2659
  },
2655
2660
  additionalProperties: false
2656
2661
  },
2662
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
2657
2663
  handler: async (args) => runtime2.list(
2658
2664
  args.scope === "directory" || args.scope === "repo" ? args.scope : "machine",
2659
2665
  asBoolean(args.include_self, false)
@@ -2668,6 +2674,7 @@ function buildToolDefinitions(runtime2) {
2668
2674
  required: ["summary"],
2669
2675
  additionalProperties: false
2670
2676
  },
2677
+ annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false },
2671
2678
  handler: async (args) => runtime2.setSummary(asString(args.summary, "summary"))
2672
2679
  },
2673
2680
  {
@@ -2683,6 +2690,7 @@ function buildToolDefinitions(runtime2) {
2683
2690
  required: ["to", "message"],
2684
2691
  additionalProperties: false
2685
2692
  },
2693
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
2686
2694
  handler: async (args) => runtime2.send(asString(args.to, "to"), asString(args.message, "message"), asAttachmentArray(args.attachments))
2687
2695
  },
2688
2696
  {
@@ -2699,6 +2707,7 @@ function buildToolDefinitions(runtime2) {
2699
2707
  required: ["to", "message"],
2700
2708
  additionalProperties: false
2701
2709
  },
2710
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
2702
2711
  handler: async (args, signal) => runtime2.ask(
2703
2712
  asString(args.to, "to"),
2704
2713
  asString(args.message, "message"),
@@ -2715,6 +2724,7 @@ function buildToolDefinitions(runtime2) {
2715
2724
  properties: { mark_read: { type: "boolean", default: false } },
2716
2725
  additionalProperties: false
2717
2726
  },
2727
+ annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: true },
2718
2728
  handler: async (args) => runtime2.pending(asBoolean(args.mark_read, false))
2719
2729
  },
2720
2730
  {
@@ -2730,6 +2740,7 @@ function buildToolDefinitions(runtime2) {
2730
2740
  required: ["message"],
2731
2741
  additionalProperties: false
2732
2742
  },
2743
+ annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },
2733
2744
  handler: async (args) => runtime2.reply(asString(args.message, "message"), typeof args.to === "string" ? args.to : void 0, args.which === "oldest" || args.which === "latest" ? args.which : void 0)
2734
2745
  }
2735
2746
  ];
@@ -2767,7 +2778,7 @@ async function handleMcpRequest(request, runtime2) {
2767
2778
  return ok(request.id, {});
2768
2779
  case "tools/list":
2769
2780
  return ok(request.id, {
2770
- tools: tools.map(({ name, description, inputSchema }) => ({ name, description, inputSchema }))
2781
+ tools: tools.map(({ name, description, inputSchema, annotations }) => ({ name, description, inputSchema, annotations }))
2771
2782
  });
2772
2783
  case "tools/call": {
2773
2784
  const name = request.params?.name;
@@ -265,7 +265,8 @@ function runClaudeTurn(options) {
265
265
  child = spawn(command, launch.args, {
266
266
  cwd: launch.cwd,
267
267
  env: { ...process.env, ...options.env },
268
- stdio: ["pipe", "pipe", "pipe"]
268
+ stdio: ["pipe", "pipe", "pipe"],
269
+ shell: false
269
270
  });
270
271
  } finally {
271
272
  launch.release();
@@ -557,7 +558,8 @@ function isNativeClaudeVersionCompatible(version) {
557
558
  function probeClaudeVersion(command = "claude") {
558
559
  const result = spawnSync(command, ["--version"], {
559
560
  encoding: "utf8",
560
- stdio: ["ignore", "pipe", "pipe"]
561
+ stdio: ["ignore", "pipe", "pipe"],
562
+ shell: false
561
563
  });
562
564
  if (result.error || result.status !== 0) {
563
565
  return {
@@ -2054,7 +2056,8 @@ function getBrokerSpawnOptions(extensionDir = EXTENSION_DIR, env = process.env)
2054
2056
  stdio: "ignore",
2055
2057
  cwd: extensionDir,
2056
2058
  env: { ...env, PI_CODING_AGENT_DIR: getAgentDirPath(env), NODE_NO_WARNINGS: "1" },
2057
- windowsHide: true
2059
+ windowsHide: true,
2060
+ shell: false
2058
2061
  };
2059
2062
  }
2060
2063
  function toError2(error) {
@@ -2426,14 +2429,14 @@ function copyTextToClipboard(text) {
2426
2429
  ["clip.exe", []]
2427
2430
  ];
2428
2431
  for (const [command, args] of candidates) {
2429
- if (spawnSync2("which", [command], { stdio: "ignore" }).status !== 0) continue;
2432
+ if (spawnSync2("which", [command], { stdio: "ignore", shell: false }).status !== 0) continue;
2430
2433
  if (command === "wl-copy") {
2431
- const child = spawn3(command, args, { stdio: ["pipe", "ignore", "ignore"] });
2434
+ const child = spawn3(command, args, { stdio: ["pipe", "ignore", "ignore"], shell: false });
2432
2435
  child.stdin.end(text);
2433
2436
  child.unref();
2434
2437
  return true;
2435
2438
  }
2436
- if (spawnSync2(command, args, { input: text, stdio: ["pipe", "ignore", "ignore"] }).status === 0) return true;
2439
+ if (spawnSync2(command, args, { input: text, stdio: ["pipe", "ignore", "ignore"], shell: false }).status === 0) return true;
2437
2440
  }
2438
2441
  return false;
2439
2442
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ctliz/agent-intercom-claude",
3
- "version": "0.13.0-connect.1",
3
+ "version": "0.13.0-connect.3",
4
4
  "description": "Claude Code MCP plugin and wakeable worker for direct local messaging with Pi, Codex, and Claude coding-agent sessions.",
5
5
  "license": "AGPL-3.0-or-later",
6
6
  "type": "module",
@@ -54,7 +54,7 @@ export function verifyPublishedProvenance(runner = spawnSync) {
54
54
  "version",
55
55
  "dist.integrity",
56
56
  "--json",
57
- ], { encoding: "utf8" });
57
+ ], { encoding: "utf8", shell: false });
58
58
  if (result.error) throw result.error;
59
59
  if (result.status !== 0) {
60
60
  throw new Error(`Unable to inspect published Core ${APPROVED_CORE.version}: ${(result.stderr || result.stdout).trim()}`);
@@ -51,6 +51,7 @@ function createPackageArchive(temp) {
51
51
  cwd: repositoryRoot,
52
52
  env: { ...process.env, npm_config_cache: join(temp, "npm-cache") },
53
53
  encoding: "utf8",
54
+ shell: false,
54
55
  });
55
56
  if (result.error) throw result.error;
56
57
  if (result.status !== 0) throw new Error(`npm pack failed: ${result.stderr || result.stdout}`);