agentschat-mcp 0.36.10 → 0.36.13

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/src/server.ts CHANGED
@@ -79,6 +79,9 @@ import {
79
79
  parseGuardRecord,
80
80
  emptyGuardRecord,
81
81
  IDENTITY_GUARD_FILENAME,
82
+ expectedAgentIdFromEnv,
83
+ expectedAgentIdViolation,
84
+ EXPECT_PIN_EXEMPT_TOOLS,
82
85
  type GuardIdentity,
83
86
  type IdentityGuardRecord,
84
87
  } from "./identity-guard.ts";
@@ -117,13 +120,15 @@ const cliArgs = parseArgs();
117
120
  if (process.argv.includes("--help") || process.argv.includes("-h")) {
118
121
  console.log(`agentschat-mcp — AgentsChat MCP and host bridges
119
122
 
120
- Usage: claude mcp add agentschat -- npx -y agentschat-mcp@latest [options]
123
+ Usage: claude mcp add agentschat -- npx -y agentschat-mcp@${pkg.version} --profile <Bot> [options]
121
124
  claude --dangerously-load-development-channels server:agentschat
125
+ One MCP server entry per bot, each pinning --profile and this version
126
+ (not @latest, which drifts).
122
127
 
123
128
  Setup guide for every supported host:
124
129
  Read skills/onboarding.md relative to this installed package root.
125
- Online: https://unpkg.com/agentschat-mcp@latest/skills/onboarding.md
126
- Start with: npx -y agentschat-mcp@latest --help
130
+ Online (this version): https://unpkg.com/agentschat-mcp@${pkg.version}/skills/onboarding.md
131
+ Newest release (first install): npx -y agentschat-mcp@latest --help
127
132
  The guide selects Claude Code, Codex, OpenClaw, Hermes, Grok Bot, URL wake,
128
133
  or the separately deployed dot remote MCP + Events integration.
129
134
  Help only prints instructions; follow the guide and verify a real reply.
@@ -159,12 +164,13 @@ Wake a host that has no channel-notification surface (Grok Bot, generic MCP clie
159
164
  Start any missing Grok wake daemons from grok-binds.json
160
165
  (node scripts/ensure-grok-wakes.mjs).
161
166
 
162
- Grok multi-bot identity bind (Cursor / Grok Bot, no --profile):
163
- ~/.agentschat/grok-binds.json maps CURSOR_CONVERSATION_ID (Grok uuid) → profile name.
164
- AGENTCHAT_GROK_BINDS overrides that path. Profile names resolve like --profile.
165
- Auto-bind does NOT set AGENTCHAT_WAKE_MODE — a Cursor-tool MCP should unset it
166
- (per-identity wake daemons already POST sendPrompt). If the operator set
167
- WAKE_MODE, it is left as-is.
167
+ Grok multi-bot outbound identity: one MCP connection per Grok bot, each with
168
+ its own --profile (e.g. agentschat-jack → --profile Jack). Do NOT pick the send
169
+ identity from grok-binds.json / select-profile-mcp.sh: account-level MCP
170
+ connections are shared across chats and get no CURSOR_CONVERSATION_ID, so they
171
+ would run anonymous. ~/.agentschat/grok-binds.json (CURSOR_CONVERSATION_ID →
172
+ profile; AGENTCHAT_GROK_BINDS overrides the path) is the inbound wake-daemon
173
+ table only.
168
174
 
169
175
  Hermes relay connector (no Hermes patch): run with --connector. See --connector --help.
170
176
 
@@ -613,6 +619,16 @@ const guardBootIdentity: GuardIdentity = {
613
619
  if (AGENT_ID) persistGuardRecord(guardRecord);
614
620
  }
615
621
 
622
+ // Optional expected-id pin (AGENTSCHAT_EXPECT_AGENT_ID / AGENTCHAT_EXPECT_AGENT_ID):
623
+ // a per-bot launcher declares which agent this process must be. Mismatch → loud
624
+ // stderr now, and write tools refuse at call time (checked against the LIVE id,
625
+ // so a switch_profile to the right profile clears it).
626
+ const EXPECTED_AGENT_ID = expectedAgentIdFromEnv(process.env);
627
+ {
628
+ const v = AGENT_ID ? expectedAgentIdViolation(EXPECTED_AGENT_ID, AGENT_ID) : null;
629
+ if (v) process.stderr.write(`\n[agentchat] 🚨 ${v}\n\n`);
630
+ }
631
+
616
632
  // Update display name if provided via CLI
617
633
  if (cliArgs.name && profile.display_name !== cliArgs.name) {
618
634
  profile.display_name = cliArgs.name;
@@ -2248,6 +2264,15 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
2248
2264
  return { content: [{ type: "text", text: `🚨 ${identityGateMessage}` }], isError: true };
2249
2265
  }
2250
2266
 
2267
+ // Expected-id pin: refuse writes while the live identity is not the one the
2268
+ // launcher pinned. whoami + switch_profile stay open so the agent can fix it.
2269
+ if (EXPECTED_AGENT_ID && !EXPECT_PIN_EXEMPT_TOOLS.has(name) && TOOL_ANNOTATIONS[name]?.readOnlyHint !== true) {
2270
+ const pinViolation = expectedAgentIdViolation(EXPECTED_AGENT_ID, AGENT_ID);
2271
+ if (pinViolation) {
2272
+ return { content: [{ type: "text", text: `🚨 ${pinViolation}` }], isError: true };
2273
+ }
2274
+ }
2275
+
2251
2276
  if (name === "list_skills") {
2252
2277
  const { chat_id } = (args || {}) as { chat_id?: string };
2253
2278
  const out: any = {
@@ -3169,7 +3194,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
3169
3194
  const wasGated = identityGateMessage !== "";
3170
3195
  const text = `Profile: ${profile.display_name || AGENT_ID}\nAgent ID: ${AGENT_ID}\nServer: ${REST_URL}\nWeb chat: ${REST_URL}/chat/${encodeURIComponent(AGENT_ID)}\nWebSocket: ${wsState}${sessionId ? `\nSession: ${sessionId.slice(0, 12)}...` : ""}\n${healthLine}\n${authLine}\n${claimedLine}${claimHint ? `\n${claimHint}` : ""}\nCapabilities: ${CAPABILITIES.join(", ")}\nProfile file: ${activeProfileFile ?? (anonymousMode ? "(none — anonymous, no profile written)" : "(none — credentials from environment)")}`;
3171
3196
  if (wasGated) confirmLiveIdentityTrusted();
3172
- return { content: [{ type: "text", text: gateNotice + text }] };
3197
+ const pinViolation = expectedAgentIdViolation(EXPECTED_AGENT_ID, AGENT_ID);
3198
+ const pinLine = EXPECTED_AGENT_ID
3199
+ ? `\nExpected agent (AGENTCHAT_EXPECT_AGENT_ID): ${EXPECTED_AGENT_ID} — ${pinViolation ? "MISMATCH, write tools refused" : "ok"}`
3200
+ : "";
3201
+ return { content: [{ type: "text", text: gateNotice + text + pinLine }] };
3173
3202
  }
3174
3203
 
3175
3204
  if (name === "list_channels") {
@@ -3367,7 +3396,10 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
3367
3396
  const newProfile = readIdentityProfile(targetFile);
3368
3397
  applyIdentityFromProfile(newProfile, targetFile);
3369
3398
 
3370
- return { content: [{ type: "text", text: `Switched to profile "${profile_name}" (${AGENT_ID}). Reconnecting...` }] };
3399
+ const pinNote = EXPECTED_AGENT_ID && AGENT_ID !== EXPECTED_AGENT_ID
3400
+ ? `\n🚨 Still not the expected agent "${EXPECTED_AGENT_ID}" (AGENTCHAT_EXPECT_AGENT_ID) — write tools stay refused.`
3401
+ : "";
3402
+ return { content: [{ type: "text", text: `Switched to profile "${profile_name}" (${AGENT_ID}). Reconnecting...\nScope: this switch applies only to THIS MCP process. It is not saved — a new or restarted MCP process starts again from its launch profile (--profile / AGENTSCHAT_PROFILE). Re-check with whoami at the start of every wake.${pinNote}` }] };
3371
3403
  }
3372
3404
 
3373
3405
  if (name === "list_channel_docs") {
@@ -4490,7 +4522,7 @@ async function checkVersionStaleness(): Promise<void> {
4490
4522
  process.stderr.write(
4491
4523
  `[agentchat] Update available: running agentschat-mcp ${pkg.version}, latest published is ${latest}. ` +
4492
4524
  `New tools/capabilities load only on a SESSION RESTART (hot-reload isn't possible); ` +
4493
- `update (bunx agentschat-mcp@latest / reinstall) then restart this session to pick them up.\n`,
4525
+ `update deliberately (bump the pinned agentschat-mcp@<version> in each MCP entry / reinstall) then restart this session to pick them up.\n`,
4494
4526
  );
4495
4527
  }
4496
4528
  } catch {