context-gatekeeper 0.3.0

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.
Files changed (305) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +774 -0
  3. package/README.zh.md +765 -0
  4. package/bin/context-gatekeeper-cli.js +60 -0
  5. package/dist/api/gdpr.d.ts +104 -0
  6. package/dist/api/gdpr.d.ts.map +1 -0
  7. package/dist/api/gdpr.js +229 -0
  8. package/dist/api/gdpr.js.map +1 -0
  9. package/dist/api/health-check.d.ts +13 -0
  10. package/dist/api/health-check.d.ts.map +1 -0
  11. package/dist/api/health-check.js +2 -0
  12. package/dist/api/health-check.js.map +1 -0
  13. package/dist/api/index.d.ts +7 -0
  14. package/dist/api/index.d.ts.map +1 -0
  15. package/dist/api/index.js +8 -0
  16. package/dist/api/index.js.map +1 -0
  17. package/dist/api/observability.d.ts +39 -0
  18. package/dist/api/observability.d.ts.map +1 -0
  19. package/dist/api/observability.js +132 -0
  20. package/dist/api/observability.js.map +1 -0
  21. package/dist/api/session-manager.d.ts +41 -0
  22. package/dist/api/session-manager.d.ts.map +1 -0
  23. package/dist/api/session-manager.js +129 -0
  24. package/dist/api/session-manager.js.map +1 -0
  25. package/dist/index.d.ts +2 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +7 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/mcp/server.d.ts +8 -0
  30. package/dist/mcp/server.d.ts.map +1 -0
  31. package/dist/mcp/server.js +613 -0
  32. package/dist/mcp/server.js.map +1 -0
  33. package/dist/mcp/tools/configure-llm.d.ts +26 -0
  34. package/dist/mcp/tools/configure-llm.d.ts.map +1 -0
  35. package/dist/mcp/tools/configure-llm.js +32 -0
  36. package/dist/mcp/tools/configure-llm.js.map +1 -0
  37. package/dist/mcp/tools/context-compress.d.ts +15 -0
  38. package/dist/mcp/tools/context-compress.d.ts.map +1 -0
  39. package/dist/mcp/tools/context-compress.js +15 -0
  40. package/dist/mcp/tools/context-compress.js.map +1 -0
  41. package/dist/mcp/tools/dual-mode-execute.d.ts +78 -0
  42. package/dist/mcp/tools/dual-mode-execute.d.ts.map +1 -0
  43. package/dist/mcp/tools/dual-mode-execute.js +299 -0
  44. package/dist/mcp/tools/dual-mode-execute.js.map +1 -0
  45. package/dist/mcp/tools/index.d.ts +19 -0
  46. package/dist/mcp/tools/index.d.ts.map +1 -0
  47. package/dist/mcp/tools/index.js +20 -0
  48. package/dist/mcp/tools/index.js.map +1 -0
  49. package/dist/mcp/tools/intelligent-recall.d.ts +67 -0
  50. package/dist/mcp/tools/intelligent-recall.d.ts.map +1 -0
  51. package/dist/mcp/tools/intelligent-recall.js +208 -0
  52. package/dist/mcp/tools/intelligent-recall.js.map +1 -0
  53. package/dist/mcp/tools/memory-anchor.d.ts +13 -0
  54. package/dist/mcp/tools/memory-anchor.d.ts.map +1 -0
  55. package/dist/mcp/tools/memory-anchor.js +16 -0
  56. package/dist/mcp/tools/memory-anchor.js.map +1 -0
  57. package/dist/mcp/tools/memory-delete-batch.d.ts +16 -0
  58. package/dist/mcp/tools/memory-delete-batch.d.ts.map +1 -0
  59. package/dist/mcp/tools/memory-delete-batch.js +26 -0
  60. package/dist/mcp/tools/memory-delete-batch.js.map +1 -0
  61. package/dist/mcp/tools/memory-extract.d.ts +68 -0
  62. package/dist/mcp/tools/memory-extract.d.ts.map +1 -0
  63. package/dist/mcp/tools/memory-extract.js +280 -0
  64. package/dist/mcp/tools/memory-extract.js.map +1 -0
  65. package/dist/mcp/tools/memory-recall.d.ts +42 -0
  66. package/dist/mcp/tools/memory-recall.d.ts.map +1 -0
  67. package/dist/mcp/tools/memory-recall.js +37 -0
  68. package/dist/mcp/tools/memory-recall.js.map +1 -0
  69. package/dist/mcp/tools/memory-report-usage.d.ts +17 -0
  70. package/dist/mcp/tools/memory-report-usage.d.ts.map +1 -0
  71. package/dist/mcp/tools/memory-report-usage.js +15 -0
  72. package/dist/mcp/tools/memory-report-usage.js.map +1 -0
  73. package/dist/mcp/tools/memory-search.d.ts +43 -0
  74. package/dist/mcp/tools/memory-search.d.ts.map +1 -0
  75. package/dist/mcp/tools/memory-search.js +38 -0
  76. package/dist/mcp/tools/memory-search.js.map +1 -0
  77. package/dist/mcp/tools/memory-session.d.ts +118 -0
  78. package/dist/mcp/tools/memory-session.d.ts.map +1 -0
  79. package/dist/mcp/tools/memory-session.js +113 -0
  80. package/dist/mcp/tools/memory-session.js.map +1 -0
  81. package/dist/mcp/tools/memory-stats.d.ts +10 -0
  82. package/dist/mcp/tools/memory-stats.d.ts.map +1 -0
  83. package/dist/mcp/tools/memory-stats.js +35 -0
  84. package/dist/mcp/tools/memory-stats.js.map +1 -0
  85. package/dist/mcp/tools/memory-store-batch.d.ts +49 -0
  86. package/dist/mcp/tools/memory-store-batch.d.ts.map +1 -0
  87. package/dist/mcp/tools/memory-store-batch.js +48 -0
  88. package/dist/mcp/tools/memory-store-batch.js.map +1 -0
  89. package/dist/mcp/tools/memory-store.d.ts +37 -0
  90. package/dist/mcp/tools/memory-store.d.ts.map +1 -0
  91. package/dist/mcp/tools/memory-store.js +34 -0
  92. package/dist/mcp/tools/memory-store.js.map +1 -0
  93. package/dist/mcp/tools/project-create.d.ts +16 -0
  94. package/dist/mcp/tools/project-create.d.ts.map +1 -0
  95. package/dist/mcp/tools/project-create.js +14 -0
  96. package/dist/mcp/tools/project-create.js.map +1 -0
  97. package/dist/models/types.d.ts +88 -0
  98. package/dist/models/types.d.ts.map +1 -0
  99. package/dist/models/types.js +19 -0
  100. package/dist/models/types.js.map +1 -0
  101. package/dist/schema/compression.d.ts +7 -0
  102. package/dist/schema/compression.d.ts.map +1 -0
  103. package/dist/schema/compression.js +66 -0
  104. package/dist/schema/compression.js.map +1 -0
  105. package/dist/schema/fulltext-search.d.ts +10 -0
  106. package/dist/schema/fulltext-search.d.ts.map +1 -0
  107. package/dist/schema/fulltext-search.js +73 -0
  108. package/dist/schema/fulltext-search.js.map +1 -0
  109. package/dist/schema/index.d.ts +9 -0
  110. package/dist/schema/index.d.ts.map +1 -0
  111. package/dist/schema/index.js +9 -0
  112. package/dist/schema/index.js.map +1 -0
  113. package/dist/schema/knowledge-graph.d.ts +108 -0
  114. package/dist/schema/knowledge-graph.d.ts.map +1 -0
  115. package/dist/schema/knowledge-graph.js +372 -0
  116. package/dist/schema/knowledge-graph.js.map +1 -0
  117. package/dist/schema/memory-session.d.ts +62 -0
  118. package/dist/schema/memory-session.d.ts.map +1 -0
  119. package/dist/schema/memory-session.js +258 -0
  120. package/dist/schema/memory-session.js.map +1 -0
  121. package/dist/schema/memory.d.ts +84 -0
  122. package/dist/schema/memory.d.ts.map +1 -0
  123. package/dist/schema/memory.js +622 -0
  124. package/dist/schema/memory.js.map +1 -0
  125. package/dist/schema/project.d.ts +8 -0
  126. package/dist/schema/project.d.ts.map +1 -0
  127. package/dist/schema/project.js +68 -0
  128. package/dist/schema/project.js.map +1 -0
  129. package/dist/schema/schema-init.d.ts +2 -0
  130. package/dist/schema/schema-init.d.ts.map +1 -0
  131. package/dist/schema/schema-init.js +199 -0
  132. package/dist/schema/schema-init.js.map +1 -0
  133. package/dist/schema/vector-index.d.ts +28 -0
  134. package/dist/schema/vector-index.d.ts.map +1 -0
  135. package/dist/schema/vector-index.js +179 -0
  136. package/dist/schema/vector-index.js.map +1 -0
  137. package/dist/scripts/agents/base.d.ts +89 -0
  138. package/dist/scripts/agents/base.d.ts.map +1 -0
  139. package/dist/scripts/agents/base.js +148 -0
  140. package/dist/scripts/agents/base.js.map +1 -0
  141. package/dist/scripts/agents/base.ts +193 -0
  142. package/dist/scripts/agents/claude-code.d.ts +21 -0
  143. package/dist/scripts/agents/claude-code.d.ts.map +1 -0
  144. package/dist/scripts/agents/claude-code.js +33 -0
  145. package/dist/scripts/agents/claude-code.js.map +1 -0
  146. package/dist/scripts/agents/claude-code.ts +36 -0
  147. package/dist/scripts/agents/claude-desktop.d.ts +25 -0
  148. package/dist/scripts/agents/claude-desktop.d.ts.map +1 -0
  149. package/dist/scripts/agents/claude-desktop.js +36 -0
  150. package/dist/scripts/agents/claude-desktop.js.map +1 -0
  151. package/dist/scripts/agents/claude-desktop.ts +39 -0
  152. package/dist/scripts/agents/cline.d.ts +22 -0
  153. package/dist/scripts/agents/cline.d.ts.map +1 -0
  154. package/dist/scripts/agents/cline.js +35 -0
  155. package/dist/scripts/agents/cline.js.map +1 -0
  156. package/dist/scripts/agents/cline.ts +38 -0
  157. package/dist/scripts/agents/continue.d.ts +20 -0
  158. package/dist/scripts/agents/continue.d.ts.map +1 -0
  159. package/dist/scripts/agents/continue.js +35 -0
  160. package/dist/scripts/agents/continue.js.map +1 -0
  161. package/dist/scripts/agents/continue.ts +38 -0
  162. package/dist/scripts/agents/cursor.d.ts +27 -0
  163. package/dist/scripts/agents/cursor.d.ts.map +1 -0
  164. package/dist/scripts/agents/cursor.js +38 -0
  165. package/dist/scripts/agents/cursor.js.map +1 -0
  166. package/dist/scripts/agents/cursor.ts +41 -0
  167. package/dist/scripts/cli/config-gen.d.ts +59 -0
  168. package/dist/scripts/cli/config-gen.d.ts.map +1 -0
  169. package/dist/scripts/cli/config-gen.js +156 -0
  170. package/dist/scripts/cli/config-gen.js.map +1 -0
  171. package/dist/scripts/cli/config-gen.ts +164 -0
  172. package/dist/scripts/cli/detect.d.ts +42 -0
  173. package/dist/scripts/cli/detect.d.ts.map +1 -0
  174. package/dist/scripts/cli/detect.js +131 -0
  175. package/dist/scripts/cli/detect.js.map +1 -0
  176. package/dist/scripts/cli/detect.ts +162 -0
  177. package/dist/scripts/cli/install.d.ts +31 -0
  178. package/dist/scripts/cli/install.d.ts.map +1 -0
  179. package/dist/scripts/cli/install.js +125 -0
  180. package/dist/scripts/cli/install.js.map +1 -0
  181. package/dist/scripts/cli/install.ts +157 -0
  182. package/dist/scripts/cli/status.d.ts +8 -0
  183. package/dist/scripts/cli/status.d.ts.map +1 -0
  184. package/dist/scripts/cli/status.js +39 -0
  185. package/dist/scripts/cli/status.js.map +1 -0
  186. package/dist/scripts/cli/status.ts +48 -0
  187. package/dist/scripts/cli/uninstall.d.ts +22 -0
  188. package/dist/scripts/cli/uninstall.d.ts.map +1 -0
  189. package/dist/scripts/cli/uninstall.js +141 -0
  190. package/dist/scripts/cli/uninstall.js.map +1 -0
  191. package/dist/scripts/cli/uninstall.ts +157 -0
  192. package/dist/scripts/cli.d.ts +23 -0
  193. package/dist/scripts/cli.d.ts.map +1 -0
  194. package/dist/scripts/cli.js +166 -0
  195. package/dist/scripts/cli.js.map +1 -0
  196. package/dist/scripts/cli.ts +173 -0
  197. package/dist/services/classifier/index.d.ts +36 -0
  198. package/dist/services/classifier/index.d.ts.map +1 -0
  199. package/dist/services/classifier/index.js +104 -0
  200. package/dist/services/classifier/index.js.map +1 -0
  201. package/dist/services/classifier/llm.d.ts +37 -0
  202. package/dist/services/classifier/llm.d.ts.map +1 -0
  203. package/dist/services/classifier/llm.js +119 -0
  204. package/dist/services/classifier/llm.js.map +1 -0
  205. package/dist/services/classifier/rules.d.ts +22 -0
  206. package/dist/services/classifier/rules.d.ts.map +1 -0
  207. package/dist/services/classifier/rules.js +98 -0
  208. package/dist/services/classifier/rules.js.map +1 -0
  209. package/dist/services/compressor/index.d.ts +3 -0
  210. package/dist/services/compressor/index.d.ts.map +1 -0
  211. package/dist/services/compressor/index.js +3 -0
  212. package/dist/services/compressor/index.js.map +1 -0
  213. package/dist/services/compressor/threshold.d.ts +35 -0
  214. package/dist/services/compressor/threshold.d.ts.map +1 -0
  215. package/dist/services/compressor/threshold.js +60 -0
  216. package/dist/services/compressor/threshold.js.map +1 -0
  217. package/dist/services/compressor/trigger.d.ts +24 -0
  218. package/dist/services/compressor/trigger.d.ts.map +1 -0
  219. package/dist/services/compressor/trigger.js +91 -0
  220. package/dist/services/compressor/trigger.js.map +1 -0
  221. package/dist/services/constraint-extractor.d.ts +25 -0
  222. package/dist/services/constraint-extractor.d.ts.map +1 -0
  223. package/dist/services/constraint-extractor.js +97 -0
  224. package/dist/services/constraint-extractor.js.map +1 -0
  225. package/dist/services/database-health.d.ts +22 -0
  226. package/dist/services/database-health.d.ts.map +1 -0
  227. package/dist/services/database-health.js +122 -0
  228. package/dist/services/database-health.js.map +1 -0
  229. package/dist/services/embedding-fixed.d.ts +9 -0
  230. package/dist/services/embedding-fixed.d.ts.map +1 -0
  231. package/dist/services/embedding-fixed.js +70 -0
  232. package/dist/services/embedding-fixed.js.map +1 -0
  233. package/dist/services/embedding-provider.d.ts +79 -0
  234. package/dist/services/embedding-provider.d.ts.map +1 -0
  235. package/dist/services/embedding-provider.js +229 -0
  236. package/dist/services/embedding-provider.js.map +1 -0
  237. package/dist/services/embedding.d.ts +17 -0
  238. package/dist/services/embedding.d.ts.map +1 -0
  239. package/dist/services/embedding.js +99 -0
  240. package/dist/services/embedding.js.map +1 -0
  241. package/dist/services/hnsw-index.d.ts +76 -0
  242. package/dist/services/hnsw-index.d.ts.map +1 -0
  243. package/dist/services/hnsw-index.js +301 -0
  244. package/dist/services/hnsw-index.js.map +1 -0
  245. package/dist/services/llm.d.ts +39 -0
  246. package/dist/services/llm.d.ts.map +1 -0
  247. package/dist/services/llm.js +207 -0
  248. package/dist/services/llm.js.map +1 -0
  249. package/dist/services/memory-tiers.d.ts +75 -0
  250. package/dist/services/memory-tiers.d.ts.map +1 -0
  251. package/dist/services/memory-tiers.js +275 -0
  252. package/dist/services/memory-tiers.js.map +1 -0
  253. package/dist/services/memory.d.ts +33 -0
  254. package/dist/services/memory.d.ts.map +1 -0
  255. package/dist/services/memory.js +209 -0
  256. package/dist/services/memory.js.map +1 -0
  257. package/dist/services/multi-agent-sharing.d.ts +83 -0
  258. package/dist/services/multi-agent-sharing.d.ts.map +1 -0
  259. package/dist/services/multi-agent-sharing.js +278 -0
  260. package/dist/services/multi-agent-sharing.js.map +1 -0
  261. package/dist/services/reranker.d.ts +88 -0
  262. package/dist/services/reranker.d.ts.map +1 -0
  263. package/dist/services/reranker.js +234 -0
  264. package/dist/services/reranker.js.map +1 -0
  265. package/dist/services/triple-extractor.d.ts +35 -0
  266. package/dist/services/triple-extractor.d.ts.map +1 -0
  267. package/dist/services/triple-extractor.js +293 -0
  268. package/dist/services/triple-extractor.js.map +1 -0
  269. package/dist/services/vector-provider.d.ts +40 -0
  270. package/dist/services/vector-provider.d.ts.map +1 -0
  271. package/dist/services/vector-provider.js +225 -0
  272. package/dist/services/vector-provider.js.map +1 -0
  273. package/dist/utils/after-chain-executor.d.ts +26 -0
  274. package/dist/utils/after-chain-executor.d.ts.map +1 -0
  275. package/dist/utils/after-chain-executor.js +135 -0
  276. package/dist/utils/after-chain-executor.js.map +1 -0
  277. package/dist/utils/after-chain.d.ts +94 -0
  278. package/dist/utils/after-chain.d.ts.map +1 -0
  279. package/dist/utils/after-chain.js +155 -0
  280. package/dist/utils/after-chain.js.map +1 -0
  281. package/dist/utils/db.d.ts +29 -0
  282. package/dist/utils/db.d.ts.map +1 -0
  283. package/dist/utils/db.js +201 -0
  284. package/dist/utils/db.js.map +1 -0
  285. package/dist/utils/encryption.d.ts +87 -0
  286. package/dist/utils/encryption.d.ts.map +1 -0
  287. package/dist/utils/encryption.js +175 -0
  288. package/dist/utils/encryption.js.map +1 -0
  289. package/dist/utils/errors.d.ts +35 -0
  290. package/dist/utils/errors.d.ts.map +1 -0
  291. package/dist/utils/errors.js +56 -0
  292. package/dist/utils/errors.js.map +1 -0
  293. package/dist/utils/logger.d.ts +27 -0
  294. package/dist/utils/logger.d.ts.map +1 -0
  295. package/dist/utils/logger.js +177 -0
  296. package/dist/utils/logger.js.map +1 -0
  297. package/dist/utils/priority.d.ts +26 -0
  298. package/dist/utils/priority.d.ts.map +1 -0
  299. package/dist/utils/priority.js +43 -0
  300. package/dist/utils/priority.js.map +1 -0
  301. package/dist/utils/watchdog.d.ts +57 -0
  302. package/dist/utils/watchdog.d.ts.map +1 -0
  303. package/dist/utils/watchdog.js +164 -0
  304. package/dist/utils/watchdog.js.map +1 -0
  305. package/package.json +78 -0
@@ -0,0 +1,613 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
+ import { z } from 'zod/v4';
4
+ import { logger } from '../utils/logger.js';
5
+ import { initDatabase } from '../utils/db.js';
6
+ import { executeAfterChain, recordConversationTurn } from '../utils/after-chain-executor.js';
7
+ import { getAfterChainRegistry } from '../utils/after-chain.js';
8
+ import { initSchema } from '../schema/index.js';
9
+ import { getWatchdogTokenManager, checkPermission, READ_ONLY_TOOLS, READ_WRITE_TOOLS, WatchdogTokenManager, TOOL_PERMISSIONS, } from '../utils/watchdog.js';
10
+ import { memoryStoreTool, memoryRecallTool, memoryAnchorTool, memoryReportUsageTool, contextCompressTool, projectCreateTool, memoryStoreBatchTool, memoryDeleteBatchTool, memorySearchTool, configureLLMTool, memoryStatsTool, memoryExtractTool, intelligentRecallTool, dualModeExecuteTool } from './tools/index.js';
11
+ const mcpServer = new McpServer({
12
+ name: 'context-gatekeeper',
13
+ version: '0.2.1'
14
+ });
15
+ // Initialize database asynchronously
16
+ async function initialize() {
17
+ try {
18
+ await initDatabase();
19
+ initSchema();
20
+ }
21
+ catch (error) {
22
+ logger.error('Failed to initialize database', { error });
23
+ throw error;
24
+ }
25
+ }
26
+ /**
27
+ * Main entry point - start the MCP server
28
+ */
29
+ mcpServer.registerTool('memory_store', {
30
+ description: 'Store a new memory with automatic embedding and deduplication',
31
+ inputSchema: {
32
+ content: z.string().min(1).describe('The memory content'),
33
+ priority: z.enum(['anchored', 'constraint', 'decision', 'preference', 'fact']).describe('Priority level'),
34
+ project_tags: z.array(z.string()).optional().describe('Project tags for filtering'),
35
+ anchored: z.boolean().optional().describe('Whether this memory is anchored (permanent)'),
36
+ expires_in_hours: z.number().positive().optional().describe('TTL in hours'),
37
+ updated_by: z.string().optional().describe('Source that updated this memory'),
38
+ user_id: z.string().optional().default('default').describe('User ID for storage isolation'),
39
+ agent_id: z.string().optional().default('default').describe('Agent ID for storage isolation'),
40
+ project_id: z.string().optional().default('default').describe('Project ID for storage isolation'),
41
+ token: z.string().optional().describe('Watchdog token for write operations')
42
+ }
43
+ }, async ({ content, priority, project_tags, anchored, expires_in_hours, updated_by, user_id, agent_id, project_id, token }) => {
44
+ const start = performance.now();
45
+ try {
46
+ const permission = checkPermission('memory_store', token || '');
47
+ if (!permission.allowed) {
48
+ const latencyMs = Math.round(performance.now() - start);
49
+ logger.warn('Tool permission denied', { tool: 'memory_store', latencyMs, reason: permission.reason });
50
+ return { content: [{ type: 'text', text: JSON.stringify({ error: 'Permission denied', reason: permission.reason }) }] };
51
+ }
52
+ const result = await memoryStoreTool({ content, priority, project_tags, anchored, expires_in_hours, updated_by, user_id, agent_id, project_id });
53
+ executeAfterChain('memory_store', { content, priority }, result).catch(() => { });
54
+ const latencyMs = Math.round(performance.now() - start);
55
+ logger.info('Tool completed', { tool: 'memory_store', latencyMs, success: true });
56
+ return { content: [{ type: 'text', text: JSON.stringify(result) }] };
57
+ }
58
+ catch (error) {
59
+ const latencyMs = Math.round(performance.now() - start);
60
+ logger.error('Tool failed', { tool: 'memory_store', latencyMs, success: false, error });
61
+ throw error;
62
+ }
63
+ });
64
+ // Tool: memory_recall
65
+ mcpServer.registerTool('memory_recall', {
66
+ description: 'Recall memories with automatic search mode detection',
67
+ inputSchema: {
68
+ query: z.string().describe('Search query for memories'),
69
+ project_tags: z.array(z.string()).optional().describe('Filter by project tags'),
70
+ limit: z.number().int().positive().optional().default(10).describe('Maximum results'),
71
+ search_mode: z.enum(['keyword', 'semantic', 'hybrid', 'auto']).optional().default('auto').describe('Search mode'),
72
+ user_id: z.string().optional().default('default').describe('User ID for storage isolation'),
73
+ agent_id: z.string().optional().default('default').describe('Agent ID for storage isolation'),
74
+ project_id: z.string().optional().default('default').describe('Project ID for storage isolation')
75
+ }
76
+ }, async ({ query, project_tags, limit, search_mode, user_id, agent_id, project_id }) => {
77
+ const result = await memoryRecallTool({ query, project_tags, limit, search_mode, user_id, agent_id, project_id });
78
+ return { content: [{ type: 'text', text: JSON.stringify(result) }] };
79
+ });
80
+ // Tool: memory_anchor
81
+ mcpServer.registerTool('memory_anchor', {
82
+ description: 'Anchor a memory to make it permanent (never compressed)',
83
+ inputSchema: {
84
+ memory_id: z.string().uuid().describe('The memory ID to anchor'),
85
+ token: z.string().optional().describe('Watchdog token for write operations')
86
+ }
87
+ }, async ({ memory_id, token }) => {
88
+ const permission = checkPermission('memory_anchor', token || '');
89
+ if (!permission.allowed) {
90
+ return { content: [{ type: 'text', text: JSON.stringify({ error: 'Permission denied', reason: permission.reason }) }] };
91
+ }
92
+ const result = await memoryAnchorTool({ memory_id });
93
+ return { content: [{ type: 'text', text: JSON.stringify(result) }] };
94
+ });
95
+ // Tool: memory_report_usage
96
+ mcpServer.registerTool('memory_report_usage', {
97
+ description: 'Report token usage and get compression recommendation',
98
+ inputSchema: {
99
+ used_tokens: z.number().min(0).describe('Current token usage'),
100
+ max_tokens: z.number().min(1).describe('Maximum token limit')
101
+ }
102
+ }, async ({ used_tokens, max_tokens }) => {
103
+ const result = await memoryReportUsageTool({ used_tokens, max_tokens });
104
+ return { content: [{ type: 'text', text: JSON.stringify(result) }] };
105
+ });
106
+ // Tool: context_compress
107
+ mcpServer.registerTool('context_compress', {
108
+ description: 'Compress low-priority memories to reduce context size',
109
+ inputSchema: {
110
+ target_ratio: z.number().min(0).max(1).optional().describe('Target compression ratio (0-1)'),
111
+ token: z.string().optional().describe('Watchdog token for write operations')
112
+ }
113
+ }, async ({ target_ratio, token }) => {
114
+ const permission = checkPermission('context_compress', token || '');
115
+ if (!permission.allowed) {
116
+ return { content: [{ type: 'text', text: JSON.stringify({ error: 'Permission denied', reason: permission.reason }) }] };
117
+ }
118
+ const result = await contextCompressTool({ target_ratio });
119
+ return { content: [{ type: 'text', text: JSON.stringify(result) }] };
120
+ });
121
+ // Tool: project_create
122
+ mcpServer.registerTool('project_create', {
123
+ description: 'Create a new project for organizing memories',
124
+ inputSchema: {
125
+ name: z.string().min(1).describe('Project name'),
126
+ root_path: z.string().optional().describe('Root path of the project'),
127
+ token: z.string().optional().describe('Watchdog token for write operations')
128
+ }
129
+ }, async ({ name, root_path, token }) => {
130
+ const permission = checkPermission('project_create', token || '');
131
+ if (!permission.allowed) {
132
+ return { content: [{ type: 'text', text: JSON.stringify({ error: 'Permission denied', reason: permission.reason }) }] };
133
+ }
134
+ const result = await projectCreateTool({ name, root_path });
135
+ return { content: [{ type: 'text', text: JSON.stringify(result) }] };
136
+ });
137
+ // Tool: memory_store_batch
138
+ mcpServer.registerTool('memory_store_batch', {
139
+ description: 'Store multiple memories in one call (up to 100)',
140
+ inputSchema: {
141
+ memories: z.array(z.object({
142
+ content: z.string().min(1).describe('Memory content'),
143
+ priority: z.enum(['anchored', 'constraint', 'decision', 'preference', 'fact']).describe('Priority'),
144
+ project_tags: z.array(z.string()).optional().describe('Project tags'),
145
+ anchored: z.boolean().optional().describe('Anchored'),
146
+ expires_in_hours: z.number().positive().optional().describe('TTL in hours')
147
+ })).min(1).max(100).describe('Array of memories to store'),
148
+ token: z.string().optional().describe('Watchdog token for write operations')
149
+ }
150
+ }, async ({ memories, token }) => {
151
+ const permission = checkPermission('memory_store_batch', token || '');
152
+ if (!permission.allowed) {
153
+ return { content: [{ type: 'text', text: JSON.stringify({ error: 'Permission denied', reason: permission.reason }) }] };
154
+ }
155
+ const result = await memoryStoreBatchTool({ memories });
156
+ executeAfterChain('memory_store_batch', { count: memories.length }, result).catch(() => { });
157
+ return { content: [{ type: 'text', text: JSON.stringify(result) }] };
158
+ });
159
+ // Tool: memory_delete_batch
160
+ mcpServer.registerTool('memory_delete_batch', {
161
+ description: 'Delete multiple memories in one call',
162
+ inputSchema: {
163
+ memory_ids: z.array(z.string().uuid()).min(1).max(100).describe('Memory IDs to delete'),
164
+ token: z.string().optional().describe('Watchdog token for write operations')
165
+ }
166
+ }, async ({ memory_ids, token }) => {
167
+ const permission = checkPermission('memory_delete_batch', token || '');
168
+ if (!permission.allowed) {
169
+ return { content: [{ type: 'text', text: JSON.stringify({ error: 'Permission denied', reason: permission.reason }) }] };
170
+ }
171
+ const result = await memoryDeleteBatchTool({ memory_ids });
172
+ return { content: [{ type: 'text', text: JSON.stringify(result) }] };
173
+ });
174
+ // Tool: memory_search
175
+ mcpServer.registerTool('memory_search', {
176
+ description: 'Search memories with explicit search mode control',
177
+ inputSchema: {
178
+ query: z.string().describe('Search query'),
179
+ project_tags: z.array(z.string()).optional().describe('Filter by project tags'),
180
+ limit: z.number().int().positive().optional().default(10).describe('Max results'),
181
+ search_mode: z.enum(['keyword', 'semantic', 'hybrid', 'auto']).optional().default('auto').describe('Search mode'),
182
+ user_id: z.string().optional().default('default').describe('User ID for storage isolation'),
183
+ agent_id: z.string().optional().default('default').describe('Agent ID for storage isolation'),
184
+ project_id: z.string().optional().default('default').describe('Project ID for storage isolation')
185
+ }
186
+ }, async ({ query, project_tags, limit, search_mode, user_id, agent_id, project_id }) => {
187
+ const result = await memorySearchTool({ query, project_tags, limit, search_mode, user_id, agent_id, project_id });
188
+ return { content: [{ type: 'text', text: JSON.stringify(result) }] };
189
+ });
190
+ // Tool: configure_llm
191
+ mcpServer.registerTool('configure_llm', {
192
+ description: 'Configure LLM provider for summarization (OpenAI/Ollama/Anthropic)',
193
+ inputSchema: {
194
+ provider: z.enum(['openai', 'ollama', 'anthropic', 'none']).optional().describe('LLM provider'),
195
+ api_key: z.string().optional().describe('API key'),
196
+ base_url: z.string().optional().describe('Base URL for API'),
197
+ model: z.string().optional().describe('Model name'),
198
+ token: z.string().optional().describe('Watchdog token for write operations')
199
+ }
200
+ }, async ({ provider, api_key, base_url, model, token }) => {
201
+ const permission = checkPermission('configure_llm', token || '');
202
+ if (!permission.allowed) {
203
+ return { content: [{ type: 'text', text: JSON.stringify({ error: 'Permission denied', reason: permission.reason }) }] };
204
+ }
205
+ const result = await configureLLMTool({ provider, api_key, base_url, model });
206
+ return { content: [{ type: 'text', text: JSON.stringify(result) }] };
207
+ });
208
+ // Tool: memory_stats
209
+ mcpServer.registerTool('memory_stats', {
210
+ description: 'Get memory statistics and cleanup expired entries',
211
+ inputSchema: {}
212
+ }, async () => {
213
+ const result = await memoryStatsTool();
214
+ return { content: [{ type: 'text', text: JSON.stringify(result) }] };
215
+ });
216
+ // ============ Phase 1: AutoSkill-style Constraint Extraction ============
217
+ // Tool: memory_extract
218
+ mcpServer.registerTool('memory_extract', {
219
+ description: 'Extract durable constraints from conversation turns (AutoSkill-style). Analyzes user turns to identify persistent preferences, constraints, and workflows. Only processes user messages, ignores one-shot requests.',
220
+ inputSchema: {
221
+ conversation_turns: z.array(z.object({
222
+ role: z.enum(['user', 'assistant']),
223
+ content: z.string()
224
+ })).min(1).describe('Conversation turns to analyze'),
225
+ project_tags: z.array(z.string()).optional().describe('Project tags for extracted constraints'),
226
+ extract_mode: z.enum(['all', 'constraints_only', 'preferences_only']).optional()
227
+ .default('all').describe('Extraction mode'),
228
+ min_confidence: z.number().min(0).max(1).optional().default(0.5)
229
+ .describe('Minimum confidence threshold'),
230
+ use_llm: z.boolean().optional().default(true)
231
+ .describe('Use LLM extraction when available (fallback to keywords if unavailable)')
232
+ }
233
+ }, async ({ conversation_turns, project_tags, extract_mode, min_confidence, use_llm }) => {
234
+ // 记录对话轮次,供 After-Chain 使用
235
+ for (const turn of conversation_turns) {
236
+ recordConversationTurn(turn.role, turn.content);
237
+ }
238
+ const result = await memoryExtractTool({
239
+ conversation_turns,
240
+ project_tags,
241
+ extract_mode,
242
+ min_confidence,
243
+ use_llm,
244
+ });
245
+ return { content: [{ type: 'text', text: JSON.stringify(result) }] };
246
+ });
247
+ // ============ Phase 2: MemGate-style Intelligent Recall ============
248
+ // Tool: intelligent_recall
249
+ mcpServer.registerTool('intelligent_recall', {
250
+ description: 'Recall memories with MemGate-style relevance scoring. Combines semantic similarity with learned relevance patterns, supports soft guidance injection and hard admissibility checking.',
251
+ inputSchema: {
252
+ query: z.string().describe('Current query or context'),
253
+ conversation_context: z.string().optional().describe('Extended conversation context'),
254
+ project_tags: z.array(z.string()).optional().describe('Project tags for filtering'),
255
+ limit: z.number().int().positive().optional().default(10).describe('Maximum results'),
256
+ relevance_threshold: z.number().min(0).max(1).optional().default(0.07)
257
+ .describe('MemGate-style relevance threshold (default 0.07)'),
258
+ return_mode: z.enum(['all', 'constraints_only', 'high_relevance_only']).optional()
259
+ .default('all').describe('Return mode'),
260
+ enable_soft_guidance: z.boolean().optional().default(true)
261
+ .describe('Enable soft guidance (memory injection context)'),
262
+ enable_hard_check: z.boolean().optional().default(false)
263
+ .describe('Enable hard admissibility check')
264
+ }
265
+ }, async ({ query, conversation_context, project_tags, limit, relevance_threshold, return_mode, enable_soft_guidance, enable_hard_check }) => {
266
+ const result = await intelligentRecallTool({
267
+ query,
268
+ conversation_context,
269
+ project_tags,
270
+ limit,
271
+ relevance_threshold,
272
+ return_mode,
273
+ enable_soft_guidance,
274
+ enable_hard_check
275
+ });
276
+ return { content: [{ type: 'text', text: JSON.stringify(result) }] };
277
+ });
278
+ // ============ Phase 3: MPR-style Dual-Mode Execution ============
279
+ // Tool: dual_mode_execute
280
+ mcpServer.registerTool('dual_mode_execute', {
281
+ description: 'Dual-mode execution combining AutoSkill soft guidance with MPR hard admissibility. Validates proposed actions against stored constraints, injects relevant context, and returns decisions with suggestions.',
282
+ inputSchema: {
283
+ action: z.string().describe('Proposed action to validate'),
284
+ context: z.string().describe('Current execution context'),
285
+ project_tags: z.array(z.string()).optional().describe('Project tags for constraint lookup'),
286
+ mode: z.enum(['soft_only', 'hard_only', 'dual']).optional().default('dual')
287
+ .describe('Execution mode: soft (guidance), hard (check), or dual (both)'),
288
+ soft_guidance_style: z.enum(['concise', 'detailed', 'minimal']).optional().default('concise')
289
+ .describe('How much guidance to inject'),
290
+ hard_threshold: z.number().min(0).max(1).optional().default(0.5)
291
+ .describe('Threshold for hard admissibility')
292
+ }
293
+ }, async ({ action, context, project_tags, mode, soft_guidance_style, hard_threshold }) => {
294
+ const result = await dualModeExecuteTool({
295
+ action,
296
+ context,
297
+ project_tags,
298
+ mode,
299
+ soft_guidance_style,
300
+ hard_threshold
301
+ });
302
+ return { content: [{ type: 'text', text: JSON.stringify(result) }] };
303
+ });
304
+ // ============ Infrastructure: Database Management ============
305
+ // Tool: db_flush
306
+ mcpServer.registerTool('db_flush', {
307
+ description: 'Manually flush the in-memory database to disk',
308
+ inputSchema: {
309
+ token: z.string().optional().describe('Watchdog token for write operations')
310
+ }
311
+ }, async ({ token }) => {
312
+ const permission = checkPermission('configure_llm', token || '');
313
+ if (!permission.allowed) {
314
+ return { content: [{ type: 'text', text: JSON.stringify({ error: 'Permission denied', reason: permission.reason }) }] };
315
+ }
316
+ const { flushDatabase, getDbPath } = await import('../utils/db.js');
317
+ const before = process.hrtime.bigint();
318
+ flushDatabase();
319
+ const after = process.hrtime.bigint();
320
+ return { content: [{ type: 'text', text: JSON.stringify({
321
+ success: true,
322
+ db_path: getDbPath(),
323
+ flushed_at: new Date().toISOString(),
324
+ duration_ms: Number(after - before) / 1_000_000
325
+ }) }] };
326
+ });
327
+ // ============ Watchdog Permission Management ============
328
+ // Tool: watchdog_manage
329
+ mcpServer.registerTool('watchdog_manage', {
330
+ description: 'Manage Watchdog tokens, generate new tokens, and check permissions',
331
+ inputSchema: {
332
+ action: z.enum(['generate_token', 'set_token', 'check_permission', 'list_tools', 'get_config']).describe('Action to perform'),
333
+ token_type: z.enum(['read', 'write', 'watchdog']).optional().describe('Token type for generate/set'),
334
+ token: z.string().optional().describe('Token value for set_token, or token to check for check_permission'),
335
+ tool_name: z.string().optional().describe('Tool name for check_permission')
336
+ }
337
+ }, async ({ action, token_type, token, tool_name }) => {
338
+ const manager = getWatchdogTokenManager();
339
+ switch (action) {
340
+ case 'generate_token': {
341
+ if (!token_type) {
342
+ return { content: [{ type: 'text', text: JSON.stringify({ error: 'token_type required for generate_token' }) }] };
343
+ }
344
+ const newToken = WatchdogTokenManager.generateToken();
345
+ manager.setToken(token_type, newToken);
346
+ return { content: [{ type: 'text', text: JSON.stringify({ success: true, token_type, token: newToken, warning: 'Store this token securely - it will not be shown again' }) }] };
347
+ }
348
+ case 'set_token': {
349
+ if (!token_type || !token) {
350
+ return { content: [{ type: 'text', text: JSON.stringify({ error: 'token_type and token required for set_token' }) }] };
351
+ }
352
+ manager.setToken(token_type, token);
353
+ return { content: [{ type: 'text', text: JSON.stringify({ success: true, token_type }) }] };
354
+ }
355
+ case 'check_permission': {
356
+ if (!tool_name || !token) {
357
+ return { content: [{ type: 'text', text: JSON.stringify({ error: 'tool_name and token required for check_permission' }) }] };
358
+ }
359
+ const result = checkPermission(tool_name, token);
360
+ return { content: [{ type: 'text', text: JSON.stringify(result) }] };
361
+ }
362
+ case 'list_tools': {
363
+ return { content: [{ type: 'text', text: JSON.stringify({
364
+ read_tools: READ_ONLY_TOOLS,
365
+ write_tools: READ_WRITE_TOOLS,
366
+ total: TOOL_PERMISSIONS.length
367
+ }) }] };
368
+ }
369
+ case 'get_config': {
370
+ return { content: [{ type: 'text', text: JSON.stringify({
371
+ has_tokens: manager.hasTokens(),
372
+ token_configured: {
373
+ read: !!process.env.CG_READ_TOKEN,
374
+ write: !!process.env.CG_WRITE_TOKEN,
375
+ watchdog: !!process.env.CG_WATCHDOG_TOKEN
376
+ }
377
+ }) }] };
378
+ }
379
+ default:
380
+ return { content: [{ type: 'text', text: JSON.stringify({ error: `Unknown action: ${action}` }) }] };
381
+ }
382
+ });
383
+ // ============ Infrastructure: GDPR Compliance Tools ============
384
+ // Tool: gdpr_export
385
+ mcpServer.registerTool('gdpr_export', {
386
+ description: 'Export all user data for GDPR compliance (Article 20 - Right to Data Portability)',
387
+ inputSchema: {
388
+ user_id: z.string().describe('User ID to export data for'),
389
+ include_sessions: z.boolean().optional().default(true).describe('Include session data'),
390
+ include_projects: z.boolean().optional().default(true).describe('Include project data'),
391
+ include_knowledge_graph: z.boolean().optional().default(true).describe('Include knowledge graph data'),
392
+ token: z.string().optional().describe('Watchdog token for write operations')
393
+ }
394
+ }, async ({ user_id, include_sessions, include_projects, include_knowledge_graph, token }) => {
395
+ const permission = checkPermission('gdpr_export', token || '');
396
+ if (!permission.allowed) {
397
+ return { content: [{ type: 'text', text: JSON.stringify({ error: 'Permission denied', reason: permission.reason }) }] };
398
+ }
399
+ const { exportUserData } = await import('../api/gdpr.js');
400
+ const result = exportUserData(user_id, {
401
+ includeSessions: include_sessions,
402
+ includeProjects: include_projects,
403
+ includeKnowledgeGraph: include_knowledge_graph
404
+ });
405
+ return { content: [{ type: 'text', text: JSON.stringify(result) }] };
406
+ });
407
+ // Tool: gdpr_delete
408
+ mcpServer.registerTool('gdpr_delete', {
409
+ description: 'Delete all user data for GDPR compliance (Article 17 - Right to Erasure)',
410
+ inputSchema: {
411
+ user_id: z.string().describe('User ID to delete data for'),
412
+ delete_sessions: z.boolean().optional().default(true).describe('Delete session data'),
413
+ delete_projects: z.boolean().optional().default(false).describe('Delete project data'),
414
+ delete_knowledge_graph: z.boolean().optional().default(true).describe('Delete knowledge graph data'),
415
+ retain_anchored: z.boolean().optional().default(true).describe('Retain anchored memories'),
416
+ token: z.string().optional().describe('Watchdog token for write operations')
417
+ }
418
+ }, async ({ user_id, delete_sessions, delete_projects, delete_knowledge_graph, retain_anchored, token }) => {
419
+ const permission = checkPermission('gdpr_delete', token || '');
420
+ if (!permission.allowed) {
421
+ return { content: [{ type: 'text', text: JSON.stringify({ error: 'Permission denied', reason: permission.reason }) }] };
422
+ }
423
+ const { deleteUserData } = await import('../api/gdpr.js');
424
+ const result = deleteUserData(user_id, {
425
+ deleteSessions: delete_sessions,
426
+ deleteProjects: delete_projects,
427
+ deleteKnowledgeGraph: delete_knowledge_graph,
428
+ retainAnchored: retain_anchored
429
+ });
430
+ return { content: [{ type: 'text', text: JSON.stringify(result) }] };
431
+ });
432
+ // Tool: data_summary
433
+ mcpServer.registerTool('data_summary', {
434
+ description: 'Get summary of user data storage',
435
+ inputSchema: {
436
+ user_id: z.string().describe('User ID to get summary for')
437
+ }
438
+ }, async ({ user_id }) => {
439
+ const { getUserDataSummary } = await import('../api/gdpr.js');
440
+ const result = getUserDataSummary(user_id);
441
+ return { content: [{ type: 'text', text: JSON.stringify(result) }] };
442
+ });
443
+ // ============ Session Management Tools ============
444
+ // Tool: session_store
445
+ mcpServer.registerTool('session_store', {
446
+ description: 'Store a session-level memory (key-value with scope control)',
447
+ inputSchema: {
448
+ key: z.string().min(1).describe('Session memory key'),
449
+ value: z.string().min(1).describe('Session memory value'),
450
+ scope: z.enum(['session', 'short', 'long', 'archival']).optional().default('session')
451
+ .describe('Scope: session (window-close release), short (hours), long (days), archival (permanent)'),
452
+ expires_in_hours: z.number().positive().optional().describe('TTL in hours'),
453
+ meta: z.record(z.string(), z.unknown()).optional().describe('Additional metadata'),
454
+ user_id: z.string().optional().default('default'),
455
+ agent_id: z.string().optional().default('default'),
456
+ project_id: z.string().optional().default('default'),
457
+ token: z.string().optional().describe('Watchdog token for write operations')
458
+ }
459
+ }, async ({ key, value, scope, expires_in_hours, meta, user_id, agent_id, project_id, token }) => {
460
+ const permission = checkPermission('memory_store', token || '');
461
+ if (!permission.allowed) {
462
+ return { content: [{ type: 'text', text: JSON.stringify({ error: 'Permission denied', reason: permission.reason }) }] };
463
+ }
464
+ const { memorySessionStoreTool } = await import('./tools/memory-session.js');
465
+ const result = await memorySessionStoreTool({ key, value, scope: scope || 'session', expires_in_hours, meta, user_id, agent_id, project_id });
466
+ executeAfterChain('session_store', { key, value, scope }, result).catch(() => { });
467
+ return { content: [{ type: 'text', text: JSON.stringify(result) }] };
468
+ });
469
+ // Tool: session_get
470
+ mcpServer.registerTool('session_get', {
471
+ description: 'Get a session-level memory by key',
472
+ inputSchema: {
473
+ key: z.string().min(1).describe('Session memory key'),
474
+ user_id: z.string().optional().default('default'),
475
+ agent_id: z.string().optional().default('default'),
476
+ project_id: z.string().optional().default('default'),
477
+ scope: z.enum(['session', 'short', 'long', 'archival']).optional().default('session')
478
+ }
479
+ }, async ({ key, user_id, agent_id, project_id, scope }) => {
480
+ const { memorySessionGetTool } = await import('./tools/memory-session.js');
481
+ const result = await memorySessionGetTool({ key, user_id, agent_id, project_id, scope: scope || 'session' });
482
+ return { content: [{ type: 'text', text: JSON.stringify(result) }] };
483
+ });
484
+ // Tool: session_list
485
+ mcpServer.registerTool('session_list', {
486
+ description: 'List all session-level memories',
487
+ inputSchema: {
488
+ user_id: z.string().optional().default('default'),
489
+ agent_id: z.string().optional().default('default'),
490
+ project_id: z.string().optional().default('default'),
491
+ scope: z.enum(['session', 'short', 'long', 'archival']).optional(),
492
+ limit: z.number().int().positive().optional().default(50)
493
+ }
494
+ }, async ({ user_id, agent_id, project_id, scope, limit }) => {
495
+ const { memorySessionListTool } = await import('./tools/memory-session.js');
496
+ const result = await memorySessionListTool({ user_id, agent_id, project_id, scope, limit });
497
+ return { content: [{ type: 'text', text: JSON.stringify(result) }] };
498
+ });
499
+ // Tool: session_delete
500
+ mcpServer.registerTool('session_delete', {
501
+ description: 'Delete a session-level memory by key',
502
+ inputSchema: {
503
+ key: z.string().min(1).describe('Session memory key to delete'),
504
+ user_id: z.string().optional().default('default'),
505
+ agent_id: z.string().optional().default('default'),
506
+ project_id: z.string().optional().default('default'),
507
+ scope: z.enum(['session', 'short', 'long', 'archival']).optional().default('session'),
508
+ token: z.string().optional().describe('Watchdog token for write operations')
509
+ }
510
+ }, async ({ key, user_id, agent_id, project_id, scope, token }) => {
511
+ const permission = checkPermission('memory_delete_batch', token || '');
512
+ if (!permission.allowed) {
513
+ return { content: [{ type: 'text', text: JSON.stringify({ error: 'Permission denied', reason: permission.reason }) }] };
514
+ }
515
+ const { memorySessionDeleteTool } = await import('./tools/memory-session.js');
516
+ const result = await memorySessionDeleteTool({ key, user_id, agent_id, project_id, scope: scope || 'session' });
517
+ return { content: [{ type: 'text', text: JSON.stringify(result) }] };
518
+ });
519
+ // ============ Infrastructure: After-Chain Configuration ============
520
+ // Tool: after_chain_configure
521
+ mcpServer.registerTool('after_chain_configure', {
522
+ description: 'Configure after-chain tool orchestration at runtime. Allows agents to register new chains, toggle chains on/off, set global enabled state, and list chains.',
523
+ inputSchema: {
524
+ action: z.enum(['list_chains', 'toggle_chain', 'set_global', 'register_chain', 'get_global_config']).describe('Action to perform'),
525
+ chain_name: z.string().optional().describe('Chain name for toggle_chain and register_chain actions'),
526
+ enabled: z.boolean().optional().describe('Enable/disable state for toggle_chain and set_global actions'),
527
+ chain_config: z.object({
528
+ name: z.string().describe('Chain name'),
529
+ description: z.string().optional().describe('Chain description'),
530
+ triggerTool: z.string().describe('Trigger tool name'),
531
+ followupTool: z.string().describe('Followup tool name'),
532
+ async: z.boolean().optional().default(true).describe('Async execution'),
533
+ condition: z.string().optional().describe('Condition function as string (advanced)'),
534
+ }).optional().describe('Chain config for register_chain action'),
535
+ token: z.string().optional().describe('Watchdog token for write operations')
536
+ }
537
+ }, async ({ action, chain_name, enabled, chain_config, token }) => {
538
+ const permission = checkPermission('configure_llm', token || '');
539
+ if (!permission.allowed) {
540
+ return { content: [{ type: 'text', text: JSON.stringify({ error: 'Permission denied', reason: permission.reason }) }] };
541
+ }
542
+ const registry = getAfterChainRegistry();
543
+ switch (action) {
544
+ case 'list_chains': {
545
+ const chains = registry.getAllChains();
546
+ return { content: [{ type: 'text', text: JSON.stringify({
547
+ chains: chains.map(c => ({
548
+ name: c.name,
549
+ description: c.description,
550
+ hooks: c.hooks.map(h => ({
551
+ triggerTool: h.triggerTool,
552
+ followupTool: h.followupTool,
553
+ async: h.async,
554
+ enabled: h.enabled,
555
+ })),
556
+ })),
557
+ total: chains.length,
558
+ }) }] };
559
+ }
560
+ case 'toggle_chain': {
561
+ if (!chain_name || enabled === undefined) {
562
+ return { content: [{ type: 'text', text: JSON.stringify({ error: 'chain_name and enabled required for toggle_chain' }) }] };
563
+ }
564
+ const success = registry.toggleChain(chain_name, enabled);
565
+ return { content: [{ type: 'text', text: JSON.stringify({ success, chain_name, enabled }) }] };
566
+ }
567
+ case 'set_global': {
568
+ if (enabled === undefined) {
569
+ return { content: [{ type: 'text', text: JSON.stringify({ error: 'enabled required for set_global' }) }] };
570
+ }
571
+ registry.updateGlobalConfig({ enabled });
572
+ return { content: [{ type: 'text', text: JSON.stringify({ success: true, global_enabled: enabled }) }] };
573
+ }
574
+ case 'register_chain': {
575
+ if (!chain_config) {
576
+ return { content: [{ type: 'text', text: JSON.stringify({ error: 'chain_config required for register_chain' }) }] };
577
+ }
578
+ registry.registerChain({
579
+ name: chain_config.name,
580
+ description: chain_config.description,
581
+ hooks: [{
582
+ triggerTool: chain_config.triggerTool,
583
+ followupTool: chain_config.followupTool,
584
+ async: chain_config.async ?? true,
585
+ enabled: true,
586
+ }],
587
+ });
588
+ return { content: [{ type: 'text', text: JSON.stringify({ success: true, chain_name: chain_config.name }) }] };
589
+ }
590
+ case 'get_global_config': {
591
+ const config = registry.getGlobalConfig();
592
+ return { content: [{ type: 'text', text: JSON.stringify(config) }] };
593
+ }
594
+ default:
595
+ return { content: [{ type: 'text', text: JSON.stringify({ error: `Unknown action: ${action}` }) }] };
596
+ }
597
+ });
598
+ // Export for testing
599
+ export { mcpServer };
600
+ /**
601
+ * Main entry point - start the MCP server
602
+ */
603
+ export async function runServer() {
604
+ await initialize();
605
+ const transport = new StdioServerTransport();
606
+ await mcpServer.connect(transport);
607
+ logger.info('Context Gatekeeper MCP server running on stdio');
608
+ }
609
+ runServer().catch(error => {
610
+ logger.error('Server error', { error });
611
+ process.exit(1);
612
+ });
613
+ //# sourceMappingURL=server.js.map