@rigstate/mcp 0.4.2 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/.agent/skills/client-side-notification-logger/SKILL.md +139 -0
  2. package/.agent/skills/react-state-counter/SKILL.md +73 -0
  3. package/.agent/skills/rigstate-evolutionary-refactor/SKILL.md +40 -0
  4. package/.agent/skills/rigstate-integrity-gate/SKILL.md +55 -0
  5. package/.agent/skills/rigstate-legacy-renovator/SKILL.md +12 -0
  6. package/.agent/skills/sec-auth-04/SKILL.md +22 -0
  7. package/.agent/skills/sec-key-01/SKILL.md +21 -0
  8. package/.agent/skills/sec-rls-01/SKILL.md +22 -0
  9. package/.agent/skills/sec-sql-01/SKILL.md +23 -0
  10. package/.agent/skills/sec-ui-01/SKILL.md +21 -0
  11. package/.cursor/rules/rigstate-database.mdc +89 -0
  12. package/.cursor/rules/rigstate-guardian.mdc +43 -0
  13. package/.cursor/rules/rigstate-identity.mdc +45 -0
  14. package/.cursor/rules/rigstate-roadmap.mdc +9 -0
  15. package/.cursor/rules/rigstate-workflow.mdc +323 -0
  16. package/.cursorrules +402 -0
  17. package/AGENTS.md +34 -0
  18. package/dist/index.js +2604 -3067
  19. package/dist/index.js.map +1 -1
  20. package/package.json +2 -2
  21. package/roadmap.json +815 -21
  22. package/src/index.ts +16 -1765
  23. package/src/lib/context-engine.ts +85 -0
  24. package/src/lib/curator/actions/fortress.ts +77 -0
  25. package/src/lib/curator/actions/query.ts +73 -0
  26. package/src/lib/curator/actions/stats.ts +70 -0
  27. package/src/lib/curator/actions/submit.ts +190 -0
  28. package/src/lib/curator/index.ts +10 -0
  29. package/src/lib/curator/schemas.ts +37 -0
  30. package/src/lib/schemas.ts +191 -0
  31. package/src/lib/types.ts +102 -261
  32. package/src/server/core.ts +40 -0
  33. package/src/server/factory.ts +78 -0
  34. package/src/server/telemetry.ts +122 -0
  35. package/src/server/types.ts +21 -0
  36. package/src/tools/analyze-database-performance.ts +157 -0
  37. package/src/tools/arch-tools.ts +16 -0
  38. package/src/tools/audit-integrity-gate.ts +166 -0
  39. package/src/tools/check-rules-sync.ts +20 -0
  40. package/src/tools/complete-roadmap-task.ts +88 -31
  41. package/src/tools/curator-tools.ts +74 -0
  42. package/src/tools/get-latest-decisions.ts +22 -0
  43. package/src/tools/get-next-roadmap-step.ts +21 -0
  44. package/src/tools/get-project-context.ts +35 -1
  45. package/src/tools/index.ts +7 -0
  46. package/src/tools/list-features.ts +4 -1
  47. package/src/tools/list-roadmap-tasks.ts +21 -0
  48. package/src/tools/planning-tools.ts +40 -0
  49. package/src/tools/query-brain.ts +25 -1
  50. package/src/tools/run-architecture-audit.ts +23 -0
  51. package/src/tools/save-decision.ts +26 -0
  52. package/src/tools/security-checks.ts +241 -0
  53. package/src/tools/security-tools.ts +88 -18
  54. package/src/tools/submit-idea.ts +25 -0
  55. package/src/tools/sync-ide-rules.ts +35 -3
  56. package/src/tools/teacher-mode.ts +92 -13
  57. package/src/tools/update-roadmap.ts +24 -0
package/src/index.ts CHANGED
@@ -1,1792 +1,43 @@
1
- /**
2
- * Rigstate MCP Server
3
- *
4
- * A Model Context Protocol server that exposes Rigstate's Project Brain
5
- * and Council Decisions to AI editors like Cursor and Claude Desktop.
6
- *
7
- * Environment Variables:
8
- * - RIGSTATE_API_KEY: Your Rigstate API key (sk_rigstate_...)
9
- * - RIGSTATE_SUPABASE_URL: Supabase project URL (optional, defaults to env)
10
- * - RIGSTATE_SUPABASE_ANON_KEY: Supabase anon key (optional, defaults to env)
11
- *
12
- * Usage:
13
- * RIGSTATE_API_KEY=sk_rigstate_xxx npx @rigstate/mcp
14
- *
15
- * Or in Cursor/Claude config:
16
- * {
17
- * "mcpServers": {
18
- * "rigstate": {
19
- * "command": "npx",
20
- * "args": ["@rigstate/mcp"],
21
- * "env": { "RIGSTATE_API_KEY": "sk_rigstate_xxx" }
22
- * }
23
- * }
24
- * }
25
- */
26
-
27
- import { Server } from '@modelcontextprotocol/sdk/server/index.js';
28
1
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
29
- import fs from 'fs/promises';
30
- import path from 'path';
31
- import os from 'os';
32
- import {
33
- CallToolRequestSchema,
34
- ListToolsRequestSchema,
35
- ListResourcesRequestSchema,
36
- ReadResourceRequestSchema,
37
- ErrorCode,
38
- McpError,
39
- } from '@modelcontextprotocol/sdk/types.js';
40
-
41
- import { authenticateApiKey, type AuthContext } from './lib/supabase.js';
42
- import {
43
- QueryBrainInputSchema,
44
- GetProjectContextInputSchema,
45
- GetLatestDecisionsInputSchema,
46
- SaveDecisionInputSchema,
47
- SubmitIdeaInputSchema,
48
- UpdateRoadmapInputSchema,
49
- RunArchitectureAuditInputSchema,
50
- GenerateCursorRulesInputSchema,
51
- RefineLogicInputSchema,
52
- GetLearnedInstructionsInputSchema,
53
- AnalyzeDependencyGraphInputSchema,
54
- AuditRlsStatusInputSchema,
55
- QueryProjectBrainInputSchema,
56
- FetchPackageHealthInputSchema,
57
- SaveToProjectBrainInputSchema,
58
- UpdateRoadmapStatusInputSchema,
59
- AddRoadmapChunkInputSchema,
60
- AnalyzeUiComponentInputSchema,
61
- ApplyDesignSystemInputSchema,
62
- FetchUiLibraryDocsInputSchema
63
- } from './lib/types.js';
64
- import { ListRoadmapTasksInputSchema, CheckAgentBridgeInputSchema, CheckRulesSyncInputSchema, GetNextRoadmapStepInputSchema, GenerateProfessionalPDFInputSchema, ArchaeologicalScanInputSchema, ImportGhostFeaturesInputSchema, GetPendingTasksInputSchema, UpdateTaskStatusInputSchema } from './lib/types.js';
65
- import { getProjectContext } from './tools/get-project-context.js';
66
- import { queryBrain } from './tools/query-brain.js';
67
- import { getLatestDecisions } from './tools/get-latest-decisions.js';
68
- import { saveDecision } from './tools/save-decision.js';
69
- import { submitIdea } from './tools/submit-idea.js';
70
- import { updateRoadmap } from './tools/update-roadmap.js';
71
- import { checkAgentBridge } from './tools/check-agent-bridge.js';
72
- import { checkRulesSync } from './tools/check-rules-sync.js';
73
- import { runArchitectureAudit } from './tools/run-architecture-audit.js';
74
- import { refineLogic, getLearnedInstructions } from './tools/teacher-mode.js';
75
- import { listRoadmapTasks } from './tools/list-roadmap-tasks.js';
76
- import { getNextRoadmapStep } from './tools/get-next-roadmap-step.js';
77
- import { generateProfessionalPdf } from './tools/generate-professional-pdf.js';
78
- import { syncIdeRules } from './tools/sync-ide-rules.js';
79
- import { performArchaeologicalScan, importGhostFeatures } from './tools/archaeological-scan.js';
80
- import { analyzeDependencyGraph } from './tools/arch-tools.js';
81
- import { auditRlsStatus } from './tools/security-tools.js';
82
- import { queryProjectBrain, fetchPackageHealth } from './tools/research-tools.js';
83
- import { saveToProjectBrain, updateRoadmapStatus, addRoadmapChunk } from './tools/planning-tools.js';
84
- import { completeRoadmapTask } from './tools/complete-roadmap-task.js';
85
- import { listFeaturesTool } from './tools/list-features.js'; // NEW
86
- import { registry } from './lib/tool-registry.js'; // NEW
87
- import { analyzeUiComponent, applyDesignSystem, fetchUiLibraryDocs } from './tools/ui-tools.js';
88
- import { getProjectMorals } from './resources/project-morals.js';
89
- import { getPendingTasks, updateTaskStatus } from './tools/pending-tasks.js';
90
- import { SupabaseClient } from '@supabase/supabase-js';
91
-
92
- // REGISTER MIGRATED TOOLS
93
- registry.register(listFeaturesTool);
94
-
95
-
96
- // =============================================================================
97
- // FRANK WATCHER (Internal Background Process)
98
- // =============================================================================
99
-
100
- let watcherState = {
101
- isRunning: false,
102
- lastCheck: null as string | null,
103
- tasksFound: 0,
104
- projectId: null as string | null // Will be auto-magically inferred if possible, or just scan all owned projects?
105
- // Actually, scan all projects owned by the authenticated user is safer.
106
- // For now, let's keep it simple: scan *all* projects the user has access to that have stuck agent jobs.
107
- // Or better: Use the authContext supabase instance which is user-scoped!
108
- };
109
-
110
- async function startFrankWatcher(supabase: SupabaseClient, userId: string) {
111
- if (watcherState.isRunning) return;
112
- watcherState.isRunning = true;
113
- console.error(`🤖 Frank Watcher started for user ${userId}`);
114
-
115
- const checkTasks = async () => {
116
- try {
117
- watcherState.lastCheck = new Date().toISOString();
118
-
119
- // 1. Check for PENDING or APPROVED tasks
120
- // 1. Check for PENDING or APPROVED tasks
121
- const { data: tasks, error } = await supabase
122
- .from('agent_bridge')
123
- .select('*') // Remove problematic join to roadmap_chunks
124
- .in('status', ['PENDING', 'APPROVED'])
125
- .order('created_at', { ascending: true })
126
- .limit(1);
127
-
128
- if (error) return;
129
-
130
- if (tasks && tasks.length > 0) {
131
- const task = tasks[0];
132
- watcherState.tasksFound++;
133
-
134
- if (task.proposal?.startsWith('ping') || (task.task_id === null && !task.proposal?.startsWith('report'))) {
135
- console.error(`\n⚡ HEARTBEAT: Frank received REAL-TIME PING for project ${task.project_id}. Response: PONG`);
136
-
137
- // Respond to ping by completing it immediately
138
- await supabase
139
- .from('agent_bridge')
140
- .update({
141
- status: 'COMPLETED',
142
- summary: 'Pong! Frank is active and listening.',
143
- updated_at: new Date().toISOString()
144
- })
145
- .eq('id', task.id);
146
-
147
- // Clear any local cache/state if needed
148
- return;
149
- }
150
-
151
- // Handle Governance Report generation
152
- if (task.proposal?.startsWith('report')) {
153
- const parts = task.proposal.split(':');
154
- const signalType = parts[1]; // 'MANIFEST' or 'INVESTOR'
155
- const reportType = signalType === 'MANIFEST' ? 'SYSTEM_MANIFEST' : 'INVESTOR_REPORT';
156
-
157
- console.error(`\n📄 Frank is generating ${reportType} report for project ${task.project_id}...`);
158
-
159
- try {
160
- // Use the real generation logic
161
- const result = await generateProfessionalPdf(
162
- supabase,
163
- userId,
164
- task.project_id,
165
- reportType
166
- );
167
-
168
- // Update the TRIGGER task with the results so the UI sees it
169
- await supabase
170
- .from('agent_bridge')
171
- .update({
172
- status: 'COMPLETED',
173
- summary: `Report generated: ${reportType === 'SYSTEM_MANIFEST' ? 'Technical Manifest' : 'Investor Intelligence'}.`,
174
- proposal: JSON.stringify(result.data), // Pass structured data back
175
- updated_at: new Date().toISOString()
176
- })
177
- .eq('id', task.id);
178
-
179
- } catch (genError: any) {
180
- console.error(`❌ Report generation failed: ${genError.message}`);
181
- await supabase
182
- .from('agent_bridge')
183
- .update({
184
- status: 'FAILED',
185
- summary: `Generation failed: ${genError.message}`,
186
- updated_at: new Date().toISOString()
187
- })
188
- .eq('id', task.id);
189
- }
190
-
191
- return;
192
- }
193
-
194
- if (task.status === 'APPROVED') {
195
- console.error(`\n🏗️ Worker: EXECUTING approved task: [${task.id}]`);
196
-
197
- // 1. Move to EXECUTING
198
- await supabase
199
- .from('agent_bridge')
200
- .update({
201
- status: 'EXECUTING',
202
- updated_at: new Date().toISOString()
203
- })
204
- .eq('id', task.id);
205
-
206
- // 2. Simulate work (Execution)
207
- await new Promise(resolve => setTimeout(resolve, 3000));
208
-
209
- // 3. Generate Mission Report (Data Ingestion)
210
- const taskTitle = (task.roadmap_chunks as any)?.title || 'manual objective';
211
- const technicalSummary = `Processed ${task.task_id || 'manual task'}. Applied architectural alignment. Verified L-max compliance.`;
212
- const humanSummary = `Mission Accomplished: I've successfully completed "${taskTitle}" and synchronized changes with the Project Brain. 🚀`; // Gunhild's mandate
213
-
214
- await supabase
215
- .from('mission_reports')
216
- .insert({
217
- bridge_id: task.id,
218
- project_id: task.project_id,
219
- task_id: task.task_id,
220
- human_summary: humanSummary,
221
- technical_summary: technicalSummary,
222
- file_diff_stats: {
223
- files: ["src/actions/lab.ts", "src/actions/lab/core.ts"],
224
- added: 142,
225
- deleted: 385
226
- }
227
- });
228
-
229
- // 4. Move to COMPLETED
230
- await supabase
231
- .from('agent_bridge')
232
- .update({
233
- status: 'COMPLETED',
234
- summary: humanSummary,
235
- execution_summary: technicalSummary,
236
- updated_at: new Date().toISOString(),
237
- completed_at: new Date().toISOString()
238
- })
239
- .eq('id', task.id);
240
-
241
- // 5. Also update the roadmap chunk status
242
- if (task.task_id) {
243
- await supabase
244
- .from('roadmap_chunks')
245
- .update({
246
- status: 'COMPLETED',
247
- completed_at: new Date().toISOString()
248
- })
249
- .eq('id', task.task_id);
250
- }
251
-
252
- console.error(`✅ Worker: Mission finished for task ${task.id}`);
253
- return;
254
- }
255
-
256
- console.error(`\n🔎 Frank Watcher found task: [${(task.roadmap_chunks as any)?.title || task.id}]`);
257
-
258
- // 2. "Analyze" - Specialized heuristics or generic plan
259
- let proposal = '';
260
-
261
- // Simple heuristic analysis
262
- const taskTitle = (task.roadmap_chunks as any)?.title || '';
263
- if (taskTitle.includes('lab.ts')) {
264
- await new Promise(resolve => setTimeout(resolve, 2000));
265
- proposal = `I will modularize the apps/web/src/actions/lab.ts file to comply with the <400 lines limit.\n\nThe new structure will be:\n- apps/web/src/actions/lab/index.ts: Re-export everything to ensure backward compatibility.\n- apps/web/src/actions/lab/core.ts: Shared types and buildProjectContext.\n- apps/web/src/actions/lab/sessions.ts: Lab sessions and chat logic.\n- apps/web/src/actions/lab/ideas.ts: Idea management and crystallization.\n- apps/web/src/actions/lab/council.ts: Council review logic.\n- apps/web/src/actions/lab/protection.ts: Roadmap protection logic.\n\nThis will strictly enforce the L-max rule and improve maintainability.`;
266
- } else {
267
- await new Promise(resolve => setTimeout(resolve, 1500));
268
- proposal = `Frank (Auto-Mode) has analyzed the request for "${taskTitle || 'Task'}".\n\n**Proposed Plan:**\n1. Review context.\n2. Apply changes.\n3. Verify.\n\nReady to proceed.`;
269
- }
270
-
271
- // 3. Update Status
272
- await supabase
273
- .from('agent_bridge')
274
- .update({
275
- status: 'AWAITING_APPROVAL',
276
- proposal: proposal,
277
- updated_at: new Date().toISOString()
278
- })
279
- .eq('id', task.id);
280
-
281
- console.error(`✅ Frank Watcher submitted plan for task ${task.id}`);
282
- }
283
-
284
- } catch (e) {
285
- // console.error('Watcher Exception:', e);
286
- }
287
- };
288
-
289
- // 1. Active Listening (Realtime)
290
- const channel = supabase
291
- .channel('frank-watcher')
292
- .on(
293
- 'postgres_changes',
294
- {
295
- event: '*', // Listen for all events (INSERT/UPDATE)
296
- schema: 'public',
297
- table: 'agent_bridge'
298
- },
299
- (payload: any) => {
300
- // Trigger immediate check if new task is PENDING or existing became APPROVED
301
- if (payload.new.status === 'PENDING' || payload.new.status === 'APPROVED') {
302
- checkTasks();
303
- }
304
- }
305
- )
306
- .subscribe();
307
-
308
- // 2. Passive Fallback (Polling)
309
- setInterval(checkTasks, 5000); // 5s fallback for aggressive response
310
-
311
- // Initial check
312
- checkTasks();
313
- }
314
-
315
- // =============================================================================
316
- // CONFIGURATION
317
- // =============================================================================
318
-
319
- const SERVER_NAME = 'rigstate-mcp';
320
- const SERVER_VERSION = '0.4.1'; // Read-Only Enforcement - Writes disabled
321
-
322
- // =============================================================================
323
- // TOOL DEFINITIONS
324
- // =============================================================================
325
-
326
- const TOOLS = [
327
- // GUARDIAN LOCKS
328
- {
329
- name: 'write_to_file',
330
- description: 'Guardian Lock: Blocks file writes if RIGSTATE_MODE is SUPERVISOR.',
331
- inputSchema: { type: 'object', properties: {}, additionalProperties: true }
332
- },
333
- {
334
- name: 'replace_file_content',
335
- description: 'Guardian Lock: Blocks file edits if RIGSTATE_MODE is SUPERVISOR.',
336
- inputSchema: { type: 'object', properties: {}, additionalProperties: true }
337
- },
338
- {
339
- name: 'run_command',
340
- description: 'Guardian Lock: Blocks commands if RIGSTATE_MODE is SUPERVISOR.',
341
- inputSchema: { type: 'object', properties: {}, additionalProperties: true }
342
- },
343
- {
344
- name: 'get_project_context',
345
- description: `Returns the project type, tech stack, and high-level description for a Rigstate project.
346
- Use this to understand what technology stack and project type you're working with before generating code.`,
347
- inputSchema: {
348
- type: 'object' as const,
349
- properties: {
350
- projectId: {
351
- type: 'string',
352
- description: 'The UUID of the Rigstate project'
353
- }
354
- },
355
- required: ['projectId']
356
- }
357
- },
358
- {
359
- name: 'query_brain',
360
- description: `Queries the Project Brain for relevant memories, architecture rules, and decisions.
361
- Use this when you need to understand project-specific constraints, coding standards, or past decisions.`,
362
- inputSchema: {
363
- type: 'object' as const,
364
- properties: {
365
- projectId: {
366
- type: 'string',
367
- description: 'The UUID of the Rigstate project'
368
- },
369
- query: {
370
- type: 'string',
371
- description: 'Natural language query to search the project brain'
372
- },
373
- limit: {
374
- type: 'number',
375
- description: 'Maximum number of memories to return (default: 8, max: 20)'
376
- },
377
- threshold: {
378
- type: 'number',
379
- description: 'Similarity threshold for semantic search (0-1, default: 0.5)'
380
- }
381
- },
382
- required: ['projectId', 'query']
383
- }
384
- },
385
- {
386
- name: 'get_latest_decisions',
387
- description: `Fetches the most recent ADRs and decisions from The Architect's Council.
388
- Use this to understand recent architectural decisions, roadmap focus, and council feedback.`,
389
- inputSchema: {
390
- type: 'object' as const,
391
- properties: {
392
- projectId: {
393
- type: 'string',
394
- description: 'The UUID of the Rigstate project'
395
- },
396
- limit: {
397
- type: 'number',
398
- description: 'Maximum number of council sessions to return (default: 5, max: 10)'
399
- }
400
- },
401
- required: ['projectId']
402
- }
403
- },
404
- {
405
- name: 'list_roadmap_tasks',
406
- description: `Lists all roadmap tasks for a project that are not completed.
407
- Returns id, title, priority, and status. Use this to find actionable tasks.`,
408
- inputSchema: {
409
- type: 'object' as const,
410
- properties: {
411
- projectId: {
412
- type: 'string',
413
- description: 'The UUID of the Rigstate project'
414
- }
415
- },
416
- required: ['projectId']
417
- }
418
- },
419
- {
420
- name: 'check_agent_bridge',
421
- description: `Checks for pending agent tasks or updates task status.
422
- Use this to poll for work (status: PENDING/APPROVED) or to update a task's progress.
423
- - If checking: Returns the oldest actionable task.
424
- - If updating: Requires 'bridgeId' and 'action="update"'.`,
425
- inputSchema: {
426
- type: 'object' as const,
427
- properties: {
428
- projectId: {
429
- type: 'string',
430
- description: 'The UUID of the Rigstate project'
431
- },
432
- action: {
433
- type: 'string',
434
- enum: ['check', 'update'],
435
- description: 'Action to perform (default: check)'
436
- },
437
- bridgeId: {
438
- type: 'string',
439
- description: 'ID of the bridge task (required for update)'
440
- },
441
- status: {
442
- type: 'string',
443
- enum: ['PENDING', 'AWAITING_APPROVAL', 'APPROVED', 'EXECUTING', 'COMPLETED', 'FAILED'],
444
- description: 'New status (for update)'
445
- },
446
- summary: {
447
- type: 'string',
448
- description: 'Execution summary or report (for update)'
449
- },
450
- proposal: {
451
- type: 'string',
452
- description: 'Plan/Proposal markdown (for update)'
453
- }
454
- },
455
- required: ['projectId']
456
- }
457
- },
458
- // =========================================================================
459
- {
460
- name: 'check_rules_sync',
461
- description: `Checks if the IDE's rules file contains the VIBELINE managed block.
462
- Use this to verifying that project rules are present and up-to-date in the editor context.`,
463
- inputSchema: {
464
- type: 'object' as const,
465
- properties: {
466
- projectId: {
467
- type: 'string',
468
- description: 'The UUID of the Rigstate project'
469
- },
470
- currentRulesContent: {
471
- type: 'string',
472
- description: 'The content of the .cursorrules or active rules file to check'
473
- }
474
- },
475
- required: ['projectId']
476
- }
477
- },
478
- {
479
- name: 'get_agent_status',
480
- description: `Checks the status of the internal Frank Watcher agent.
481
- Returns whether the background poll loop is running and stats.`,
482
- inputSchema: {
483
- type: 'object' as const,
484
- properties: {},
485
- required: []
486
- }
487
- },
488
- {
489
- name: 'get_next_roadmap_step',
490
- description: `Fetches the next logical step from the project roadmap.
491
- Use this after completing a task to see what to do next.`,
492
- inputSchema: {
493
- type: 'object' as const,
494
- properties: {
495
- projectId: {
496
- type: 'string',
497
- description: 'The UUID of the Rigstate project'
498
- },
499
- currentStepId: {
500
- type: 'string',
501
- description: 'Optional: The ID of the step you just finished.'
502
- }
503
- },
504
- required: ['projectId']
505
- }
506
- },
507
- {
508
- name: 'complete_roadmap_task',
509
- description: `Marks a roadmap task as COMPLETED and logs a summary of the work done.
510
- Use this when you have finished a task in the IDE and want to update the project roadmap.`,
511
- inputSchema: {
512
- type: 'object' as const,
513
- properties: {
514
- projectId: {
515
- type: 'string',
516
- description: 'The UUID of the Rigstate project'
517
- },
518
- summary: {
519
- type: 'string',
520
- description: 'A human-readable summary of what was completed.'
521
- },
522
- taskId: {
523
- type: 'string',
524
- description: 'Optional: ID of specific task. If omitted, completes the current active task.'
525
- },
526
- gitDiff: {
527
- type: 'string',
528
- description: 'Optional: Technical summary or git diff stats.'
529
- }
530
- },
531
- required: ['projectId', 'summary']
532
- }
533
- },
534
- {
535
- name: 'sync_ide_rules',
536
- description: `Generates and returns the appropriate rules file (.cursorrules, .windsurfrules, etc.) based on project context and user preferences.`,
537
- inputSchema: {
538
- type: 'object' as const,
539
- properties: {
540
- projectId: {
541
- type: 'string',
542
- description: 'The UUID of the Rigstate project'
543
- }
544
- },
545
- required: ['projectId']
546
- }
547
- },
548
- // =========================================================================
549
- // WRITE OPERATIONS (DEPRECATED - Use CLI or Dashboard instead)
550
- // =========================================================================
551
- {
552
- name: 'save_decision',
553
- description: `[DEPRECATED] Saves a new architectural decision (ADR) to the Project Brain.
554
- ⚠️ NOTICE: Write operations via MCP are deprecated. Use the Rigstate Dashboard or CLI instead.
555
- This tool will be removed in a future version. For now, it still works but is not recommended.`,
556
- inputSchema: {
557
- type: 'object' as const,
558
- properties: {
559
- projectId: {
560
- type: 'string',
561
- description: 'The UUID of the Rigstate project'
562
- },
563
- title: {
564
- type: 'string',
565
- description: 'Short title for the decision (e.g., "Use Prisma for ORM")'
566
- },
567
- decision: {
568
- type: 'string',
569
- description: 'The full decision content'
570
- },
571
- rationale: {
572
- type: 'string',
573
- description: 'Optional explanation of why this decision was made'
574
- },
575
- category: {
576
- type: 'string',
577
- enum: ['decision', 'architecture', 'constraint', 'tech_stack', 'design_rule'],
578
- description: 'Category of the decision (default: decision)'
579
- },
580
- tags: {
581
- type: 'array',
582
- items: { type: 'string' },
583
- description: 'Optional tags for categorization'
584
- }
585
- },
586
- required: ['projectId', 'title', 'decision']
587
- }
588
- },
589
- {
590
- name: 'submit_idea',
591
- description: `[DEPRECATED] Submits a new idea to the Idea Lab.
592
- ⚠️ NOTICE: Write operations via MCP are deprecated. Use the Rigstate Dashboard instead.
593
- This tool will be removed in a future version.`,
594
- inputSchema: {
595
- type: 'object' as const,
596
- properties: {
597
- projectId: {
598
- type: 'string',
599
- description: 'The UUID of the Rigstate project'
600
- },
601
- title: {
602
- type: 'string',
603
- description: 'Short title for the idea'
604
- },
605
- description: {
606
- type: 'string',
607
- description: 'Detailed description of the idea'
608
- },
609
- category: {
610
- type: 'string',
611
- enum: ['feature', 'improvement', 'experiment', 'pivot'],
612
- description: 'Category of the idea (default: feature)'
613
- },
614
- tags: {
615
- type: 'array',
616
- items: { type: 'string' },
617
- description: 'Optional tags for categorization'
618
- }
619
- },
620
- required: ['projectId', 'title', 'description']
621
- }
622
- },
623
- {
624
- name: 'update_roadmap',
625
- description: `[DEPRECATED] Updates the status of a roadmap step.
626
- ⚠️ NOTICE: Write operations via MCP are deprecated. Use "rigstate sync" CLI or Dashboard instead.
627
- This tool will be removed in a future version.`,
628
- inputSchema: {
629
- type: 'object' as const,
630
- properties: {
631
- projectId: {
632
- type: 'string',
633
- description: 'The UUID of the Rigstate project'
634
- },
635
- chunkId: {
636
- type: 'string',
637
- description: 'The UUID of the roadmap chunk to update'
638
- },
639
- title: {
640
- type: 'string',
641
- description: 'Search for chunk by title (fuzzy match). Use if chunkId is not known.'
642
- },
643
- status: {
644
- type: 'string',
645
- enum: ['LOCKED', 'ACTIVE', 'COMPLETED'],
646
- description: 'New status for the roadmap step'
647
- }
648
- },
649
- required: ['projectId', 'status']
650
- }
651
- },
652
- {
653
- name: 'run_architecture_audit',
654
- description: `Audits code against project architecture rules and security patterns.
655
- Returns violations or "Pass" status with a score.`,
656
- inputSchema: {
657
- type: 'object' as const,
658
- properties: {
659
- projectId: {
660
- type: 'string',
661
- description: 'The UUID of the Rigstate project'
662
- },
663
- filePath: {
664
- type: 'string',
665
- description: 'Path of the file being audited (for context)'
666
- },
667
- content: {
668
- type: 'string',
669
- description: 'The source code content to audit'
670
- }
671
- },
672
- required: ['projectId', 'filePath', 'content']
673
- }
674
- },
675
- // =========================================================================
676
- // TEACHER MODE TOOLS
677
- // =========================================================================
678
- {
679
- name: 'refine_logic',
680
- description: `Send a logic correction to teach Frank how to handle similar situations.
681
- Use this when Frank made a reasoning error and you want to correct it.
682
- The correction will be saved and applied to future interactions.`,
683
- inputSchema: {
684
- type: 'object' as const,
685
- properties: {
686
- projectId: {
687
- type: 'string',
688
- description: 'The UUID of the Rigstate project'
689
- },
690
- originalReasoning: {
691
- type: 'string',
692
- description: 'What Frank originally said or did wrong'
693
- },
694
- userCorrection: {
695
- type: 'string',
696
- description: 'How Frank should handle this in the future'
697
- },
698
- scope: {
699
- type: 'string',
700
- enum: ['project', 'global'],
701
- description: 'Whether this correction applies to this project only or all projects (default: project)'
702
- }
703
- },
704
- required: ['projectId', 'originalReasoning', 'userCorrection']
705
- }
706
- },
707
- {
708
- name: 'get_learned_instructions',
709
- description: `Fetch all learned behaviors and corrections from the database.
710
- Use this to inject prior corrections into your context window.
711
- Returns both user-specific and global Rigstate standards.`,
712
- inputSchema: {
713
- type: 'object' as const,
714
- properties: {
715
- projectId: {
716
- type: 'string',
717
- description: 'Optional project ID to include project-specific instructions'
718
- }
719
- },
720
- required: []
721
- }
722
- },
723
- {
724
- name: 'generate_professional_pdf',
725
- description: `Requests "The Scribe" to generate a professional PDF report.
726
- Translates technical data into high-value briefings.`,
727
- inputSchema: {
728
- type: 'object' as const,
729
- properties: {
730
- projectId: {
731
- type: 'string',
732
- description: 'The UUID of the Rigstate project'
733
- },
734
- reportType: {
735
- type: 'string',
736
- enum: ['SYSTEM_MANIFEST', 'INVESTOR_REPORT'],
737
- description: 'The type of report to generate'
738
- }
739
- },
740
- required: ['projectId', 'reportType']
741
- }
742
- },
743
- // =========================================================================
744
- // BRYNJAR - THE LIBRARIAN (Archaeological Tools)
745
- // =========================================================================
746
- {
747
- name: 'archaeological_scan',
748
- description: `Invokes Brynjar, the Project Archivist, to perform an archaeological scan.
749
- Analyzes Git history and file structure to reconstruct a project's historical context.
750
- Returns discovered "Ghost Features" that represent completed work not yet in the roadmap.
751
- Use this for projects that feel like "empty shells" to restore their history.`,
752
- inputSchema: {
753
- type: 'object' as const,
754
- properties: {
755
- projectId: {
756
- type: 'string',
757
- description: 'The UUID of the Rigstate project'
758
- },
759
- gitLog: {
760
- type: 'string',
761
- description: 'Git log output. Format each commit as: hash:X\\ndate:X\\nauthor:X\\nmessage\\n---COMMIT---'
762
- },
763
- fileTree: {
764
- type: 'array',
765
- items: { type: 'string' },
766
- description: 'Array of file/directory paths in the repository'
767
- }
768
- },
769
- required: ['projectId', 'gitLog', 'fileTree']
770
- }
771
- },
772
- {
773
- name: 'import_ghost_features',
774
- description: `Imports discovered Ghost Features into the roadmap as COMPLETED chunks.
775
- Use this after reviewing the archaeological_scan results to add historical features to the project.
776
- All imported features are marked with is_ghost=true and appear with green checkmarks.`,
777
- inputSchema: {
778
- type: 'object' as const,
779
- properties: {
780
- projectId: {
781
- type: 'string',
782
- description: 'The UUID of the Rigstate project'
783
- },
784
- features: {
785
- type: 'array',
786
- items: {
787
- type: 'object',
788
- properties: {
789
- id: { type: 'string' },
790
- title: { type: 'string' },
791
- description: { type: 'string' },
792
- status: { type: 'string', enum: ['COMPLETED'] },
793
- source: { type: 'string', enum: ['git', 'filesystem', 'combined'] },
794
- evidence: { type: 'array', items: { type: 'string' } },
795
- estimatedCompletionDate: { type: 'string' },
796
- priority: { type: 'number' }
797
- }
798
- },
799
- description: 'Array of discovered features from archaeological_scan to import'
800
- }
801
- },
802
- required: ['projectId', 'features']
803
- }
804
- },
805
- // =========================================================================
806
- // STRUCTURAL SHIELD (EINAR & SVEN)
807
- // =========================================================================
808
- {
809
- name: 'analyze_dependency_graph',
810
- description: `Analyzes the project source code to build a dependency graph and detect circular dependencies.
811
- Returns a report on architectural violations that must be fixed.`,
812
- inputSchema: {
813
- type: 'object' as const,
814
- properties: {
815
- path: {
816
- type: 'string',
817
- description: 'Root path to analyze (default: src)'
818
- }
819
- },
820
- required: []
821
- }
822
- },
823
- {
824
- name: 'audit_rls_status',
825
- description: `Audits the database tables to verify Row Level Security (RLS) is enabled.
826
- Returns a list of unsecured tables that pose a security risk.`,
827
- inputSchema: {
828
- type: 'object' as const,
829
- properties: {
830
- projectId: {
831
- type: 'string',
832
- description: 'The UUID of the Rigstate project (optional context)'
833
- }
834
- },
835
- required: []
836
- }
837
- },
838
- // =========================================================================
839
- // INTELLIGENCE CORE (MAJA & ASTRID)
840
- // =========================================================================
841
- {
842
- name: 'query_project_brain',
843
- description: `Maja's Tool: Searches the project brain for context, ADRs, and decisions.
844
- Supports simple text search across content and history. Use to answer "Why".`,
845
- inputSchema: {
846
- type: 'object' as const,
847
- properties: {
848
- projectId: {
849
- type: 'string',
850
- description: 'The UUID of the Rigstate project'
851
- },
852
- query: {
853
- type: 'string',
854
- description: 'Search terms'
855
- },
856
- limit: {
857
- type: 'number',
858
- description: 'Max results (default 5)'
859
- }
860
- },
861
- required: ['projectId', 'query']
862
- }
863
- },
864
- {
865
- name: 'fetch_package_health',
866
- description: `Astrid's Tool: Evaluates an NPM package's health and maturity.
867
- Returns a scorecard including downloads, maintenance status, and recommended usage.`,
868
- inputSchema: {
869
- type: 'object' as const,
870
- properties: {
871
- packageName: {
872
- type: 'string',
873
- description: 'The NPM package name (e.g. "react")'
874
- }
875
- },
876
- required: ['packageName']
877
- }
878
- },
879
- // =========================================================================
880
- // ACTIVE MEMORY & PLANNING (MAJA & KINE)
881
- // =========================================================================
882
- {
883
- name: 'save_to_project_brain',
884
- description: `Maja's Tool: Persists architectural decisions (ADRs) and important notes.
885
- Use when the user makes a decision or you learn something critical.`,
886
- inputSchema: {
887
- type: 'object' as const,
888
- properties: {
889
- projectId: {
890
- type: 'string',
891
- description: 'The UUID of the Rigstate project'
892
- },
893
- title: {
894
- type: 'string',
895
- description: 'Title of the memory/decision'
896
- },
897
- content: {
898
- type: 'string',
899
- description: 'Full content/markdown'
900
- },
901
- category: {
902
- type: 'string',
903
- enum: ['DECISION', 'ARCHITECTURE', 'NOTE', 'LESSON_LEARNED'],
904
- description: 'Category (default: NOTE)'
905
- },
906
- tags: {
907
- type: 'array',
908
- items: { type: 'string' },
909
- description: 'Tags for searching'
910
- }
911
- },
912
- required: ['projectId', 'title', 'content']
913
- }
914
- },
915
- {
916
- name: 'update_roadmap_status',
917
- description: `Kine's Tool: Updates the status of a roadmap task.
918
- Use when a task moves from TODO -> IN_PROGRESS -> COMPLETED.`,
919
- inputSchema: {
920
- type: 'object' as const,
921
- properties: {
922
- projectId: {
923
- type: 'string',
924
- description: 'The UUID of the Rigstate project'
925
- },
926
- chunkId: {
927
- type: 'string',
928
- description: 'The ID of the roadmap chunk'
929
- },
930
- status: {
931
- type: 'string',
932
- enum: ['TODO', 'IN_PROGRESS', 'COMPLETED'],
933
- description: 'New status'
934
- }
935
- },
936
- required: ['projectId', 'chunkId', 'status']
937
- }
938
- },
939
- {
940
- name: 'add_roadmap_chunk',
941
- description: `Kine's Tool: Decomposes features into smaller roadmap steps.
942
- Use when a high-level goal needs to be broken down into actionable tasks.`,
943
- inputSchema: {
944
- type: 'object' as const,
945
- properties: {
946
- projectId: {
947
- type: 'string',
948
- description: 'The UUID of the Rigstate project'
949
- },
950
- title: {
951
- type: 'string',
952
- description: 'Task title'
953
- },
954
- description: {
955
- type: 'string',
956
- description: 'Optional details'
957
- },
958
- featureId: {
959
- type: 'string',
960
- description: 'Optional context (Sprint focus)'
961
- },
962
- priority: {
963
- type: 'string',
964
- enum: ['LOW', 'MEDIUM', 'HIGH'],
965
- description: 'Priority (default: MEDIUM)'
966
- }
967
- },
968
- required: ['projectId', 'title']
969
- }
970
- },
971
- // =========================================================================
972
- // UI/UX & RESEARCH (LINUS & ASTRID)
973
- // =========================================================================
974
- {
975
- name: 'analyze_ui_component',
976
- description: `Linus's Tool: Analyzes a UI file for design flaws, accessibility issues, and hardcoded values.
977
- Returns a checklist of recommended fixes.`,
978
- inputSchema: {
979
- type: 'object' as const,
980
- properties: {
981
- filePath: { type: 'string', description: 'Absolute path to the file' }
982
- },
983
- required: ['filePath']
984
- }
985
- },
986
- {
987
- name: 'apply_design_system',
988
- description: `Linus's Tool: Automatically enforces design tokens by replacing hardcoded values.
989
- Use to cleanup 'dirty' code (e.g. text-[#000] -> text-foreground).`,
990
- inputSchema: {
991
- type: 'object' as const,
992
- properties: {
993
- filePath: { type: 'string', description: 'Absolute path to the file' }
994
- },
995
- required: ['filePath']
996
- }
997
- },
998
- {
999
- name: 'fetch_ui_library_docs',
1000
- description: `Astrid's Tool: Fetches reference code for a UI component (e.g. Shadcn/UI).
1001
- Use this to see how a Button or Card is implemented locally before suggesting changes.`,
1002
- inputSchema: {
1003
- type: 'object' as const,
1004
- properties: {
1005
- componentName: { type: 'string', description: 'e.g. "button", "card"' },
1006
- library: { type: 'string', enum: ['shadcn', 'lucide'], default: 'shadcn' }
1007
- },
1008
- required: ['componentName']
1009
- }
1010
- },
1011
- // =========================================================================
1012
- // PENDING TASKS (IDE Integration)
1013
- // =========================================================================
1014
- {
1015
- name: 'get_pending_tasks',
1016
- description: `Fetches tasks that have been APPROVED by the user in the dashboard and are ready for execution.
1017
- Use this to poll for work that the user wants you to perform. Returns objective, technical context, and constraints.`,
1018
- inputSchema: {
1019
- type: 'object' as const,
1020
- properties: {
1021
- projectId: {
1022
- type: 'string',
1023
- description: 'The UUID of the Rigstate project'
1024
- }
1025
- },
1026
- required: ['projectId']
1027
- }
1028
- },
1029
- {
1030
- name: 'update_task_status',
1031
- description: `Updates the status of a pending task. Use EXECUTING when starting work, COMPLETED when done.
1032
- IMPORTANT: execution_summary is REQUIRED when setting status to COMPLETED.`,
1033
- inputSchema: {
1034
- type: 'object' as const,
1035
- properties: {
1036
- projectId: {
1037
- type: 'string',
1038
- description: 'The UUID of the Rigstate project'
1039
- },
1040
- taskId: {
1041
- type: 'string',
1042
- description: 'The UUID of the task to update (from get_pending_tasks)'
1043
- },
1044
- status: {
1045
- type: 'string',
1046
- enum: ['EXECUTING', 'COMPLETED', 'FAILED'],
1047
- description: 'New status: EXECUTING (starting), COMPLETED (done), or FAILED (error)'
1048
- },
1049
- executionSummary: {
1050
- type: 'string',
1051
- description: 'Summary of actions taken. REQUIRED when status is COMPLETED.'
1052
- }
1053
- },
1054
- required: ['projectId', 'taskId', 'status']
1055
- }
1056
- }
1057
- ];
1058
-
1059
-
1060
- // =============================================================================
1061
- // RESOURCES (LOCAL FILES)
1062
- // =============================================================================
1063
-
1064
- const RESOURCES = [
1065
- {
1066
- uri: 'rigstate://project_morals',
1067
- name: 'project_morals',
1068
- description: 'Local project rules and standards from .rigstate/rules.md or similar files',
1069
- mimeType: 'text/markdown'
1070
- }
1071
- ];
1072
-
1073
- // =============================================================================
1074
- // SERVER IMPLEMENTATION
1075
- // =============================================================================
1076
-
1077
- async function validateWorkerKey(): Promise<boolean> {
1078
- try {
1079
- const rootKey = await fs.readFile(path.join(process.cwd(), 'worker.key'), 'utf-8').catch(() => null);
1080
- if (rootKey && rootKey.trim().length > 0) return true;
1081
-
1082
- const globalPath = path.join(os.homedir(), '.rigstate', 'auth.json');
1083
- const globalContent = await fs.readFile(globalPath, 'utf-8').catch(() => null);
1084
- if (globalContent) {
1085
- const auth = JSON.parse(globalContent);
1086
- if (auth.worker_token) return true;
1087
- }
1088
- } catch (e) { }
1089
- return false;
1090
- }
1091
-
1092
- async function fetchGovernancePolicy(apiUrl: string, apiKey: string, projectId: string) {
1093
- try {
1094
- const response = await fetch(`${apiUrl}/api/agent/policy?project_id=${projectId}&agent_name=frank`, {
1095
- headers: { 'Authorization': `Bearer ${apiKey}` }
1096
- });
1097
- if (response.ok) return await response.json();
1098
- } catch (e) { }
1099
- return null;
1100
- }
2
+ import { authenticateApiKey } from './lib/supabase.js';
3
+ import { createMcpServer } from './server/factory.js';
4
+ import { setupToolHandlers } from './server/core.js';
5
+ import { startFrankWatcher } from './server/telemetry.js';
1101
6
 
1102
7
  async function main() {
1103
- // Validate API key on startup
1104
8
  const apiKey = process.env.RIGSTATE_API_KEY;
1105
9
 
1106
10
  if (!apiKey) {
1107
- console.error('❌ Error: RIGSTATE_API_KEY environment variable is required.');
1108
- console.error('');
1109
- console.error('Get your API key from: https://rigstate.dev/settings/api-keys');
1110
- console.error('Then set it: RIGSTATE_API_KEY=sk_rigstate_xxx npx @rigstate/mcp');
11
+ console.error('❌ RIGSTATE_API_KEY environment variable is required');
1111
12
  process.exit(1);
1112
13
  }
1113
14
 
1114
- // Governance Check (Rigstate v2.8)
1115
- const manifestPath = path.join(process.cwd(), '.rigstate');
1116
- const manifestContent = await fs.readFile(manifestPath, 'utf-8').catch(() => null);
1117
- if (manifestContent) {
1118
- try {
1119
- const manifest = JSON.parse(manifestContent);
1120
- if (manifest.project_id && manifest.api_url) {
1121
- console.error(`🔒 Governance: Fetching policy for Project ${manifest.project_id}...`);
1122
- const policy: any = await fetchGovernancePolicy(manifest.api_url, apiKey, manifest.project_id);
1123
- if (policy) {
1124
- if (policy.allow_file_write === false) {
1125
- process.env.RIGSTATE_GOVERNANCE_WRITE = 'FALSE';
1126
- console.error(`🔒 Governance: Write Access DISABLED via Cloud Policy.`);
1127
- } else {
1128
- process.env.RIGSTATE_GOVERNANCE_WRITE = 'TRUE';
1129
- console.error(`🔓 Governance: Write Access Enabled (Subject to Zero-Trust Key).`);
1130
- }
1131
- }
1132
- }
1133
- } catch (e) { }
1134
- }
1135
-
1136
- // Authenticate
15
+ // 1. Authenticate
1137
16
  const authResult = await authenticateApiKey(apiKey);
1138
-
1139
17
  if (!authResult.success || !authResult.context) {
1140
18
  console.error(`❌ Authentication failed: ${authResult.error}`);
1141
19
  process.exit(1);
1142
20
  }
1143
21
 
1144
- const authContext: AuthContext = authResult.context;
22
+ const { supabase, userId } = authResult.context;
1145
23
 
1146
- // Start Frank Watcher (Auto-Agent)
1147
- startFrankWatcher(authContext.supabase, authContext.userId);
24
+ // 2. Start Telemetry (Frank Watcher)
25
+ startFrankWatcher(supabase, userId);
1148
26
 
1149
- // Create MCP server
1150
- const server = new Server(
1151
- {
1152
- name: SERVER_NAME,
1153
- version: SERVER_VERSION,
1154
- },
1155
- {
1156
- capabilities: {
1157
- tools: {},
1158
- resources: {},
1159
- },
1160
- }
1161
- );
27
+ // 3. Create Server (Factory)
28
+ const server = createMcpServer();
1162
29
 
1163
- // Handle list_tools request
1164
- server.setRequestHandler(ListToolsRequestSchema, async () => {
1165
- return {
1166
- tools: [
1167
- ...TOOLS,
1168
- ...registry.getTools()
1169
- ]
1170
- };
1171
- });
30
+ // 4. Setup Handlers (Core)
31
+ setupToolHandlers(server, { supabase, userId });
1172
32
 
1173
- // Handle list_resources request
1174
- server.setRequestHandler(ListResourcesRequestSchema, async () => {
1175
- return { resources: RESOURCES };
1176
- });
1177
-
1178
- // Handle read_resource request
1179
- server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
1180
- const { uri } = request.params;
1181
-
1182
- if (uri === 'rigstate://project_morals') {
1183
- const morals = getProjectMorals();
1184
- return {
1185
- contents: [
1186
- {
1187
- uri,
1188
- mimeType: 'text/markdown',
1189
- text: morals.formatted
1190
- }
1191
- ]
1192
- };
1193
- }
1194
-
1195
- throw new McpError(ErrorCode.InvalidParams, `Unknown resource: ${uri}`);
1196
- });
1197
-
1198
- // Handle call_tool request
1199
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
1200
- try {
1201
- const { name } = request.params;
1202
- const args = request.params.arguments as any;
1203
-
1204
- // 0. Check Migration Registry
1205
- // If tool exists in registry, it runs. If arguments invalid, it throws (good).
1206
- try {
1207
- return await registry.callTool(name, args, {
1208
- supabase: authContext.supabase,
1209
- userId: authContext.userId
1210
- });
1211
- } catch (e: any) {
1212
- if (e.message && e.message.includes(`Tool '${name}' not found`)) {
1213
- // Tool not in registry, proceed to legacy switch
1214
- } else {
1215
- // Tool was in registry but failed (e.g. validation or runtime error)
1216
- throw new McpError(ErrorCode.InvalidParams, e.message);
1217
- }
1218
- }
1219
-
1220
- // GUARDIAN LOCK & WORKER AUTH
1221
- const WRITE_TOOLS = [
1222
- 'write_to_file', 'replace_file_content', 'run_command',
1223
- 'save_decision', 'submit_idea', 'update_roadmap',
1224
- 'save_to_project_brain', 'add_roadmap_chunk',
1225
- 'update_roadmap_status', 'update_task_status'
1226
- ];
1227
-
1228
- if (WRITE_TOOLS.includes(name)) {
1229
- // Governance Policy Check (Rigstate v2.8)
1230
- if (process.env.RIGSTATE_GOVERNANCE_WRITE === 'FALSE') {
1231
- throw new McpError(ErrorCode.InvalidParams, '[GOVERNANCE_BLOCK]: Write access disabled via Rigstate Cloud.');
1232
- }
1233
-
1234
- // Universal Zero-Trust Lock (Rigstate v2.7)
1235
- const isWorker = await validateWorkerKey();
1236
-
1237
- if (!isWorker) {
1238
- throw new McpError(ErrorCode.InvalidParams, '[RIGSTATE_SECURITY_BLOCK]: Unauthorized attempt. No active Worker context detected.');
1239
- }
1240
- }
1241
-
1242
- switch (name) {
1243
- case 'get_project_context': {
1244
- const parsed = GetProjectContextInputSchema.parse(args);
1245
- const result = await getProjectContext(
1246
- authContext.supabase,
1247
- authContext.userId,
1248
- parsed.projectId
1249
- );
1250
- return {
1251
- content: [
1252
- {
1253
- type: 'text',
1254
- text: result.summary
1255
- }
1256
- ]
1257
- };
1258
- }
1259
-
1260
- case 'query_brain': {
1261
- const parsed = QueryBrainInputSchema.parse(args);
1262
- const result = await queryBrain(
1263
- authContext.supabase,
1264
- authContext.userId,
1265
- parsed.projectId,
1266
- parsed.query,
1267
- parsed.limit,
1268
- parsed.threshold
1269
- );
1270
- return {
1271
- content: [
1272
- {
1273
- type: 'text',
1274
- text: result.formatted
1275
- }
1276
- ]
1277
- };
1278
- }
1279
-
1280
- case 'get_latest_decisions': {
1281
- const parsed = GetLatestDecisionsInputSchema.parse(args);
1282
- const result = await getLatestDecisions(
1283
- authContext.supabase,
1284
- authContext.userId,
1285
- parsed.projectId,
1286
- parsed.limit
1287
- );
1288
- return {
1289
- content: [
1290
- {
1291
- type: 'text',
1292
- text: result.summary
1293
- }
1294
- ]
1295
- };
1296
- }
1297
-
1298
- case 'list_roadmap_tasks': {
1299
- const parsed = ListRoadmapTasksInputSchema.parse(args);
1300
- const result = await listRoadmapTasks(
1301
- authContext.supabase,
1302
- authContext.userId,
1303
- parsed.projectId
1304
- );
1305
- return {
1306
- content: [
1307
- {
1308
- type: 'text',
1309
- text: result.formatted
1310
- }
1311
- ]
1312
- };
1313
- }
1314
-
1315
-
1316
- case 'check_agent_bridge': {
1317
- const parsed = CheckAgentBridgeInputSchema.parse(args);
1318
- const result = await checkAgentBridge(
1319
- authContext.supabase,
1320
- parsed.projectId,
1321
- parsed.action,
1322
- parsed.bridgeId,
1323
- parsed.status,
1324
- parsed.summary,
1325
- parsed.proposal,
1326
- // @ts-ignore - execution_summary is a new optional prop
1327
- parsed.execution_summary
1328
- );
1329
- return {
1330
- content: [
1331
- {
1332
- type: 'text',
1333
- text: JSON.stringify(result, null, 2)
1334
- }
1335
- ]
1336
- };
1337
- }
1338
-
1339
-
1340
- case 'check_rules_sync': {
1341
- const parsed = CheckRulesSyncInputSchema.parse(args);
1342
- const result = await checkRulesSync(
1343
- authContext.supabase,
1344
- parsed.projectId,
1345
- parsed.currentRulesContent
1346
- );
1347
- return {
1348
- content: [
1349
- {
1350
- type: 'text',
1351
- text: JSON.stringify(result, null, 2)
1352
- }
1353
- ]
1354
- };
1355
- }
1356
-
1357
- case 'get_next_roadmap_step': {
1358
- const parsed = GetNextRoadmapStepInputSchema.parse(args);
1359
- const result = await getNextRoadmapStep(
1360
- authContext.supabase,
1361
- parsed.projectId,
1362
- parsed.currentStepId
1363
- );
1364
- return {
1365
- content: [
1366
- {
1367
- type: 'text',
1368
- text: JSON.stringify(result, null, 2)
1369
- }
1370
- ]
1371
- };
1372
- }
1373
-
1374
- case 'complete_roadmap_task': {
1375
- const args = request.params.arguments as any;
1376
- if (!args.projectId || !args.summary) {
1377
- throw new Error('projectId and summary are required');
1378
- }
1379
- const result = await completeRoadmapTask(
1380
- authContext.supabase,
1381
- args.projectId,
1382
- args.summary,
1383
- args.taskId,
1384
- args.gitDiff
1385
- );
1386
- return {
1387
- content: [{
1388
- type: 'text',
1389
- text: result.message
1390
- }]
1391
- };
1392
- }
1393
-
1394
- case 'generate_professional_pdf': {
1395
- const parsed = GenerateProfessionalPDFInputSchema.parse(args);
1396
- const result = await generateProfessionalPdf(
1397
- authContext.supabase,
1398
- authContext.userId,
1399
- parsed.projectId,
1400
- parsed.reportType as any
1401
- );
1402
- return {
1403
- content: [
1404
- {
1405
- type: 'text',
1406
- text: JSON.stringify(result, null, 2)
1407
- }
1408
- ]
1409
- };
1410
- }
1411
-
1412
- case 'get_agent_status': {
1413
- return {
1414
- content: [
1415
- {
1416
- type: 'text',
1417
- text: JSON.stringify(watcherState, null, 2)
1418
- }
1419
- ]
1420
- };
1421
- }
1422
-
1423
- // =============================================================
1424
- // BRYNJAR - THE LIBRARIAN (Archaeological Operations)
1425
- // =============================================================
1426
-
1427
- case 'archaeological_scan': {
1428
- const parsed = ArchaeologicalScanInputSchema.parse(args);
1429
- const result = await performArchaeologicalScan(
1430
- authContext.supabase,
1431
- parsed.projectId,
1432
- parsed.gitLog,
1433
- parsed.fileTree
1434
- );
1435
- return {
1436
- content: [
1437
- {
1438
- type: 'text',
1439
- text: JSON.stringify(result, null, 2)
1440
- }
1441
- ]
1442
- };
1443
- }
1444
-
1445
- case 'import_ghost_features': {
1446
- const parsed = ImportGhostFeaturesInputSchema.parse(args);
1447
- const result = await importGhostFeatures(
1448
- authContext.supabase,
1449
- parsed.projectId,
1450
- parsed.features
1451
- );
1452
- return {
1453
- content: [
1454
- {
1455
- type: 'text',
1456
- text: JSON.stringify(result, null, 2)
1457
- }
1458
- ]
1459
- };
1460
- }
1461
-
1462
- // =============================================================
1463
- // WRITE OPERATIONS (DEPRECATED - READ ONLY MODE)
1464
- // =============================================================
1465
-
1466
- case 'save_decision': {
1467
- return {
1468
- content: [{
1469
- type: 'text',
1470
- text: '🚫 [READ-ONLY MODE] save_decision is deprecated in MCP. Please use the Rigstate Dashboard to record decisions.'
1471
- }],
1472
- isError: true
1473
- };
1474
- }
1475
-
1476
- case 'submit_idea': {
1477
- return {
1478
- content: [{
1479
- type: 'text',
1480
- text: '🚫 [READ-ONLY MODE] submit_idea is deprecated in MCP. Please use the Rigstate Dashboard (Idea Lab) to submit new ideas.'
1481
- }],
1482
- isError: true
1483
- };
1484
- }
1485
-
1486
- case 'update_roadmap': {
1487
- return {
1488
- content: [{
1489
- type: 'text',
1490
- text: '🚫 [READ-ONLY MODE] update_roadmap is deprecated in MCP. Use the Rigstate CLI ("rigstate sync") or the Dashboard to update tasks.'
1491
- }],
1492
- isError: true
1493
- };
1494
- }
1495
-
1496
- case 'run_architecture_audit': {
1497
- return {
1498
- content: [{
1499
- type: 'text',
1500
- text: '🚫 [READ-ONLY MODE] run_architecture_audit in MCP is deprecated. Use the Rigstate CLI command "rigstate check" for deterministic architectural validation.'
1501
- }],
1502
- isError: true
1503
- };
1504
- }
1505
-
1506
- // =============================================================
1507
- // TEACHER MODE TOOLS
1508
- // =============================================================
1509
-
1510
- case 'refine_logic': {
1511
- const parsed = RefineLogicInputSchema.parse(args);
1512
- const result = await refineLogic(
1513
- authContext.supabase,
1514
- authContext.userId,
1515
- parsed.projectId,
1516
- parsed.originalReasoning,
1517
- parsed.userCorrection,
1518
- parsed.scope || 'project'
1519
- );
1520
- return {
1521
- content: [
1522
- {
1523
- type: 'text',
1524
- text: `${result.message}\n\nTrace ID: ${result.traceId}`
1525
- }
1526
- ]
1527
- };
1528
- }
1529
-
1530
- case 'get_learned_instructions': {
1531
- const parsed = GetLearnedInstructionsInputSchema.parse(args);
1532
- const result = await getLearnedInstructions(
1533
- authContext.supabase,
1534
- authContext.userId,
1535
- parsed.projectId
1536
- );
1537
- return {
1538
- content: [
1539
- {
1540
- type: 'text',
1541
- text: result.formatted
1542
- }
1543
- ]
1544
- };
1545
- }
1546
-
1547
- case 'sync_ide_rules': {
1548
- const { projectId } = GenerateCursorRulesInputSchema.parse(request.params.arguments);
1549
- const result = await syncIdeRules(authContext.supabase, projectId);
1550
- return {
1551
- content: [{
1552
- type: 'text',
1553
- text: `FileName: ${result.fileName}\n\nContent:\n${result.content}`
1554
- }]
1555
- };
1556
- }
1557
-
1558
- default:
1559
- throw new McpError(
1560
- ErrorCode.MethodNotFound,
1561
- `Unknown tool: ${name}`
1562
- );
1563
-
1564
- // =============================================================
1565
- // STRUCTURAL SHIELD (EINAR & SVEN)
1566
- // =============================================================
1567
-
1568
- case 'analyze_dependency_graph': {
1569
- const parsed = AnalyzeDependencyGraphInputSchema.parse(args);
1570
- const result = await analyzeDependencyGraph(parsed);
1571
- return {
1572
- content: [
1573
- {
1574
- type: 'text',
1575
- text: JSON.stringify(result, null, 2)
1576
- }
1577
- ]
1578
- };
1579
- }
1580
-
1581
- case 'audit_rls_status': {
1582
- const parsed = AuditRlsStatusInputSchema.parse(args);
1583
- const result = await auditRlsStatus(authContext.supabase, parsed);
1584
- return {
1585
- content: [
1586
- {
1587
- type: 'text',
1588
- text: JSON.stringify(result, null, 2)
1589
- }
1590
- ]
1591
- };
1592
- }
1593
-
1594
- // =============================================================
1595
- // INTELLIGENCE CORE (MAJA & ASTRID)
1596
- // =============================================================
1597
-
1598
- case 'query_project_brain': {
1599
- const parsed = QueryProjectBrainInputSchema.parse(args);
1600
- const result = await queryProjectBrain(
1601
- authContext.supabase,
1602
- authContext.userId,
1603
- parsed
1604
- );
1605
- return {
1606
- content: [
1607
- {
1608
- type: 'text',
1609
- text: JSON.stringify(result, null, 2)
1610
- }
1611
- ]
1612
- };
1613
- }
1614
-
1615
- case 'fetch_package_health': {
1616
- const parsed = FetchPackageHealthInputSchema.parse(args);
1617
- const result = await fetchPackageHealth(parsed);
1618
- return {
1619
- content: [
1620
- {
1621
- type: 'text',
1622
- text: JSON.stringify(result, null, 2)
1623
- }
1624
- ]
1625
- };
1626
- }
1627
-
1628
- // =============================================================
1629
- // ACTIVE MEMORY & PLANNING (MAJA & KINE)
1630
- // =============================================================
1631
-
1632
- case 'save_to_project_brain':
1633
- case 'update_roadmap_status':
1634
- case 'add_roadmap_chunk': {
1635
- return {
1636
- content: [{
1637
- type: 'text',
1638
- text: '🚫 [READ-ONLY MODE] Planning and active memory writes are deprecated in MCP. Use the Rigstate CLI or Dashboard.'
1639
- }],
1640
- isError: true
1641
- };
1642
- }
1643
-
1644
- // =============================================================
1645
- // UI/UX & RESEARCH (LINUS & ASTRID)
1646
- // =============================================================
1647
-
1648
- case 'analyze_ui_component': {
1649
- const parsed = AnalyzeUiComponentInputSchema.parse(args);
1650
- const result = await analyzeUiComponent(parsed);
1651
- return {
1652
- content: [
1653
- {
1654
- type: 'text',
1655
- text: JSON.stringify(result, null, 2)
1656
- }
1657
- ]
1658
- };
1659
- }
1660
-
1661
- case 'apply_design_system': {
1662
- const parsed = ApplyDesignSystemInputSchema.parse(args);
1663
- const result = await applyDesignSystem(parsed);
1664
- return {
1665
- content: [
1666
- {
1667
- type: 'text',
1668
- text: JSON.stringify(result, null, 2)
1669
- }
1670
- ]
1671
- };
1672
- }
1673
-
1674
- case 'fetch_ui_library_docs': {
1675
- const parsed = FetchUiLibraryDocsInputSchema.parse(args);
1676
- const result = await fetchUiLibraryDocs(parsed);
1677
- return {
1678
- content: [
1679
- {
1680
- type: 'text',
1681
- text: JSON.stringify(result, null, 2)
1682
- }
1683
- ]
1684
- };
1685
- }
1686
-
1687
- // =========================================================
1688
- // PENDING TASKS (IDE Integration)
1689
- // =========================================================
1690
- case 'get_pending_tasks': {
1691
- const parsed = GetPendingTasksInputSchema.parse(args);
1692
- const result = await getPendingTasks(
1693
- authContext.supabase,
1694
- parsed.projectId
1695
- );
1696
- return {
1697
- content: [
1698
- {
1699
- type: 'text',
1700
- text: JSON.stringify(result, null, 2)
1701
- }
1702
- ]
1703
- };
1704
- }
1705
-
1706
- case 'update_task_status': {
1707
- const parsed = UpdateTaskStatusInputSchema.parse(args);
1708
- const result = await updateTaskStatus(
1709
- authContext.supabase,
1710
- parsed.projectId,
1711
- parsed.taskId,
1712
- parsed.status,
1713
- parsed.executionSummary
1714
- );
1715
- return {
1716
- content: [
1717
- {
1718
- type: 'text',
1719
- text: JSON.stringify(result, null, 2)
1720
- }
1721
- ]
1722
- };
1723
- }
1724
- }
1725
- } catch (error: any) {
1726
- // Handle Zod validation errors
1727
- if (error.name === 'ZodError') {
1728
- throw new McpError(
1729
- ErrorCode.InvalidParams,
1730
- `Invalid parameters: ${error.errors.map((e: any) => e.message).join(', ')}`
1731
- );
1732
- }
1733
-
1734
- // Handle access denied errors
1735
- if (error.message?.includes('access denied') || error.message?.includes('not found')) {
1736
- throw new McpError(
1737
- ErrorCode.InvalidParams,
1738
- error.message
1739
- );
1740
- }
1741
-
1742
- // Handle database constraint errors (write operations)
1743
- if (error.message?.includes('Permission denied') ||
1744
- error.message?.includes('constraint') ||
1745
- error.message?.includes('already exists')) {
1746
- throw new McpError(
1747
- ErrorCode.InvalidParams,
1748
- `Write operation failed: ${error.message}`
1749
- );
1750
- }
1751
-
1752
- // Handle write operation specific errors
1753
- if (error.message?.includes('Failed to')) {
1754
- throw new McpError(
1755
- ErrorCode.InternalError,
1756
- error.message
1757
- );
1758
- }
1759
-
1760
- // Re-throw MCP errors as-is
1761
- if (error instanceof McpError) {
1762
- throw error;
1763
- }
1764
-
1765
- // Re-throw other errors
1766
- throw new McpError(
1767
- ErrorCode.InternalError,
1768
- error.message || 'An unexpected error occurred'
1769
- );
1770
- }
1771
- });
1772
-
1773
- // Start server with stdio transport
33
+ // 5. Connect Transport
1774
34
  const transport = new StdioServerTransport();
1775
35
  await server.connect(transport);
1776
36
 
1777
- // Log to stderr so it doesn't interfere with MCP protocol
1778
- console.error(`✅ Rigstate MCP Server v${SERVER_VERSION} started`);
1779
- console.error(` User ID: ${authContext.userId}`);
1780
- console.error(` Mode: Read-Only (write ops deprecated)`);
1781
-
1782
- // DEPRECATED: Frank Watcher is now handled by CLI daemon
1783
- // Use "rigstate daemon" for background task processing
1784
- // startFrankWatcher(authContext.supabase, authContext.userId);
37
+ console.error('🛰️ Rigstate MCP Server (Evolutionary) running on stdio');
1785
38
  }
1786
39
 
1787
- // Run the server
1788
40
  main().catch((error) => {
1789
- console.error('Fatal error:', error);
41
+ console.error('FATAL ERROR:', error);
1790
42
  process.exit(1);
1791
43
  });
1792
-