macro-agent 0.1.0 → 0.1.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/.claude/settings.local.json +3 -1
- package/.sudocode/specs.jsonl +4 -0
- package/CLAUDE.md +16 -14
- package/README.md +11 -29
- package/dist/acp/macro-agent.d.ts +15 -0
- package/dist/acp/macro-agent.d.ts.map +1 -1
- package/dist/acp/macro-agent.js +131 -35
- package/dist/acp/macro-agent.js.map +1 -1
- package/dist/acp/types.d.ts +32 -1
- package/dist/acp/types.d.ts.map +1 -1
- package/dist/acp/types.js.map +1 -1
- package/dist/agent/agent-manager.d.ts +65 -1
- package/dist/agent/agent-manager.d.ts.map +1 -1
- package/dist/agent/agent-manager.js +464 -183
- package/dist/agent/agent-manager.js.map +1 -1
- package/dist/agent/types.d.ts +1 -1
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/api/server.d.ts +3 -0
- package/dist/api/server.d.ts.map +1 -1
- package/dist/api/server.js +37 -6
- package/dist/api/server.js.map +1 -1
- package/dist/auth/index.d.ts +2 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +2 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/token.d.ts +41 -0
- package/dist/auth/token.d.ts.map +1 -0
- package/dist/auth/token.js +73 -0
- package/dist/auth/token.js.map +1 -0
- package/dist/cli/acp.d.ts +2 -23
- package/dist/cli/acp.d.ts.map +1 -1
- package/dist/cli/acp.js +127 -61
- package/dist/cli/acp.js.map +1 -1
- package/dist/cli/index.js +147 -15
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/mcp.d.ts +6 -0
- package/dist/cli/mcp.d.ts.map +1 -1
- package/dist/cli/mcp.js +268 -181
- package/dist/cli/mcp.js.map +1 -1
- package/dist/cli/parse-args.d.ts +20 -0
- package/dist/cli/parse-args.d.ts.map +1 -0
- package/dist/cli/parse-args.js +43 -0
- package/dist/cli/parse-args.js.map +1 -0
- package/dist/cli/stable-instance-id.d.ts +8 -0
- package/dist/cli/stable-instance-id.d.ts.map +1 -0
- package/dist/cli/stable-instance-id.js +14 -0
- package/dist/cli/stable-instance-id.js.map +1 -0
- package/dist/config/project-config.d.ts +74 -7
- package/dist/config/project-config.d.ts.map +1 -1
- package/dist/config/project-config.js +123 -20
- package/dist/config/project-config.js.map +1 -1
- package/dist/map/adapter/acp-over-map.d.ts +17 -0
- package/dist/map/adapter/acp-over-map.d.ts.map +1 -1
- package/dist/map/adapter/acp-over-map.js +384 -23
- package/dist/map/adapter/acp-over-map.js.map +1 -1
- package/dist/map/adapter/connection-manager.d.ts.map +1 -1
- package/dist/map/adapter/connection-manager.js +3 -0
- package/dist/map/adapter/connection-manager.js.map +1 -1
- package/dist/map/adapter/event-log.d.ts +87 -0
- package/dist/map/adapter/event-log.d.ts.map +1 -0
- package/dist/map/adapter/event-log.js +122 -0
- package/dist/map/adapter/event-log.js.map +1 -0
- package/dist/map/adapter/event-translator.js +6 -6
- package/dist/map/adapter/event-translator.js.map +1 -1
- package/dist/map/adapter/extensions/agent-lifecycle.d.ts +82 -0
- package/dist/map/adapter/extensions/agent-lifecycle.d.ts.map +1 -0
- package/dist/map/adapter/extensions/agent-lifecycle.js +164 -0
- package/dist/map/adapter/extensions/agent-lifecycle.js.map +1 -0
- package/dist/map/adapter/extensions/index.d.ts +10 -1
- package/dist/map/adapter/extensions/index.d.ts.map +1 -1
- package/dist/map/adapter/extensions/index.js +34 -0
- package/dist/map/adapter/extensions/index.js.map +1 -1
- package/dist/map/adapter/extensions/mcp-bridge.d.ts +57 -0
- package/dist/map/adapter/extensions/mcp-bridge.d.ts.map +1 -0
- package/dist/map/adapter/extensions/mcp-bridge.js +745 -0
- package/dist/map/adapter/extensions/mcp-bridge.js.map +1 -0
- package/dist/map/adapter/extensions/rename.d.ts +29 -0
- package/dist/map/adapter/extensions/rename.d.ts.map +1 -0
- package/dist/map/adapter/extensions/rename.js +49 -0
- package/dist/map/adapter/extensions/rename.js.map +1 -0
- package/dist/map/adapter/extensions/task.d.ts.map +1 -1
- package/dist/map/adapter/extensions/task.js +10 -0
- package/dist/map/adapter/extensions/task.js.map +1 -1
- package/dist/map/adapter/extensions/update-metadata.d.ts +29 -0
- package/dist/map/adapter/extensions/update-metadata.d.ts.map +1 -0
- package/dist/map/adapter/extensions/update-metadata.js +67 -0
- package/dist/map/adapter/extensions/update-metadata.js.map +1 -0
- package/dist/map/adapter/index.d.ts +2 -1
- package/dist/map/adapter/index.d.ts.map +1 -1
- package/dist/map/adapter/index.js +8 -2
- package/dist/map/adapter/index.js.map +1 -1
- package/dist/map/adapter/interface.d.ts +2 -0
- package/dist/map/adapter/interface.d.ts.map +1 -1
- package/dist/map/adapter/map-adapter.d.ts +3 -0
- package/dist/map/adapter/map-adapter.d.ts.map +1 -1
- package/dist/map/adapter/map-adapter.js +258 -35
- package/dist/map/adapter/map-adapter.js.map +1 -1
- package/dist/map/adapter/subscription-manager.d.ts.map +1 -1
- package/dist/map/adapter/subscription-manager.js +5 -1
- package/dist/map/adapter/subscription-manager.js.map +1 -1
- package/dist/map/adapter/types.d.ts +2 -0
- package/dist/map/adapter/types.d.ts.map +1 -1
- package/dist/mcp/map-client.d.ts +39 -0
- package/dist/mcp/map-client.d.ts.map +1 -0
- package/dist/mcp/map-client.js +129 -0
- package/dist/mcp/map-client.js.map +1 -0
- package/dist/mcp/mcp-server.d.ts +14 -0
- package/dist/mcp/mcp-server.d.ts.map +1 -1
- package/dist/mcp/mcp-server.js +113 -85
- package/dist/mcp/mcp-server.js.map +1 -1
- package/dist/mcp/types.d.ts +9 -1
- package/dist/mcp/types.d.ts.map +1 -1
- package/dist/mcp/types.js.map +1 -1
- package/dist/metrics/metrics.js +1 -1
- package/dist/metrics/metrics.js.map +1 -1
- package/dist/roles/capabilities.d.ts +3 -1
- package/dist/roles/capabilities.d.ts.map +1 -1
- package/dist/roles/capabilities.js +17 -7
- package/dist/roles/capabilities.js.map +1 -1
- package/dist/roles/config-loader.d.ts +6 -6
- package/dist/roles/config-loader.d.ts.map +1 -1
- package/dist/roles/config-loader.js +6 -6
- package/dist/roles/config-loader.js.map +1 -1
- package/dist/roles/registry.d.ts +2 -2
- package/dist/roles/registry.js +2 -2
- package/dist/server/combined-server.d.ts +20 -0
- package/dist/server/combined-server.d.ts.map +1 -1
- package/dist/server/combined-server.js +107 -8
- package/dist/server/combined-server.js.map +1 -1
- package/dist/store/event-store.d.ts +2 -1
- package/dist/store/event-store.d.ts.map +1 -1
- package/dist/store/event-store.js +69 -20
- package/dist/store/event-store.js.map +1 -1
- package/dist/store/types/agents.d.ts +18 -0
- package/dist/store/types/agents.d.ts.map +1 -1
- package/dist/store/types/events.d.ts +1 -1
- package/dist/store/types/events.d.ts.map +1 -1
- package/dist/task/backend/index.d.ts +47 -29
- package/dist/task/backend/index.d.ts.map +1 -1
- package/dist/task/backend/index.js +109 -71
- package/dist/task/backend/index.js.map +1 -1
- package/dist/task/backend/memory.d.ts +1 -0
- package/dist/task/backend/memory.d.ts.map +1 -1
- package/dist/task/backend/memory.js +3 -0
- package/dist/task/backend/memory.js.map +1 -1
- package/dist/task/backend/opentasks/backend.d.ts +140 -0
- package/dist/task/backend/opentasks/backend.d.ts.map +1 -0
- package/dist/task/backend/opentasks/backend.js +1023 -0
- package/dist/task/backend/opentasks/backend.js.map +1 -0
- package/dist/task/backend/opentasks/client.d.ts +337 -0
- package/dist/task/backend/opentasks/client.d.ts.map +1 -0
- package/dist/task/backend/opentasks/client.js +225 -0
- package/dist/task/backend/opentasks/client.js.map +1 -0
- package/dist/task/backend/opentasks/daemon-manager.d.ts +89 -0
- package/dist/task/backend/opentasks/daemon-manager.d.ts.map +1 -0
- package/dist/task/backend/opentasks/daemon-manager.js +195 -0
- package/dist/task/backend/opentasks/daemon-manager.js.map +1 -0
- package/dist/task/backend/opentasks/index.d.ts +21 -0
- package/dist/task/backend/opentasks/index.d.ts.map +1 -0
- package/dist/task/backend/opentasks/index.js +21 -0
- package/dist/task/backend/opentasks/index.js.map +1 -0
- package/dist/task/backend/opentasks/mapping.d.ts +48 -0
- package/dist/task/backend/opentasks/mapping.d.ts.map +1 -0
- package/dist/task/backend/opentasks/mapping.js +77 -0
- package/dist/task/backend/opentasks/mapping.js.map +1 -0
- package/dist/task/backend/types.d.ts +33 -53
- package/dist/task/backend/types.d.ts.map +1 -1
- package/dist/task/backend/types.js +7 -11
- package/dist/task/backend/types.js.map +1 -1
- package/dist/task/backend/unified-tool-provider.d.ts +57 -0
- package/dist/task/backend/unified-tool-provider.d.ts.map +1 -0
- package/dist/task/backend/unified-tool-provider.js +623 -0
- package/dist/task/backend/unified-tool-provider.js.map +1 -0
- package/dist/teams/team-loader.d.ts +2 -2
- package/dist/teams/team-loader.js +3 -3
- package/dist/teams/team-loader.js.map +1 -1
- package/dist/teams/team-runtime.d.ts.map +1 -1
- package/dist/teams/team-runtime.js +2 -0
- package/dist/teams/team-runtime.js.map +1 -1
- package/docs/architecture.md +7 -6
- package/docs/configuration.md +26 -62
- package/docs/implementation-details.md +5 -5
- package/docs/implementation-summary.md +17 -17
- package/docs/plan-self-driving-support.md +4 -4
- package/docs/spec-self-driving-support.md +10 -10
- package/docs/team-templates.md +2 -2
- package/docs/teams.md +3 -3
- package/docs/troubleshooting.md +10 -11
- package/package.json +6 -4
- package/src/__tests__/e2e/agent-spawn-visibility.e2e.test.ts +761 -0
- package/src/__tests__/e2e/full-agent-conflict-resolution.e2e.test.ts +2 -2
- package/src/__tests__/e2e/mcp-thin-client-bridge.e2e.test.ts +304 -0
- package/src/__tests__/e2e/mcp-tools-available.e2e.test.ts +324 -0
- package/src/__tests__/e2e/multi-agent.e2e.test.ts +5 -5
- package/src/__tests__/e2e/spawn-session-streaming.e2e.test.ts +563 -0
- package/src/acp/__tests__/integration.test.ts +56 -31
- package/src/acp/__tests__/macro-agent.test.ts +16 -7
- package/src/acp/macro-agent.ts +170 -36
- package/src/acp/types.ts +46 -1
- package/src/agent/__tests__/agent-manager.test.ts +228 -2
- package/src/agent/agent-manager.ts +714 -261
- package/src/agent/types.ts +3 -1
- package/src/api/server.ts +41 -7
- package/src/auth/__tests__/token.test.ts +100 -0
- package/src/auth/index.ts +1 -0
- package/src/auth/token.ts +82 -0
- package/src/cli/__tests__/acp.test.ts +1 -1
- package/src/cli/__tests__/stable-instance-id.test.ts +1 -1
- package/src/cli/acp.ts +130 -72
- package/src/cli/index.ts +120 -14
- package/src/cli/mcp.ts +311 -207
- package/src/cli/parse-args.ts +54 -0
- package/src/cli/stable-instance-id.ts +14 -0
- package/src/config/project-config.ts +190 -27
- package/src/lifecycle/__tests__/cascade-termination.test.ts +1 -1
- package/src/map/adapter/__tests__/acp-over-map-cancel.test.ts +22 -4
- package/src/map/adapter/__tests__/acp-over-map-getmodels.test.ts +355 -0
- package/src/map/adapter/__tests__/acp-over-map-history.test.ts +263 -0
- package/src/map/adapter/__tests__/acp-over-map-persistence.e2e.test.ts +1 -1
- package/src/map/adapter/__tests__/event-broadcast.test.ts +420 -0
- package/src/map/adapter/__tests__/event-log.test.ts +527 -0
- package/src/map/adapter/__tests__/event-translator.test.ts +3 -3
- package/src/map/adapter/__tests__/extensions.test.ts +408 -0
- package/src/map/adapter/__tests__/map-adapter.test.ts +99 -0
- package/src/map/adapter/__tests__/mcp-bridge.test.ts +1187 -0
- package/src/map/adapter/__tests__/multi-client-broadcast.test.ts +711 -0
- package/src/map/adapter/__tests__/websocket-integration.test.ts +218 -0
- package/src/map/adapter/acp-over-map.ts +678 -66
- package/src/map/adapter/connection-manager.ts +3 -0
- package/src/map/adapter/event-log.ts +208 -0
- package/src/map/adapter/event-translator.ts +6 -6
- package/src/map/adapter/extensions/agent-lifecycle.ts +267 -0
- package/src/map/adapter/extensions/index.ts +60 -0
- package/src/map/adapter/extensions/mcp-bridge.ts +995 -0
- package/src/map/adapter/extensions/task.ts +11 -0
- package/src/map/adapter/extensions/update-metadata.ts +126 -0
- package/src/map/adapter/index.ts +28 -0
- package/src/map/adapter/interface.ts +2 -0
- package/src/map/adapter/map-adapter.ts +312 -47
- package/src/map/adapter/subscription-manager.ts +5 -1
- package/src/map/adapter/types.ts +2 -0
- package/src/mcp/__tests__/map-client.test.ts +386 -0
- package/src/mcp/__tests__/mcp-server-thin-client.test.ts +368 -0
- package/src/mcp/__tests__/mcp-server.test.ts +100 -1
- package/src/mcp/map-client.ts +177 -0
- package/src/mcp/mcp-server.ts +191 -100
- package/src/mcp/types.ts +6 -1
- package/src/metrics/metrics.ts +1 -1
- package/src/monitor/__tests__/stale-agent-flow.integration.test.ts +1 -1
- package/src/roles/__tests__/config-loader.test.ts +7 -7
- package/src/roles/capabilities.ts +17 -7
- package/src/roles/config-loader.ts +6 -6
- package/src/roles/registry.ts +2 -2
- package/src/server/__tests__/combined-server.test.ts +94 -21
- package/src/server/combined-server.ts +189 -33
- package/src/steering/__tests__/steering-integration.test.ts +1 -1
- package/src/store/__tests__/event-store.test.ts +196 -1
- package/src/store/__tests__/instance.test.ts +3 -3
- package/src/store/event-store.ts +80 -21
- package/src/store/types/agents.ts +15 -0
- package/src/store/types/events.ts +1 -1
- package/src/task/backend/__tests__/create-task-backend.test.ts +225 -0
- package/src/task/backend/__tests__/e2e/unified-tool-provider-opentasks.e2e.test.ts +524 -0
- package/src/task/backend/__tests__/unified-tool-provider.test.ts +579 -0
- package/src/task/backend/index.ts +156 -106
- package/src/task/backend/memory.ts +4 -0
- package/src/task/backend/opentasks/__tests__/backend.test.ts +968 -0
- package/src/task/backend/opentasks/__tests__/daemon-manager.test.ts +406 -0
- package/src/task/backend/opentasks/__tests__/mapping.test.ts +84 -0
- package/src/task/backend/opentasks/__tests__/opentasks-backend.e2e.test.ts +1338 -0
- package/src/task/backend/opentasks/backend.ts +1323 -0
- package/src/task/backend/opentasks/client.ts +652 -0
- package/src/task/backend/opentasks/daemon-manager.ts +253 -0
- package/src/task/backend/opentasks/index.ts +69 -0
- package/src/task/backend/opentasks/mapping.ts +94 -0
- package/src/task/backend/types.ts +42 -66
- package/src/task/backend/unified-tool-provider.ts +779 -0
- package/src/teams/__tests__/cross-subsystem.integration.test.ts +1 -1
- package/src/teams/team-loader.ts +3 -3
- package/src/teams/team-runtime.ts +2 -0
- package/test_fixtures/README.md +2 -3
- package/test_fixtures/fixtures/index.ts +0 -3
- package/test_fixtures/fixtures/projects/project-with-specs.ts +7 -149
- package/test_fixtures/fixtures/repos/index.ts +1 -3
- package/test_fixtures/fixtures/repos/temp-repo-factory.ts +0 -116
- package/test_fixtures/fixtures/repos/types.ts +0 -11
- package/test_fixtures/harness/__tests__/fixtures.test.ts +10 -102
- package/test_fixtures/harness/__tests__/temp-repo-and-simulator.test.ts +0 -33
- package/test_fixtures/harness/simulator/agent-simulator.ts +4 -4
- package/vitest.config.ts +1 -1
- package/vitest.e2e.config.ts +1 -1
- package/vitest.setup.ts +1 -30
- package/.macro-agent/teams/self-driving/prompts/grinder.md +0 -27
- package/.macro-agent/teams/self-driving/prompts/judge.md +0 -27
- package/.macro-agent/teams/self-driving/prompts/planner.md +0 -33
- package/.macro-agent/teams/self-driving/roles/grinder.yaml +0 -17
- package/.macro-agent/teams/self-driving/roles/judge.yaml +0 -24
- package/.macro-agent/teams/self-driving/roles/planner.yaml +0 -18
- package/.macro-agent/teams/self-driving/team.yaml +0 -103
- package/.macro-agent/teams/structured/prompts/developer.md +0 -26
- package/.macro-agent/teams/structured/prompts/lead.md +0 -25
- package/.macro-agent/teams/structured/prompts/reviewer.md +0 -24
- package/.macro-agent/teams/structured/roles/developer.yaml +0 -12
- package/.macro-agent/teams/structured/roles/lead.yaml +0 -11
- package/.macro-agent/teams/structured/roles/reviewer.yaml +0 -19
- package/.macro-agent/teams/structured/team.yaml +0 -89
- package/docs/sudocode-integration.md +0 -383
- package/src/task/backend/__tests__/backend-parity.test.ts +0 -451
- package/src/task/backend/__tests__/tool-provider-edge-cases.test.ts +0 -430
- package/src/task/backend/__tests__/tool-provider.test.ts +0 -983
- package/src/task/backend/sudocode/__tests__/backend-edge-cases.test.ts +0 -575
- package/src/task/backend/sudocode/__tests__/backend.test.ts +0 -1194
- package/src/task/backend/sudocode/__tests__/client-integration.test.ts +0 -418
- package/src/task/backend/sudocode/__tests__/client.test.ts +0 -345
- package/src/task/backend/sudocode/__tests__/e2e/backend.e2e.test.ts +0 -753
- package/src/task/backend/sudocode/__tests__/e2e/server-client.e2e.test.ts +0 -680
- package/src/task/backend/sudocode/__tests__/e2e-workflow.test.ts +0 -666
- package/src/task/backend/sudocode/__tests__/integration/standalone-client.integration.test.ts +0 -396
- package/src/task/backend/sudocode/__tests__/integration/sudocode-cli.integration.test.ts +0 -328
- package/src/task/backend/sudocode/__tests__/integration/test-utils.ts +0 -175
- package/src/task/backend/sudocode/__tests__/mapping-edge-cases.test.ts +0 -265
- package/src/task/backend/sudocode/__tests__/server-client.test.ts +0 -675
- package/src/task/backend/sudocode/__tests__/sync-policy-edge-cases.test.ts +0 -521
- package/src/task/backend/sudocode/__tests__/sync-policy.test.ts +0 -519
- package/src/task/backend/sudocode/__tests__/tools.test.ts +0 -471
- package/src/task/backend/sudocode/backend.ts +0 -1237
- package/src/task/backend/sudocode/client.ts +0 -515
- package/src/task/backend/sudocode/index.ts +0 -120
- package/src/task/backend/sudocode/mapping.ts +0 -93
- package/src/task/backend/sudocode/server-client.ts +0 -522
- package/src/task/backend/sudocode/standalone-client.ts +0 -623
- package/src/task/backend/sudocode/sync-policy.ts +0 -387
- package/src/task/backend/sudocode/tools.ts +0 -896
- package/src/task/backend/tool-provider.ts +0 -506
- package/test_fixtures/fixtures/sudocode/index.ts +0 -29
- package/test_fixtures/fixtures/sudocode/issues.ts +0 -185
- package/test_fixtures/fixtures/sudocode/specs.ts +0 -159
|
@@ -0,0 +1,995 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Bridge Extensions (_macro/mcp/*)
|
|
3
|
+
*
|
|
4
|
+
* Server-side extension handlers that mirror each MCP tool.
|
|
5
|
+
* When MCP subprocesses run in thin-client mode, their tool calls
|
|
6
|
+
* are routed here via ephemeral MAP WebSocket connections.
|
|
7
|
+
*
|
|
8
|
+
* Each handler receives agent context via `params.context` since the
|
|
9
|
+
* MAP ExtensionContext only has participant info, not agent identity.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { MAPAdapter, ExtensionHandler, ExtensionContext } from "../interface.js";
|
|
13
|
+
import type { EventStore } from "../../../store/event-store.js";
|
|
14
|
+
import type { AgentManager } from "../../../agent/agent-manager.js";
|
|
15
|
+
import type { TaskManager } from "../../../task/task-manager.js";
|
|
16
|
+
import type { MessageRouter } from "../../../router/message-router.js";
|
|
17
|
+
import type { PeerManager } from "../../../peer/peer-manager.js";
|
|
18
|
+
import type { ActivityWatcher } from "../../../activity/watcher.js";
|
|
19
|
+
import type { RoleRegistry } from "../../../roles/types.js";
|
|
20
|
+
import type { TaskBackend } from "../../../task/backend/types.js";
|
|
21
|
+
import type { AgentId } from "../../../store/types/index.js";
|
|
22
|
+
import type { ToolContext } from "../../../mcp/types.js";
|
|
23
|
+
import type { Agent } from "../../../store/types/index.js";
|
|
24
|
+
import type { AgentStopReason } from "../../../agent/types.js";
|
|
25
|
+
import type { EventNotification, MAPEventType } from "../types.js";
|
|
26
|
+
import { RPCError } from "../rpc-handler.js";
|
|
27
|
+
import type { AgentTokenManager } from "../../../auth/token.js";
|
|
28
|
+
import { ulid } from "ulid";
|
|
29
|
+
import { createDoneHandler, type DoneToolDeps } from "../../../mcp/tools/done.js";
|
|
30
|
+
import { createInjectContextHandler } from "../../../mcp/tools/inject_context.js";
|
|
31
|
+
import {
|
|
32
|
+
createWaitForActivityHandler,
|
|
33
|
+
} from "../../../mcp/tools/wait_for_activity.js";
|
|
34
|
+
import { createClaimTaskHandler } from "../../../mcp/tools/claim_task.js";
|
|
35
|
+
import { createUnclaimTaskHandler } from "../../../mcp/tools/unclaim_task.js";
|
|
36
|
+
import { createListClaimableTasksHandler } from "../../../mcp/tools/list_claimable_tasks.js";
|
|
37
|
+
|
|
38
|
+
// =============================================================================
|
|
39
|
+
// Services
|
|
40
|
+
// =============================================================================
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Services required for MCP bridge extensions.
|
|
44
|
+
*/
|
|
45
|
+
export interface MCPBridgeServices {
|
|
46
|
+
eventStore: EventStore;
|
|
47
|
+
agentManager: AgentManager;
|
|
48
|
+
taskManager: TaskManager;
|
|
49
|
+
messageRouter: MessageRouter;
|
|
50
|
+
peerManager?: PeerManager;
|
|
51
|
+
activityWatcher?: ActivityWatcher;
|
|
52
|
+
roleRegistry?: RoleRegistry;
|
|
53
|
+
taskBackend?: TaskBackend;
|
|
54
|
+
taskToolProvider?: import("../../../task/backend/types.js").TaskToolProvider;
|
|
55
|
+
/** Mutable context holder for task tool provider agent_id injection */
|
|
56
|
+
taskToolContext?: { agent_id: string };
|
|
57
|
+
integrationStrategy?: import("../../../workspace/strategies/types.js").IntegrationStrategy;
|
|
58
|
+
/** Optional agent token manager for per-agent authentication */
|
|
59
|
+
agentTokenManager?: AgentTokenManager;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// =============================================================================
|
|
63
|
+
// Agent Token Validation (module-level, set by registerMCPBridgeExtensions)
|
|
64
|
+
// =============================================================================
|
|
65
|
+
|
|
66
|
+
let _agentTokenManager: AgentTokenManager | undefined;
|
|
67
|
+
|
|
68
|
+
// =============================================================================
|
|
69
|
+
// Agent Context Extraction
|
|
70
|
+
// =============================================================================
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Agent context passed in params.context by thin-client MCP tools.
|
|
74
|
+
*/
|
|
75
|
+
interface AgentContext {
|
|
76
|
+
agent_id: string;
|
|
77
|
+
session_id: string;
|
|
78
|
+
task_id?: string;
|
|
79
|
+
lineage: string[];
|
|
80
|
+
cwd: string;
|
|
81
|
+
agent_token?: string;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Extract and validate agent context from params.
|
|
86
|
+
*/
|
|
87
|
+
function extractContext(params: unknown): { context: AgentContext; args: Record<string, unknown> } {
|
|
88
|
+
const p = (params ?? {}) as Record<string, unknown>;
|
|
89
|
+
const context = p.context as AgentContext | undefined;
|
|
90
|
+
|
|
91
|
+
if (!context?.agent_id) {
|
|
92
|
+
throw RPCError.invalidParams("params.context.agent_id is required");
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Validate per-agent token if token manager is configured
|
|
96
|
+
if (_agentTokenManager) {
|
|
97
|
+
if (!context.agent_token || !_agentTokenManager.verifyToken(context.agent_id, context.agent_token)) {
|
|
98
|
+
throw RPCError.invalidParams("Invalid or missing agent token");
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Return everything except context as args
|
|
103
|
+
const { context: _, ...args } = p;
|
|
104
|
+
return { context, args };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Build a ToolContext from the agent context.
|
|
109
|
+
*/
|
|
110
|
+
function toToolContext(ctx: AgentContext): ToolContext {
|
|
111
|
+
return {
|
|
112
|
+
agent_id: ctx.agent_id,
|
|
113
|
+
session_id: ctx.session_id,
|
|
114
|
+
task_id: ctx.task_id,
|
|
115
|
+
lineage: ctx.lineage ?? [],
|
|
116
|
+
cwd: ctx.cwd ?? process.cwd(),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// =============================================================================
|
|
121
|
+
// Handler Implementations
|
|
122
|
+
// =============================================================================
|
|
123
|
+
|
|
124
|
+
function createSpawnAgentBridge(
|
|
125
|
+
services: MCPBridgeServices,
|
|
126
|
+
emitMAPEvent: (event: EventNotification) => void,
|
|
127
|
+
): ExtensionHandler {
|
|
128
|
+
return async (_extCtx: ExtensionContext, params: unknown) => {
|
|
129
|
+
const { context, args } = extractContext(params);
|
|
130
|
+
|
|
131
|
+
const task = args.task as string;
|
|
132
|
+
const spawned = await services.agentManager.spawn({
|
|
133
|
+
task,
|
|
134
|
+
parent: context.agent_id,
|
|
135
|
+
subscribeParent: (args.subscribe_parent as boolean) ?? true,
|
|
136
|
+
topics: (args.topics as string[]) ?? [],
|
|
137
|
+
config: args.config as Record<string, unknown> | undefined,
|
|
138
|
+
cwd: (args.cwd as string) ?? context.cwd,
|
|
139
|
+
permissionMode: (args.permission_mode as string | undefined) as import("acp-factory").PermissionMode | undefined,
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// Fire-and-forget initial prompt so the agent starts working on its task.
|
|
143
|
+
// Without this, the agent process is running but idle — waiting for a message.
|
|
144
|
+
if (task) {
|
|
145
|
+
(async () => {
|
|
146
|
+
// Accumulate assistant response parts for history persistence
|
|
147
|
+
const buffer: {
|
|
148
|
+
parts: Array<
|
|
149
|
+
| { type: "text"; text: string }
|
|
150
|
+
| ({ type: "tool" } & Record<string, unknown>)
|
|
151
|
+
>;
|
|
152
|
+
} = { parts: [] };
|
|
153
|
+
const toolInfoCache = new Map<
|
|
154
|
+
string,
|
|
155
|
+
{ title?: string; name?: string; input?: unknown }
|
|
156
|
+
>();
|
|
157
|
+
|
|
158
|
+
// Emit user message event so TUI clients can show the task prompt
|
|
159
|
+
emitMAPEvent({
|
|
160
|
+
eventId: ulid(),
|
|
161
|
+
type: "session_user_message" as MAPEventType,
|
|
162
|
+
timestamp: Date.now(),
|
|
163
|
+
agentId: spawned.id as AgentId,
|
|
164
|
+
data: {
|
|
165
|
+
agentId: spawned.id,
|
|
166
|
+
sessionId: spawned.session_id,
|
|
167
|
+
content: task,
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
try {
|
|
172
|
+
for await (const update of services.agentManager.prompt(
|
|
173
|
+
spawned.id,
|
|
174
|
+
task,
|
|
175
|
+
)) {
|
|
176
|
+
// Accumulate content for turn recording (mirrors acp-over-map.ts handlePrompt)
|
|
177
|
+
const u = update as Record<string, unknown>;
|
|
178
|
+
const updateType = (u.sessionUpdate as string) ?? (u.type as string);
|
|
179
|
+
|
|
180
|
+
if (updateType === "agent_message_chunk") {
|
|
181
|
+
const content = u.content as { type?: string; text?: string } | undefined;
|
|
182
|
+
if (content?.text) {
|
|
183
|
+
const last = buffer.parts[buffer.parts.length - 1];
|
|
184
|
+
if (last && last.type === "text") {
|
|
185
|
+
last.text += content.text;
|
|
186
|
+
} else {
|
|
187
|
+
buffer.parts.push({ type: "text", text: content.text });
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
} else if (updateType === "tool_call" || updateType === "tool_call_update") {
|
|
191
|
+
const toolCallId = u.toolCallId as string | undefined;
|
|
192
|
+
const status = u.status as string | undefined;
|
|
193
|
+
const meta = u._meta as { claudeCode?: { toolName?: string } } | undefined;
|
|
194
|
+
|
|
195
|
+
if (updateType === "tool_call" && toolCallId) {
|
|
196
|
+
toolInfoCache.set(toolCallId, {
|
|
197
|
+
title: u.title as string | undefined,
|
|
198
|
+
name: meta?.claudeCode?.toolName,
|
|
199
|
+
input: u.rawInput,
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (status === "completed" || status === "failed") {
|
|
204
|
+
const cached = toolCallId ? toolInfoCache.get(toolCallId) : undefined;
|
|
205
|
+
const rawOutput = u.rawOutput;
|
|
206
|
+
let output: string | undefined;
|
|
207
|
+
if (typeof rawOutput === "string") output = rawOutput;
|
|
208
|
+
else if (Array.isArray(rawOutput)) {
|
|
209
|
+
output = rawOutput
|
|
210
|
+
.filter((item: any) => item.type === "text" && typeof item.text === "string")
|
|
211
|
+
.map((item: any) => item.text as string)
|
|
212
|
+
.join("\n") || undefined;
|
|
213
|
+
}
|
|
214
|
+
buffer.parts.push({
|
|
215
|
+
type: "tool",
|
|
216
|
+
toolCallId,
|
|
217
|
+
title: u.title ?? cached?.title,
|
|
218
|
+
name: meta?.claudeCode?.toolName ?? cached?.name,
|
|
219
|
+
status: u.status,
|
|
220
|
+
input: u.rawInput ?? cached?.input,
|
|
221
|
+
output,
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Emit each session update as a MAP event for live streaming
|
|
227
|
+
emitMAPEvent({
|
|
228
|
+
eventId: ulid(),
|
|
229
|
+
type: "session_update" as MAPEventType,
|
|
230
|
+
timestamp: Date.now(),
|
|
231
|
+
agentId: spawned.id as AgentId,
|
|
232
|
+
data: {
|
|
233
|
+
agentId: spawned.id,
|
|
234
|
+
sessionId: spawned.session_id,
|
|
235
|
+
update,
|
|
236
|
+
},
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Emit prompt done event
|
|
241
|
+
emitMAPEvent({
|
|
242
|
+
eventId: ulid(),
|
|
243
|
+
type: "session_prompt_done" as MAPEventType,
|
|
244
|
+
timestamp: Date.now(),
|
|
245
|
+
agentId: spawned.id as AgentId,
|
|
246
|
+
data: {
|
|
247
|
+
agentId: spawned.id,
|
|
248
|
+
sessionId: spawned.session_id,
|
|
249
|
+
stopReason: "end_turn",
|
|
250
|
+
},
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
// Record turns so history is available when TUI reconnects
|
|
254
|
+
const now = Date.now();
|
|
255
|
+
const conversationId = spawned.session_id;
|
|
256
|
+
|
|
257
|
+
// Record user turn (the initial task prompt)
|
|
258
|
+
services.eventStore.emit({
|
|
259
|
+
type: "turn",
|
|
260
|
+
source: { agent_id: spawned.id },
|
|
261
|
+
payload: {
|
|
262
|
+
action: "recorded",
|
|
263
|
+
turn_id: `turn_user_${now}_${Math.random().toString(36).slice(2, 8)}`,
|
|
264
|
+
conversation_id: conversationId,
|
|
265
|
+
participant: "user",
|
|
266
|
+
timestamp: now,
|
|
267
|
+
content_type: "user_prompt",
|
|
268
|
+
content: task,
|
|
269
|
+
source_type: "explicit",
|
|
270
|
+
},
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
// Record assistant turn with accumulated content
|
|
274
|
+
if (buffer.parts.length > 0) {
|
|
275
|
+
services.eventStore.emit({
|
|
276
|
+
type: "turn",
|
|
277
|
+
source: { agent_id: spawned.id },
|
|
278
|
+
payload: {
|
|
279
|
+
action: "recorded",
|
|
280
|
+
turn_id: `turn_asst_${now}_${Math.random().toString(36).slice(2, 8)}`,
|
|
281
|
+
conversation_id: conversationId,
|
|
282
|
+
participant: spawned.id,
|
|
283
|
+
timestamp: now + 1,
|
|
284
|
+
content_type: "assistant_response",
|
|
285
|
+
content: { parts: buffer.parts },
|
|
286
|
+
source_type: "explicit",
|
|
287
|
+
},
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
} catch (err) {
|
|
291
|
+
// Emit prompt done with error so TUI stops spinner
|
|
292
|
+
emitMAPEvent({
|
|
293
|
+
eventId: ulid(),
|
|
294
|
+
type: "session_prompt_done" as MAPEventType,
|
|
295
|
+
timestamp: Date.now(),
|
|
296
|
+
agentId: spawned.id as AgentId,
|
|
297
|
+
data: {
|
|
298
|
+
agentId: spawned.id,
|
|
299
|
+
sessionId: spawned.session_id,
|
|
300
|
+
stopReason: "error",
|
|
301
|
+
},
|
|
302
|
+
});
|
|
303
|
+
console.error(
|
|
304
|
+
`[MCP Bridge] Failed to send initial prompt to spawned agent ${spawned.id}:`,
|
|
305
|
+
err,
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
})();
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
return {
|
|
312
|
+
agent_id: spawned.id,
|
|
313
|
+
task_id: spawned.agent.task_id,
|
|
314
|
+
session_id: spawned.session_id,
|
|
315
|
+
};
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function createEmitStatusBridge(
|
|
320
|
+
services: MCPBridgeServices,
|
|
321
|
+
emitMAPEvent: (event: EventNotification) => void,
|
|
322
|
+
): ExtensionHandler {
|
|
323
|
+
return async (_extCtx: ExtensionContext, params: unknown) => {
|
|
324
|
+
const { context, args } = extractContext(params);
|
|
325
|
+
|
|
326
|
+
const event = services.eventStore.emit({
|
|
327
|
+
type: "status",
|
|
328
|
+
source: { agent_id: context.agent_id },
|
|
329
|
+
payload: {
|
|
330
|
+
status_type: args.status_type,
|
|
331
|
+
summary: args.summary,
|
|
332
|
+
details: args.details,
|
|
333
|
+
},
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
// Emit MAP event so TUI subscribers see the status update
|
|
337
|
+
const now = Date.now();
|
|
338
|
+
emitMAPEvent({
|
|
339
|
+
eventId: ulid(),
|
|
340
|
+
type: "status_emitted" as MAPEventType,
|
|
341
|
+
timestamp: now,
|
|
342
|
+
agentId: context.agent_id as AgentId,
|
|
343
|
+
data: {
|
|
344
|
+
id: event.id,
|
|
345
|
+
agentId: context.agent_id,
|
|
346
|
+
taskId: context.task_id,
|
|
347
|
+
statusType: args.status_type,
|
|
348
|
+
summary: args.summary,
|
|
349
|
+
details: args.details,
|
|
350
|
+
timestamp: now,
|
|
351
|
+
},
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
let taskUpdated = false;
|
|
355
|
+
|
|
356
|
+
if (args.complete_task && args.status_type === "completed" && context.task_id) {
|
|
357
|
+
try {
|
|
358
|
+
services.taskManager.updateStatus(context.task_id, "completed");
|
|
359
|
+
taskUpdated = true;
|
|
360
|
+
} catch { /* Task may not exist */ }
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
if (args.complete_task && args.status_type === "failed" && context.task_id) {
|
|
364
|
+
try {
|
|
365
|
+
services.taskManager.updateStatus(context.task_id, "failed");
|
|
366
|
+
taskUpdated = true;
|
|
367
|
+
} catch { /* Task may not exist */ }
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
return { event_id: event.id, task_updated: taskUpdated };
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function createSendMessageBridge(services: MCPBridgeServices): ExtensionHandler {
|
|
375
|
+
return async (_extCtx: ExtensionContext, params: unknown) => {
|
|
376
|
+
const { context, args } = extractContext(params);
|
|
377
|
+
const to = args.to as { agent_id?: string; task_id?: string; topic?: string } | undefined;
|
|
378
|
+
|
|
379
|
+
if (!to) {
|
|
380
|
+
throw RPCError.invalidParams("params.to is required");
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
const address = to.agent_id
|
|
384
|
+
? { agent: to.agent_id }
|
|
385
|
+
: to.task_id
|
|
386
|
+
? { task: to.task_id }
|
|
387
|
+
: to.topic
|
|
388
|
+
? { scope: to.topic }
|
|
389
|
+
: null;
|
|
390
|
+
|
|
391
|
+
if (!address) {
|
|
392
|
+
throw RPCError.invalidParams("Must specify one of: agent_id, task_id, or topic");
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const result = await services.messageRouter.sendToAddress({
|
|
396
|
+
from: context.agent_id,
|
|
397
|
+
to: address,
|
|
398
|
+
content: args.content as string,
|
|
399
|
+
options: args.correlation_id ? { correlationId: args.correlation_id as string } : undefined,
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
return {
|
|
403
|
+
message_id: result.id,
|
|
404
|
+
delivered_to: result.delivered.length,
|
|
405
|
+
};
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
function createCheckMessagesBridge(services: MCPBridgeServices): ExtensionHandler {
|
|
410
|
+
return async (_extCtx: ExtensionContext, params: unknown) => {
|
|
411
|
+
const { context, args } = extractContext(params);
|
|
412
|
+
const limit = (args.limit as number) ?? 10;
|
|
413
|
+
|
|
414
|
+
const internalMessages = services.messageRouter.getMessages(context.agent_id, {
|
|
415
|
+
limit,
|
|
416
|
+
includeAcknowledged: (args.include_acknowledged as boolean) ?? false,
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
const formattedInternalMessages = internalMessages.map((msg) => ({
|
|
420
|
+
id: msg.id,
|
|
421
|
+
from: `agent:${msg.from.agent_id}`,
|
|
422
|
+
content: msg.content.length > 500 ? msg.content.substring(0, 500) : msg.content,
|
|
423
|
+
timestamp: msg.timestamp,
|
|
424
|
+
truncated: msg.truncated || msg.content.length > 500,
|
|
425
|
+
correlation_id: msg.correlation_id,
|
|
426
|
+
}));
|
|
427
|
+
|
|
428
|
+
// Get peer messages if peerManager is available
|
|
429
|
+
let formattedPeerMessages: Array<{
|
|
430
|
+
id: string;
|
|
431
|
+
from: string;
|
|
432
|
+
content: string;
|
|
433
|
+
timestamp: number;
|
|
434
|
+
truncated: boolean;
|
|
435
|
+
correlation_id?: string;
|
|
436
|
+
is_request?: boolean;
|
|
437
|
+
request_id?: string;
|
|
438
|
+
}> = [];
|
|
439
|
+
|
|
440
|
+
if (services.peerManager) {
|
|
441
|
+
const peerMessages = services.peerManager.getPeerMessages(context.agent_id);
|
|
442
|
+
formattedPeerMessages = peerMessages.map((msg) => ({
|
|
443
|
+
id: msg.id,
|
|
444
|
+
from: msg.from,
|
|
445
|
+
content: typeof msg.payload === "string"
|
|
446
|
+
? msg.payload.length > 500 ? msg.payload.substring(0, 500) : msg.payload
|
|
447
|
+
: JSON.stringify(msg.payload).substring(0, 500),
|
|
448
|
+
timestamp: msg.timestamp,
|
|
449
|
+
truncated: typeof msg.payload === "string" ? msg.payload.length > 500 : false,
|
|
450
|
+
correlation_id: msg.correlationId,
|
|
451
|
+
is_request: msg.isRequest,
|
|
452
|
+
request_id: msg.requestId,
|
|
453
|
+
}));
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
const allFormattedMessages = [...formattedInternalMessages, ...formattedPeerMessages]
|
|
457
|
+
.sort((a, b) => a.timestamp - b.timestamp)
|
|
458
|
+
.slice(0, limit);
|
|
459
|
+
|
|
460
|
+
const allInternalMessages = services.messageRouter.getMessages(context.agent_id, {
|
|
461
|
+
limit: 1000,
|
|
462
|
+
includeAcknowledged: false,
|
|
463
|
+
});
|
|
464
|
+
const allPeerMessages = services.peerManager ? services.peerManager.getPeerMessages(context.agent_id) : [];
|
|
465
|
+
|
|
466
|
+
return {
|
|
467
|
+
messages: allFormattedMessages,
|
|
468
|
+
total_pending: allInternalMessages.length + allPeerMessages.length,
|
|
469
|
+
};
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
function createQueryIndexBridge(services: MCPBridgeServices): ExtensionHandler {
|
|
474
|
+
return async (_extCtx: ExtensionContext, params: unknown) => {
|
|
475
|
+
const { context: _context, args } = extractContext(params);
|
|
476
|
+
|
|
477
|
+
const entries: Array<{
|
|
478
|
+
type: "agent" | "task";
|
|
479
|
+
id: string;
|
|
480
|
+
summary: string;
|
|
481
|
+
state?: string;
|
|
482
|
+
status?: string;
|
|
483
|
+
}> = [];
|
|
484
|
+
|
|
485
|
+
const limit = (args.limit as number) ?? 20;
|
|
486
|
+
const offset = (args.offset as number) ?? 0;
|
|
487
|
+
const search = (args.search as string)?.toLowerCase();
|
|
488
|
+
const filter = args.filter as { state?: string; status?: string; parent?: string | null } | undefined;
|
|
489
|
+
const queryType = args.type as string;
|
|
490
|
+
|
|
491
|
+
if (queryType === "agents" || queryType === "all") {
|
|
492
|
+
let agents = services.agentManager.list();
|
|
493
|
+
|
|
494
|
+
if (filter?.state) {
|
|
495
|
+
agents = agents.filter((a) => a.state === filter!.state);
|
|
496
|
+
}
|
|
497
|
+
if (filter?.parent !== undefined) {
|
|
498
|
+
agents = agents.filter((a) => a.parent === filter!.parent);
|
|
499
|
+
}
|
|
500
|
+
if (search) {
|
|
501
|
+
agents = agents.filter(
|
|
502
|
+
(a) =>
|
|
503
|
+
a.id.toLowerCase().includes(search) ||
|
|
504
|
+
a.task?.toLowerCase().includes(search)
|
|
505
|
+
);
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
for (const agent of agents) {
|
|
509
|
+
entries.push({
|
|
510
|
+
type: "agent",
|
|
511
|
+
id: agent.id,
|
|
512
|
+
summary: agent.task ?? "No task description",
|
|
513
|
+
state: agent.state,
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
if (queryType === "tasks" || queryType === "all") {
|
|
519
|
+
let tasks = services.taskManager.list();
|
|
520
|
+
|
|
521
|
+
if (filter?.status) {
|
|
522
|
+
tasks = tasks.filter((t) => t.status === filter!.status);
|
|
523
|
+
}
|
|
524
|
+
if (search) {
|
|
525
|
+
tasks = tasks.filter((t) =>
|
|
526
|
+
t.id.toLowerCase().includes(search) ||
|
|
527
|
+
t.description.toLowerCase().includes(search)
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
for (const task of tasks) {
|
|
532
|
+
entries.push({
|
|
533
|
+
type: "task",
|
|
534
|
+
id: task.id,
|
|
535
|
+
summary: task.description,
|
|
536
|
+
status: task.status,
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
const paginatedEntries = entries.slice(offset, offset + limit);
|
|
542
|
+
|
|
543
|
+
return {
|
|
544
|
+
entries: paginatedEntries,
|
|
545
|
+
total: entries.length,
|
|
546
|
+
has_more: offset + limit < entries.length,
|
|
547
|
+
};
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
function createGetHierarchyBridge(services: MCPBridgeServices): ExtensionHandler {
|
|
552
|
+
return async (_extCtx: ExtensionContext, params: unknown) => {
|
|
553
|
+
const { context, args } = extractContext(params);
|
|
554
|
+
|
|
555
|
+
let rootId: AgentId;
|
|
556
|
+
if (args.root) {
|
|
557
|
+
rootId = args.root as string;
|
|
558
|
+
} else {
|
|
559
|
+
rootId = context.lineage.length > 0 ? context.lineage[0] : context.agent_id;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
const hierarchy = services.agentManager.getHierarchy(rootId);
|
|
563
|
+
if (!hierarchy) {
|
|
564
|
+
throw RPCError.notFound("agent", rootId);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
function buildNode(node: { agent: Agent; children: Array<{ agent: Agent; children: any[] }> }, currentDepth: number): {
|
|
568
|
+
agent_id: string;
|
|
569
|
+
task: string;
|
|
570
|
+
state: string;
|
|
571
|
+
children: any[];
|
|
572
|
+
} {
|
|
573
|
+
const shouldIncludeChildren = args.depth === undefined || currentDepth < (args.depth as number);
|
|
574
|
+
return {
|
|
575
|
+
agent_id: node.agent.id,
|
|
576
|
+
task: node.agent.task ?? "No task",
|
|
577
|
+
state: node.agent.state,
|
|
578
|
+
children: shouldIncludeChildren
|
|
579
|
+
? node.children.map((c) => buildNode(c, currentDepth + 1))
|
|
580
|
+
: [],
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
const tree = buildNode(hierarchy.root, 1);
|
|
585
|
+
|
|
586
|
+
return {
|
|
587
|
+
tree,
|
|
588
|
+
depth: hierarchy.depth,
|
|
589
|
+
total_agents: hierarchy.totalAgents,
|
|
590
|
+
};
|
|
591
|
+
};
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
function createGetAgentSummaryBridge(services: MCPBridgeServices): ExtensionHandler {
|
|
595
|
+
return async (_extCtx: ExtensionContext, params: unknown) => {
|
|
596
|
+
const { context: _context, args } = extractContext(params);
|
|
597
|
+
const agentId = args.agent_id as string;
|
|
598
|
+
|
|
599
|
+
const agent = services.agentManager.get(agentId);
|
|
600
|
+
if (!agent) {
|
|
601
|
+
throw RPCError.notFound("agent", agentId);
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
const children = services.agentManager.getChildren(agentId);
|
|
605
|
+
|
|
606
|
+
const statusEvents = services.eventStore.query({
|
|
607
|
+
type: "status",
|
|
608
|
+
source_agent_id: agentId,
|
|
609
|
+
limit: 1,
|
|
610
|
+
});
|
|
611
|
+
|
|
612
|
+
const recentStatus = statusEvents.length > 0
|
|
613
|
+
? {
|
|
614
|
+
type: statusEvents[0].payload.status_type as string,
|
|
615
|
+
summary: statusEvents[0].payload.summary as string,
|
|
616
|
+
timestamp: statusEvents[0].timestamp,
|
|
617
|
+
}
|
|
618
|
+
: undefined;
|
|
619
|
+
|
|
620
|
+
const lastActivity = agent.stopped_at ?? agent.started_at ?? agent.created_at;
|
|
621
|
+
|
|
622
|
+
return {
|
|
623
|
+
id: agent.id,
|
|
624
|
+
session_id: agent.session_id,
|
|
625
|
+
task: agent.task ?? "No task",
|
|
626
|
+
state: agent.state,
|
|
627
|
+
parent: agent.parent,
|
|
628
|
+
children_count: children.length,
|
|
629
|
+
last_activity: lastActivity,
|
|
630
|
+
recent_status: recentStatus,
|
|
631
|
+
};
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
function createStopAgentBridge(services: MCPBridgeServices): ExtensionHandler {
|
|
636
|
+
return async (_extCtx: ExtensionContext, params: unknown) => {
|
|
637
|
+
const { context, args } = extractContext(params);
|
|
638
|
+
const targetAgentId = args.agent_id as string;
|
|
639
|
+
const reason = ((args.reason as string) ?? "cancelled") as AgentStopReason;
|
|
640
|
+
|
|
641
|
+
const targetAgent = services.agentManager.get(targetAgentId);
|
|
642
|
+
if (!targetAgent) {
|
|
643
|
+
throw RPCError.notFound("agent", targetAgentId);
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
// Check subtree authorization
|
|
647
|
+
const isInSubtree =
|
|
648
|
+
targetAgentId === context.agent_id ||
|
|
649
|
+
targetAgent.lineage?.includes(context.agent_id);
|
|
650
|
+
|
|
651
|
+
if (!isInSubtree) {
|
|
652
|
+
throw RPCError.permissionDenied(
|
|
653
|
+
`Cannot stop agent outside your subtree: ${targetAgentId}`
|
|
654
|
+
);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
const stoppedAgents: AgentId[] = [];
|
|
658
|
+
|
|
659
|
+
async function stopRecursive(agentId: AgentId): Promise<void> {
|
|
660
|
+
const agent = services.agentManager.get(agentId);
|
|
661
|
+
if (!agent || agent.state === "stopped") return;
|
|
662
|
+
|
|
663
|
+
const children = services.agentManager.getChildren(agentId);
|
|
664
|
+
for (const child of children) {
|
|
665
|
+
await stopRecursive(child.id);
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
await services.agentManager.terminate(agentId, reason);
|
|
669
|
+
stoppedAgents.push(agentId);
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
await stopRecursive(targetAgentId);
|
|
673
|
+
|
|
674
|
+
return {
|
|
675
|
+
success: true,
|
|
676
|
+
stopped_agents: stoppedAgents,
|
|
677
|
+
};
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
function createDoneBridge(services: MCPBridgeServices): ExtensionHandler {
|
|
682
|
+
return async (_extCtx: ExtensionContext, params: unknown) => {
|
|
683
|
+
const { context, args } = extractContext(params);
|
|
684
|
+
const toolContext = toToolContext(context);
|
|
685
|
+
|
|
686
|
+
const doneDeps: DoneToolDeps = {
|
|
687
|
+
eventStore: services.eventStore,
|
|
688
|
+
agentManager: services.agentManager,
|
|
689
|
+
messageRouter: services.messageRouter,
|
|
690
|
+
taskManager: services.taskManager,
|
|
691
|
+
roleRegistry: services.roleRegistry,
|
|
692
|
+
integrationStrategy: services.integrationStrategy,
|
|
693
|
+
};
|
|
694
|
+
|
|
695
|
+
const doneHandler = createDoneHandler(toolContext, doneDeps);
|
|
696
|
+
const result = await doneHandler(args as {
|
|
697
|
+
status: "completed" | "failed" | "blocked" | "deferred";
|
|
698
|
+
summary?: string;
|
|
699
|
+
details?: Record<string, unknown>;
|
|
700
|
+
task_id?: string;
|
|
701
|
+
});
|
|
702
|
+
|
|
703
|
+
// Handle termination if needed
|
|
704
|
+
if (result.shouldTerminate) {
|
|
705
|
+
setImmediate(async () => {
|
|
706
|
+
try {
|
|
707
|
+
await services.agentManager.terminate(context.agent_id, "completed");
|
|
708
|
+
} catch { /* ignore */ }
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
return result;
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
function createInjectContextBridge(services: MCPBridgeServices): ExtensionHandler {
|
|
717
|
+
return async (_extCtx: ExtensionContext, params: unknown) => {
|
|
718
|
+
const { context, args } = extractContext(params);
|
|
719
|
+
|
|
720
|
+
const handler = createInjectContextHandler(
|
|
721
|
+
{ agentManager: services.agentManager, messageRouter: services.messageRouter },
|
|
722
|
+
context.agent_id
|
|
723
|
+
);
|
|
724
|
+
|
|
725
|
+
return handler(args as {
|
|
726
|
+
target_agent_id: string;
|
|
727
|
+
content: string;
|
|
728
|
+
urgent?: boolean;
|
|
729
|
+
reason?: string;
|
|
730
|
+
});
|
|
731
|
+
};
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
function createWaitForActivityBridge(services: MCPBridgeServices): ExtensionHandler {
|
|
735
|
+
return async (_extCtx: ExtensionContext, params: unknown) => {
|
|
736
|
+
const { context, args } = extractContext(params);
|
|
737
|
+
|
|
738
|
+
if (!services.activityWatcher) {
|
|
739
|
+
throw RPCError.internalError("ActivityWatcher not available");
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
const toolContext = toToolContext(context);
|
|
743
|
+
const handler = createWaitForActivityHandler(toolContext, {
|
|
744
|
+
activityWatcher: services.activityWatcher,
|
|
745
|
+
});
|
|
746
|
+
|
|
747
|
+
return handler(args as {
|
|
748
|
+
event_types?: string[];
|
|
749
|
+
timeout_ms?: number;
|
|
750
|
+
scope?: {
|
|
751
|
+
subtree?: string;
|
|
752
|
+
role?: string;
|
|
753
|
+
target_agent?: string;
|
|
754
|
+
};
|
|
755
|
+
});
|
|
756
|
+
};
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
function createClaimTaskBridge(services: MCPBridgeServices): ExtensionHandler {
|
|
760
|
+
return async (_extCtx: ExtensionContext, params: unknown) => {
|
|
761
|
+
const { context, args } = extractContext(params);
|
|
762
|
+
|
|
763
|
+
if (!services.taskBackend) {
|
|
764
|
+
throw RPCError.internalError("Task backend not available");
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
const toolContext = toToolContext(context);
|
|
768
|
+
const handler = createClaimTaskHandler(toolContext, {
|
|
769
|
+
taskBackend: services.taskBackend,
|
|
770
|
+
});
|
|
771
|
+
|
|
772
|
+
return handler(args as { tags?: string[]; root_tasks_only?: boolean });
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
function createUnclaimTaskBridge(services: MCPBridgeServices): ExtensionHandler {
|
|
777
|
+
return async (_extCtx: ExtensionContext, params: unknown) => {
|
|
778
|
+
const { context, args } = extractContext(params);
|
|
779
|
+
|
|
780
|
+
if (!services.taskBackend) {
|
|
781
|
+
throw RPCError.internalError("Task backend not available");
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
const toolContext = toToolContext(context);
|
|
785
|
+
const handler = createUnclaimTaskHandler(toolContext, {
|
|
786
|
+
taskBackend: services.taskBackend,
|
|
787
|
+
});
|
|
788
|
+
|
|
789
|
+
return handler(args as { task_id: string });
|
|
790
|
+
};
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
function createListClaimableTasksBridge(services: MCPBridgeServices): ExtensionHandler {
|
|
794
|
+
return async (_extCtx: ExtensionContext, params: unknown) => {
|
|
795
|
+
const { context, args } = extractContext(params);
|
|
796
|
+
|
|
797
|
+
if (!services.taskBackend) {
|
|
798
|
+
throw RPCError.internalError("Task backend not available");
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
const toolContext = toToolContext(context);
|
|
802
|
+
const handler = createListClaimableTasksHandler(toolContext, {
|
|
803
|
+
taskBackend: services.taskBackend,
|
|
804
|
+
});
|
|
805
|
+
|
|
806
|
+
return handler(args as { tags?: string[]; root_tasks_only?: boolean; limit?: number });
|
|
807
|
+
};
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
// Peer communication bridges
|
|
811
|
+
|
|
812
|
+
function createSendPeerMessageBridge(services: MCPBridgeServices): ExtensionHandler {
|
|
813
|
+
return async (_extCtx: ExtensionContext, params: unknown) => {
|
|
814
|
+
const { context, args } = extractContext(params);
|
|
815
|
+
|
|
816
|
+
if (!services.peerManager || !services.peerManager.hasTransport()) {
|
|
817
|
+
throw RPCError.internalError("Peer communication not available");
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
await services.peerManager.sendMessage(context.agent_id, args.to as string, {
|
|
821
|
+
type: args.type as string,
|
|
822
|
+
payload: args.payload,
|
|
823
|
+
metadata: args.correlation_id ? { correlationId: args.correlation_id as string } : undefined,
|
|
824
|
+
});
|
|
825
|
+
|
|
826
|
+
return { success: true, timestamp: Date.now() };
|
|
827
|
+
};
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
function createSendPeerRequestBridge(services: MCPBridgeServices): ExtensionHandler {
|
|
831
|
+
return async (_extCtx: ExtensionContext, params: unknown) => {
|
|
832
|
+
const { context, args } = extractContext(params);
|
|
833
|
+
|
|
834
|
+
if (!services.peerManager || !services.peerManager.hasTransport()) {
|
|
835
|
+
throw RPCError.internalError("Peer communication not available");
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
return services.peerManager.sendRequest(context.agent_id, args.to as string, {
|
|
839
|
+
method: args.method as string,
|
|
840
|
+
params: args.params,
|
|
841
|
+
timeout: args.timeout as number | undefined,
|
|
842
|
+
});
|
|
843
|
+
};
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
function createRespondToPeerRequestBridge(services: MCPBridgeServices): ExtensionHandler {
|
|
847
|
+
return async (_extCtx: ExtensionContext, params: unknown) => {
|
|
848
|
+
const { context, args } = extractContext(params);
|
|
849
|
+
|
|
850
|
+
if (!services.peerManager) {
|
|
851
|
+
throw RPCError.internalError("Peer communication not available");
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
services.peerManager.respondToRequest(context.agent_id, args.request_id as string, {
|
|
855
|
+
result: args.result,
|
|
856
|
+
error: args.error as { code: number; message: string; data?: unknown } | undefined,
|
|
857
|
+
});
|
|
858
|
+
|
|
859
|
+
return { success: true };
|
|
860
|
+
};
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
// =============================================================================
|
|
864
|
+
// Dynamic Task Tool Bridge
|
|
865
|
+
// =============================================================================
|
|
866
|
+
|
|
867
|
+
/**
|
|
868
|
+
* Creates a bridge handler for a dynamic task tool from the TaskToolProvider.
|
|
869
|
+
* The handler extracts agent context from params and delegates to the tool's
|
|
870
|
+
* handler. The TaskToolProvider's getContext() is backed by a mutable holder
|
|
871
|
+
* that we update here before each call (safe since Node.js is single-threaded).
|
|
872
|
+
*/
|
|
873
|
+
function createTaskToolBridge(
|
|
874
|
+
tool: import("../../../task/backend/types.js").MCPToolDefinition,
|
|
875
|
+
contextHolder: { agent_id: string },
|
|
876
|
+
): ExtensionHandler {
|
|
877
|
+
return async (_extCtx: ExtensionContext, params: unknown) => {
|
|
878
|
+
const { context, args } = extractContext(params);
|
|
879
|
+
|
|
880
|
+
// Set the calling agent's ID so the tool provider's getContext() returns it
|
|
881
|
+
contextHolder.agent_id = context.agent_id;
|
|
882
|
+
|
|
883
|
+
// The tool handler receives the params directly (or unwrapped from the
|
|
884
|
+
// `params` envelope that the thin-client MCP server wraps them in)
|
|
885
|
+
const toolParams = (args as Record<string, unknown>).params ?? args;
|
|
886
|
+
|
|
887
|
+
// The tool handler now calls backend.syncExternalTransition() internally
|
|
888
|
+
// after successful opentasks transitions, which updates the EventStore
|
|
889
|
+
// and triggers MAP events via onTaskChange.
|
|
890
|
+
return await tool.handler(toolParams);
|
|
891
|
+
};
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
// =============================================================================
|
|
895
|
+
// Registration
|
|
896
|
+
// =============================================================================
|
|
897
|
+
|
|
898
|
+
/**
|
|
899
|
+
* All MCP bridge extension method names.
|
|
900
|
+
*/
|
|
901
|
+
export const MCP_BRIDGE_METHODS = [
|
|
902
|
+
"_macro/mcp/spawn_agent",
|
|
903
|
+
"_macro/mcp/emit_status",
|
|
904
|
+
"_macro/mcp/send_message",
|
|
905
|
+
"_macro/mcp/check_messages",
|
|
906
|
+
"_macro/mcp/query_index",
|
|
907
|
+
"_macro/mcp/get_hierarchy",
|
|
908
|
+
"_macro/mcp/get_agent_summary",
|
|
909
|
+
"_macro/mcp/stop_agent",
|
|
910
|
+
"_macro/mcp/done",
|
|
911
|
+
"_macro/mcp/inject_context",
|
|
912
|
+
"_macro/mcp/wait_for_activity",
|
|
913
|
+
"_macro/mcp/claim_task",
|
|
914
|
+
"_macro/mcp/unclaim_task",
|
|
915
|
+
"_macro/mcp/list_claimable_tasks",
|
|
916
|
+
"_macro/mcp/send_peer_message",
|
|
917
|
+
"_macro/mcp/send_peer_request",
|
|
918
|
+
"_macro/mcp/respond_to_peer_request",
|
|
919
|
+
"_macro/mcp/task_tools_list",
|
|
920
|
+
] as const;
|
|
921
|
+
|
|
922
|
+
/**
|
|
923
|
+
* Register all MCP bridge extension methods with the MAP adapter.
|
|
924
|
+
*
|
|
925
|
+
* These handlers mirror each MCP tool, allowing thin-client MCP subprocesses
|
|
926
|
+
* to execute tool logic on the main server via ephemeral WebSocket calls.
|
|
927
|
+
*/
|
|
928
|
+
export function registerMCPBridgeExtensions(
|
|
929
|
+
adapter: MAPAdapter,
|
|
930
|
+
services: MCPBridgeServices
|
|
931
|
+
): void {
|
|
932
|
+
// Set module-level agent token manager for extractContext() validation
|
|
933
|
+
_agentTokenManager = services.agentTokenManager;
|
|
934
|
+
|
|
935
|
+
const emitMAPEvent = (event: EventNotification) => adapter.emitEvent(event);
|
|
936
|
+
|
|
937
|
+
adapter.registerExtension("_macro/mcp/spawn_agent", createSpawnAgentBridge(services, emitMAPEvent));
|
|
938
|
+
adapter.registerExtension("_macro/mcp/emit_status", createEmitStatusBridge(services, emitMAPEvent));
|
|
939
|
+
adapter.registerExtension("_macro/mcp/send_message", createSendMessageBridge(services));
|
|
940
|
+
adapter.registerExtension("_macro/mcp/check_messages", createCheckMessagesBridge(services));
|
|
941
|
+
adapter.registerExtension("_macro/mcp/query_index", createQueryIndexBridge(services));
|
|
942
|
+
adapter.registerExtension("_macro/mcp/get_hierarchy", createGetHierarchyBridge(services));
|
|
943
|
+
adapter.registerExtension("_macro/mcp/get_agent_summary", createGetAgentSummaryBridge(services));
|
|
944
|
+
adapter.registerExtension("_macro/mcp/stop_agent", createStopAgentBridge(services));
|
|
945
|
+
adapter.registerExtension("_macro/mcp/done", createDoneBridge(services));
|
|
946
|
+
adapter.registerExtension("_macro/mcp/inject_context", createInjectContextBridge(services));
|
|
947
|
+
|
|
948
|
+
if (services.activityWatcher) {
|
|
949
|
+
adapter.registerExtension("_macro/mcp/wait_for_activity", createWaitForActivityBridge(services));
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
if (services.taskBackend) {
|
|
953
|
+
adapter.registerExtension("_macro/mcp/claim_task", createClaimTaskBridge(services));
|
|
954
|
+
adapter.registerExtension("_macro/mcp/unclaim_task", createUnclaimTaskBridge(services));
|
|
955
|
+
adapter.registerExtension("_macro/mcp/list_claimable_tasks", createListClaimableTasksBridge(services));
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
if (services.peerManager) {
|
|
959
|
+
adapter.registerExtension("_macro/mcp/send_peer_message", createSendPeerMessageBridge(services));
|
|
960
|
+
adapter.registerExtension("_macro/mcp/send_peer_request", createSendPeerRequestBridge(services));
|
|
961
|
+
adapter.registerExtension("_macro/mcp/respond_to_peer_request", createRespondToPeerRequestBridge(services));
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
// Register dynamic task tool bridges from the TaskToolProvider
|
|
965
|
+
if (services.taskToolProvider) {
|
|
966
|
+
const contextHolder = services.taskToolContext ?? { agent_id: "" };
|
|
967
|
+
const tools = services.taskToolProvider.getTools();
|
|
968
|
+
for (const tool of tools) {
|
|
969
|
+
const method = `_macro/mcp/task_tool/${tool.name}`;
|
|
970
|
+
adapter.registerExtension(method, createTaskToolBridge(tool, contextHolder));
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
// Discovery endpoint: returns list of available task tool names so
|
|
974
|
+
// thin-client MCP subprocesses only register tools the server supports
|
|
975
|
+
adapter.registerExtension("_macro/mcp/task_tools_list", async () => {
|
|
976
|
+
return { tools: tools.map((t) => ({ name: t.name, description: t.description })) };
|
|
977
|
+
});
|
|
978
|
+
} else {
|
|
979
|
+
// No task tools available — return empty list
|
|
980
|
+
adapter.registerExtension("_macro/mcp/task_tools_list", async () => {
|
|
981
|
+
return { tools: [] };
|
|
982
|
+
});
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
/**
|
|
987
|
+
* Unregister all MCP bridge extension methods.
|
|
988
|
+
*/
|
|
989
|
+
export function unregisterMCPBridgeExtensions(adapter: MAPAdapter): void {
|
|
990
|
+
for (const method of MCP_BRIDGE_METHODS) {
|
|
991
|
+
try {
|
|
992
|
+
adapter.unregisterExtension(method);
|
|
993
|
+
} catch { /* ignore */ }
|
|
994
|
+
}
|
|
995
|
+
}
|