@rigstate/mcp 0.4.2

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 (40) hide show
  1. package/.env.example +8 -0
  2. package/README.md +352 -0
  3. package/dist/index.d.ts +2 -0
  4. package/dist/index.js +3445 -0
  5. package/dist/index.js.map +1 -0
  6. package/package.json +43 -0
  7. package/roadmap.json +531 -0
  8. package/src/agents/the-scribe.ts +122 -0
  9. package/src/index.ts +1792 -0
  10. package/src/lib/supabase.ts +120 -0
  11. package/src/lib/tool-registry.ts +134 -0
  12. package/src/lib/types.ts +415 -0
  13. package/src/lib/utils.ts +10 -0
  14. package/src/resources/project-morals.ts +92 -0
  15. package/src/tools/arch-tools.ts +166 -0
  16. package/src/tools/archaeological-scan.ts +335 -0
  17. package/src/tools/check-agent-bridge.ts +169 -0
  18. package/src/tools/check-rules-sync.ts +85 -0
  19. package/src/tools/complete-roadmap-task.ts +96 -0
  20. package/src/tools/generate-professional-pdf.ts +232 -0
  21. package/src/tools/get-latest-decisions.ts +130 -0
  22. package/src/tools/get-next-roadmap-step.ts +76 -0
  23. package/src/tools/get-project-context.ts +163 -0
  24. package/src/tools/index.ts +17 -0
  25. package/src/tools/list-features.ts +67 -0
  26. package/src/tools/list-roadmap-tasks.ts +61 -0
  27. package/src/tools/pending-tasks.ts +228 -0
  28. package/src/tools/planning-tools.ts +123 -0
  29. package/src/tools/query-brain.ts +125 -0
  30. package/src/tools/research-tools.ts +149 -0
  31. package/src/tools/run-architecture-audit.ts +203 -0
  32. package/src/tools/save-decision.ts +77 -0
  33. package/src/tools/security-tools.ts +82 -0
  34. package/src/tools/submit-idea.ts +66 -0
  35. package/src/tools/sync-ide-rules.ts +76 -0
  36. package/src/tools/teacher-mode.ts +171 -0
  37. package/src/tools/ui-tools.ts +191 -0
  38. package/src/tools/update-roadmap.ts +105 -0
  39. package/tsconfig.json +29 -0
  40. package/tsup.config.ts +16 -0
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Rigstate MCP Server - Supabase Client
3
+ *
4
+ * Creates a Supabase client for MCP server operations.
5
+ * Uses the RIGSTATE_API_KEY to authenticate and determine the user context.
6
+ *
7
+ * Required Environment Variables:
8
+ * - RIGSTATE_API_KEY: Your API key from https://rigstate.dev/settings/api-keys
9
+ *
10
+ * Optional Environment Variables (defaults to production Rigstate):
11
+ * - RIGSTATE_SUPABASE_URL: Override Supabase URL
12
+ * - RIGSTATE_SUPABASE_ANON_KEY: Override Supabase anon key
13
+ */
14
+
15
+ import { createClient as createSupabaseClient, SupabaseClient } from '@supabase/supabase-js';
16
+ import { createHash } from 'crypto';
17
+
18
+ // Production Rigstate configuration
19
+ const PRODUCTION_SUPABASE_URL = 'https://gseblsxnfppsxbmtzcfj.supabase.co';
20
+ const PRODUCTION_SUPABASE_ANON_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImdzZWJsc3huZnBwc3hibXR6Y2ZqIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MzYxODIxNjgsImV4cCI6MjA1MTc1ODE2OH0.bltFf6_gNH-p3jH8lRY8dCRK6fmgxO8Hjp7UGx8xSxY';
21
+ const PRODUCTION_SUPABASE_SERVICE_KEY = process.env.SUPABASE_SERVICE_ROLE_KEY || '';
22
+
23
+ // Environment configuration
24
+ // Priority: Explicit MCP env > Next.js env > Production defaults
25
+ const SUPABASE_URL =
26
+ process.env.RIGSTATE_SUPABASE_URL ||
27
+ process.env.NEXT_PUBLIC_SUPABASE_URL ||
28
+ PRODUCTION_SUPABASE_URL;
29
+
30
+ const SUPABASE_ANON_KEY =
31
+ process.env.RIGSTATE_SUPABASE_ANON_KEY ||
32
+ process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY ||
33
+ PRODUCTION_SUPABASE_ANON_KEY;
34
+
35
+ const SUPABASE_SERVICE_KEY =
36
+ process.env.RIGSTATE_SUPABASE_SERVICE_KEY ||
37
+ process.env.SUPABASE_SERVICE_ROLE_KEY ||
38
+ PRODUCTION_SUPABASE_SERVICE_KEY;
39
+
40
+
41
+ export interface AuthContext {
42
+ userId: string;
43
+ apiKeyId: string;
44
+ supabase: SupabaseClient;
45
+ }
46
+
47
+ /**
48
+ * Validates the RIGSTATE_API_KEY and returns the authenticated user context.
49
+ *
50
+ * CRITICAL: API keys are stored as SHA-256 hashes in the database.
51
+ * We must hash the incoming key before lookup.
52
+ */
53
+ export async function authenticateApiKey(apiKey: string): Promise<{
54
+ success: boolean;
55
+ error?: string;
56
+ context?: AuthContext;
57
+ }> {
58
+ if (!apiKey) {
59
+ return { success: false, error: 'RIGSTATE_API_KEY is required' };
60
+ }
61
+
62
+ if (!apiKey.startsWith('sk_rigstate_')) {
63
+ return { success: false, error: 'Invalid API key format. Expected sk_rigstate_...' };
64
+ }
65
+
66
+ if (!SUPABASE_URL || !SUPABASE_ANON_KEY) {
67
+ return {
68
+ success: false,
69
+ error: 'Supabase configuration missing. Set RIGSTATE_SUPABASE_URL and RIGSTATE_SUPABASE_ANON_KEY.'
70
+ };
71
+ }
72
+
73
+ // Hash the API key with SHA-256 (same as web app)
74
+ const hashedKey = createHash('sha256').update(apiKey).digest('hex');
75
+
76
+ // Use service key if available to bypass RLS, otherwise anon key
77
+ const clientKey = SUPABASE_SERVICE_KEY || SUPABASE_ANON_KEY;
78
+ const supabase = createSupabaseClient(SUPABASE_URL, clientKey);
79
+
80
+ // Look up the API key by HASH (not plain text!)
81
+ const { data: keyData, error: keyError } = await supabase
82
+ .from('api_keys')
83
+ .select('id, user_id, project_id, organization_id, scope')
84
+ .eq('key_hash', hashedKey)
85
+ .single();
86
+
87
+ if (keyError || !keyData) {
88
+ return { success: false, error: 'Invalid or revoked API key' };
89
+ }
90
+
91
+ // Update last_used_at timestamp (fire and forget)
92
+ supabase
93
+ .from('api_keys')
94
+ .update({ last_used_at: new Date().toISOString() })
95
+ .eq('id', keyData.id)
96
+ .then();
97
+
98
+ // Create a user-scoped client for subsequent operations
99
+ const userSupabase = createSupabaseClient(SUPABASE_URL, SUPABASE_ANON_KEY);
100
+
101
+ return {
102
+ success: true,
103
+ context: {
104
+ userId: keyData.user_id,
105
+ apiKeyId: keyData.id,
106
+ supabase: userSupabase
107
+ }
108
+ };
109
+ }
110
+
111
+ /**
112
+ * Creates a Supabase client using environment variables.
113
+ * For use after authentication has been established.
114
+ */
115
+ export function createClient(): SupabaseClient {
116
+ if (!SUPABASE_URL || !SUPABASE_ANON_KEY) {
117
+ throw new Error('Supabase configuration missing. Set RIGSTATE_SUPABASE_URL and RIGSTATE_SUPABASE_ANON_KEY.');
118
+ }
119
+ return createSupabaseClient(SUPABASE_URL, SUPABASE_ANON_KEY);
120
+ }
@@ -0,0 +1,134 @@
1
+ import { z } from 'zod';
2
+ import { SupabaseClient } from '@supabase/supabase-js';
3
+ import { Tool } from '@modelcontextprotocol/sdk/types.js';
4
+
5
+ /**
6
+ * Context provided to every tool execution.
7
+ * Contains authentication and database access.
8
+ */
9
+ export interface ToolContext {
10
+ supabase: SupabaseClient;
11
+ userId: string;
12
+ }
13
+
14
+ /**
15
+ * A generic definition of a Tool.
16
+ * T corresponds to the Zod schema for input validation.
17
+ */
18
+ export interface ToolDefinition<T extends z.ZodType> {
19
+ /** The unique name of the tool (e.g., 'list_features') */
20
+ name: string;
21
+ /** A helpful description for the AI */
22
+ description: string;
23
+ /** The Zod schema for input validation */
24
+ schema: T;
25
+ /** The function that executes the tool logic */
26
+ handler: (args: z.infer<T>, context: ToolContext) => Promise<{ content: Array<{ type: string; text: string }> }>;
27
+ }
28
+
29
+ /**
30
+ * The central registry for all MCP tools.
31
+ * Handles registration, listing, and execution.
32
+ */
33
+ class ToolRegistry {
34
+ private tools: Map<string, ToolDefinition<any>> = new Map();
35
+
36
+ /**
37
+ * Registers a tool logic with the system.
38
+ */
39
+ register<T extends z.ZodType>(tool: ToolDefinition<T>) {
40
+ if (this.tools.has(tool.name)) {
41
+ console.warn(`Tool '${tool.name}' is already registered. Overwriting.`);
42
+ }
43
+ this.tools.set(tool.name, tool);
44
+ }
45
+
46
+ /**
47
+ * Returns the list of tools formatted for the MCP 'ListTools' request.
48
+ * Converts Zod schemas to JSON Schemas implicitly via manual type mapping or libraries.
49
+ * For now, we manually map 'inputSchema' as 'object' in the index.ts,
50
+ * but here we prepare the definitions.
51
+ */
52
+ getTools(): Tool[] {
53
+ return Array.from(this.tools.values()).map(tool => {
54
+ // Very basic Zod -> JSON Schema mapping.
55
+ // In a real robust system, use 'zod-to-json-schema'.
56
+ // Here we rely on the tool definer to keep it simple or we expand later.
57
+ // For now, we assume standard object schema.
58
+
59
+ // To avoid complex zod-to-json-schema dependency issues for now,
60
+ // we will construct a basic JSON schema representation
61
+ // from the Zod shape if possible, or expect the tool to provide it?
62
+ // Let's rely on zod-to-json-schema if installed, or do a simple conversion.
63
+ // Actually, let's keep it simple: We just return the Tool interface required by MCP SDK.
64
+
65
+ // Hack for MVP: We assume the top level is always an object with properties.
66
+ // We can improve this reflection later.
67
+
68
+ return {
69
+ name: tool.name,
70
+ description: tool.description,
71
+ inputSchema: this.zodToJsonSchema(tool.schema)
72
+ };
73
+ });
74
+ }
75
+
76
+ /**
77
+ * Executes a tool by name.
78
+ * Validates input against schema before execution.
79
+ */
80
+ async callTool(name: string, args: any, context: ToolContext) {
81
+ const tool = this.tools.get(name);
82
+ if (!tool) {
83
+ throw new Error(`Tool '${name}' not found.`);
84
+ }
85
+
86
+ const parseResult = tool.schema.safeParse(args);
87
+ if (!parseResult.success) {
88
+ throw new Error(`Invalid arguments for tool '${name}': ${parseResult.error.message}`);
89
+ }
90
+
91
+ return await tool.handler(parseResult.data, context);
92
+ }
93
+
94
+ /**
95
+ * Basic helper to convert Zod Object to JSON Schema.
96
+ * Note: This is a simplified version. For complex types, use 'zod-to-json-schema'.
97
+ */
98
+ private zodToJsonSchema(schema: z.ZodType): any {
99
+ if (schema instanceof z.ZodObject) {
100
+ const shape = schema.shape;
101
+ const properties: Record<string, any> = {};
102
+ const required: string[] = [];
103
+
104
+ for (const key in shape) {
105
+ const field = shape[key];
106
+ // Accessing description if available in Zod (requires extended Zod or casting)
107
+ // Basic type mapping:
108
+ let type = 'string';
109
+ let description = field.description; // Leverages Zod's .describe()
110
+
111
+ if (field instanceof z.ZodNumber) type = 'number';
112
+ if (field instanceof z.ZodBoolean) type = 'boolean';
113
+ if (field instanceof z.ZodArray) type = 'array';
114
+
115
+ // Determine if optional
116
+ if (!field.isOptional()) {
117
+ required.push(key);
118
+ }
119
+
120
+ properties[key] = { type, description };
121
+ }
122
+
123
+ return {
124
+ type: 'object',
125
+ properties,
126
+ required
127
+ };
128
+ }
129
+ // Fallback or other types
130
+ return { type: 'object', properties: {} };
131
+ }
132
+ }
133
+
134
+ export const registry = new ToolRegistry();
@@ -0,0 +1,415 @@
1
+ /**
2
+ * Rigstate MCP Server - Types
3
+ *
4
+ * Shared type definitions for the MCP server tools.
5
+ */
6
+
7
+ import { z } from 'zod';
8
+
9
+ // =============================================================================
10
+ // PROJECT CONTEXT TYPES
11
+ // =============================================================================
12
+
13
+ export interface ProjectInfo {
14
+ id: string;
15
+ name: string;
16
+ description: string | null;
17
+ projectType: string | null;
18
+ createdAt: string;
19
+ lastIndexedAt: string | null;
20
+ }
21
+
22
+ export interface TechStackInfo {
23
+ framework: string | null;
24
+ orm: string | null;
25
+ database: string | null;
26
+ keyLibraries: string[];
27
+ topFolders: string[];
28
+ }
29
+
30
+ export interface ProjectContextResponse {
31
+ project: ProjectInfo;
32
+ techStack: TechStackInfo;
33
+ summary: string;
34
+ }
35
+
36
+ // =============================================================================
37
+ // BRAIN/MEMORY TYPES
38
+ // =============================================================================
39
+
40
+ export interface MemoryRecord {
41
+ id: string;
42
+ content: string;
43
+ category: string;
44
+ tags: string[];
45
+ similarity?: number;
46
+ netVotes?: number;
47
+ createdAt: string;
48
+ }
49
+
50
+ export interface BrainQueryResponse {
51
+ query: string;
52
+ memories: MemoryRecord[];
53
+ formatted: string;
54
+ }
55
+
56
+ // =============================================================================
57
+ // COUNCIL/DECISIONS TYPES
58
+ // =============================================================================
59
+
60
+ export interface CouncilSession {
61
+ id: string;
62
+ projectId: string;
63
+ recruitedAgents: string[];
64
+ feedbackSummary: Array<{
65
+ agentName: string;
66
+ emoji: string;
67
+ critiques: string[];
68
+ approved: boolean;
69
+ }>;
70
+ durationMs: number | null;
71
+ sprintsCount: number | null;
72
+ tasksCount: number | null;
73
+ createdAt: string;
74
+ }
75
+
76
+ export interface RoadmapStep {
77
+ id: string;
78
+ stepNumber: number;
79
+ title: string;
80
+ status: string;
81
+ sprintFocus: string | null;
82
+ }
83
+
84
+ export interface DecisionsResponse {
85
+ sessions: CouncilSession[];
86
+ activeRoadmapStep: RoadmapStep | null;
87
+ summary: string;
88
+ }
89
+
90
+ // =============================================================================
91
+ // ZOD SCHEMAS FOR TOOL INPUTS
92
+ // =============================================================================
93
+
94
+ export const QueryBrainInputSchema = z.object({
95
+ projectId: z.string().uuid('Invalid project ID'),
96
+ query: z.string().min(1, 'Query is required'),
97
+ limit: z.number().min(1).max(20).optional().default(8),
98
+ threshold: z.number().min(0).max(1).optional().default(0.5)
99
+ });
100
+
101
+ export const GetProjectContextInputSchema = z.object({
102
+ projectId: z.string().uuid('Invalid project ID')
103
+ });
104
+
105
+ export const GetLatestDecisionsInputSchema = z.object({
106
+ projectId: z.string().uuid('Invalid project ID'),
107
+ limit: z.number().min(1).max(10).optional().default(5)
108
+ });
109
+
110
+ // =============================================================================
111
+ // WRITE OPERATION SCHEMAS
112
+ // =============================================================================
113
+
114
+ export const SaveDecisionInputSchema = z.object({
115
+ projectId: z.string().uuid('Invalid project ID'),
116
+ title: z.string().min(1, 'Title is required').max(200, 'Title too long'),
117
+ decision: z.string().min(1, 'Decision content is required'),
118
+ rationale: z.string().optional(),
119
+ category: z.enum(['decision', 'architecture', 'constraint', 'tech_stack', 'design_rule']).optional().default('decision'),
120
+ tags: z.array(z.string()).optional().default([])
121
+ });
122
+
123
+ export const SubmitIdeaInputSchema = z.object({
124
+ projectId: z.string().uuid('Invalid project ID'),
125
+ title: z.string().min(1, 'Title is required').max(200, 'Title too long'),
126
+ description: z.string().min(1, 'Description is required'),
127
+ category: z.enum(['feature', 'improvement', 'experiment', 'pivot']).optional().default('feature'),
128
+ tags: z.array(z.string()).optional().default([])
129
+ });
130
+
131
+ export const UpdateRoadmapInputSchema = z.object({
132
+ projectId: z.string().uuid('Invalid project ID'),
133
+ chunkId: z.string().uuid('Invalid chunk ID').optional(),
134
+ title: z.string().optional(),
135
+ status: z.enum(['LOCKED', 'ACTIVE', 'COMPLETED'])
136
+ });
137
+
138
+ export const RunArchitectureAuditInputSchema = z.object({
139
+ projectId: z.string().uuid('Invalid project ID'),
140
+ filePath: z.string().min(1, 'File path is required'),
141
+ content: z.string().min(1, 'Content is required')
142
+ });
143
+
144
+ export const ListRoadmapTasksInputSchema = z.object({
145
+ projectId: z.string().uuid('Invalid project ID')
146
+ });
147
+
148
+
149
+
150
+ // =============================================================================
151
+ // TEACHER MODE SCHEMAS
152
+ // =============================================================================
153
+
154
+ export const RefineLogicInputSchema = z.object({
155
+ projectId: z.string().uuid('Invalid project ID'),
156
+ originalReasoning: z.string().min(1, 'Original reasoning is required'),
157
+ userCorrection: z.string().min(1, 'User correction is required'),
158
+ scope: z.enum(['project', 'global']).optional().default('project')
159
+ });
160
+
161
+ export const GetLearnedInstructionsInputSchema = z.object({
162
+ projectId: z.string().uuid('Invalid project ID').optional()
163
+ });
164
+
165
+ // =============================================================================
166
+ // RESPONSE TYPES
167
+ // =============================================================================
168
+
169
+ export interface SaveDecisionResponse {
170
+ success: boolean;
171
+ memoryId: string;
172
+ message: string;
173
+ }
174
+
175
+ export interface SubmitIdeaResponse {
176
+ success: boolean;
177
+ ideaId: string;
178
+ message: string;
179
+ }
180
+
181
+ export interface UpdateRoadmapResponse {
182
+ success: boolean;
183
+ chunkId: string;
184
+ previousStatus: string;
185
+ newStatus: string;
186
+ message: string;
187
+ }
188
+
189
+ export interface AuditViolation {
190
+ type: string;
191
+ severity: 'LOW' | 'MEDIUM' | 'HIGH';
192
+ title: string;
193
+ description: string;
194
+ lineNumber?: number;
195
+ recommendation: string;
196
+ }
197
+
198
+ export interface ArchitectureAuditResponse {
199
+ passed: boolean;
200
+ score: number;
201
+ violations: AuditViolation[];
202
+ summary: string;
203
+ }
204
+
205
+ export type QueryBrainInput = z.infer<typeof QueryBrainInputSchema>;
206
+ export type GetProjectContextInput = z.infer<typeof GetProjectContextInputSchema>;
207
+ export type GetLatestDecisionsInput = z.infer<typeof GetLatestDecisionsInputSchema>;
208
+ export type SaveDecisionInput = z.infer<typeof SaveDecisionInputSchema>;
209
+ export type SubmitIdeaInput = z.infer<typeof SubmitIdeaInputSchema>;
210
+ export type UpdateRoadmapInput = z.infer<typeof UpdateRoadmapInputSchema>;
211
+ export type RunArchitectureAuditInput = z.infer<typeof RunArchitectureAuditInputSchema>;
212
+ export type ListRoadmapTasksInput = z.infer<typeof ListRoadmapTasksInputSchema>;
213
+
214
+
215
+ export interface ListRoadmapTasksResponse {
216
+ tasks: Array<{
217
+ id: string;
218
+ title: string;
219
+ priority: string;
220
+ status: string;
221
+ step_number?: number;
222
+ }>;
223
+ formatted: string;
224
+ }
225
+
226
+
227
+ export const CheckAgentBridgeInputSchema = z.object({
228
+ projectId: z.string().uuid('Invalid project ID'),
229
+ action: z.enum(['check', 'update', 'submit_for_review']).optional().default('check'),
230
+ bridgeId: z.string().uuid('Invalid bridge ID').optional(),
231
+ status: z.enum(['PENDING', 'NEEDS_REVIEW', 'AWAITING_APPROVAL', 'APPROVED', 'REJECTED', 'EXECUTING', 'COMPLETED', 'FAILED']).optional(),
232
+ summary: z.string().optional(),
233
+ execution_summary: z.string().optional(),
234
+ proposal: z.string().optional()
235
+ });
236
+
237
+ export const CheckRulesSyncInputSchema = z.object({
238
+ projectId: z.string().uuid('Invalid project ID'),
239
+ currentRulesContent: z.string().optional() // Provide content to check, or tool tries to read file
240
+ });
241
+
242
+ export type CheckAgentBridgeInput = z.infer<typeof CheckAgentBridgeInputSchema>;
243
+
244
+ export interface AgentBridgeTask {
245
+ id: string;
246
+ project_id: string;
247
+ task_id: string;
248
+ status: string;
249
+ summary: string | null;
250
+ execution_summary: string | null;
251
+ proposal: string | null;
252
+ created_at: string;
253
+ completed_at: string | null;
254
+ task_title?: string;
255
+ task_description?: string;
256
+ task_content?: string;
257
+ }
258
+
259
+ export interface CheckAgentBridgeResponse {
260
+ success: boolean;
261
+ task?: AgentBridgeTask;
262
+ message: string;
263
+ }
264
+
265
+ export interface CheckRulesSyncResponse {
266
+ synced: boolean;
267
+ message: string;
268
+ shouldTriggerSync: boolean;
269
+ missingBlock?: boolean;
270
+ offlineMode?: boolean;
271
+ }
272
+
273
+ export interface RoadmapChunk {
274
+ id: string;
275
+ project_id: string;
276
+ title: string;
277
+ description: string | null;
278
+ status: string;
279
+ priority: string;
280
+ step_number: number;
281
+ prompt_content?: string;
282
+ }
283
+
284
+ // =============================================================================
285
+ // PENDING TASKS SCHEMAS (get_pending_tasks & update_task_status)
286
+ // =============================================================================
287
+
288
+ export const GetPendingTasksInputSchema = z.object({
289
+ projectId: z.string().uuid('Invalid project ID')
290
+ });
291
+
292
+ export const UpdateTaskStatusInputSchema = z.object({
293
+ projectId: z.string().uuid('Invalid project ID'),
294
+ taskId: z.string().uuid('Invalid task ID'),
295
+ status: z.enum(['EXECUTING', 'COMPLETED', 'FAILED']),
296
+ executionSummary: z.string().optional()
297
+ });
298
+
299
+ export type GetPendingTasksInput = z.infer<typeof GetPendingTasksInputSchema>;
300
+ export type UpdateTaskStatusInput = z.infer<typeof UpdateTaskStatusInputSchema>;
301
+
302
+ export const GetNextRoadmapStepInputSchema = z.object({
303
+ projectId: z.string().uuid('Invalid project ID'),
304
+ currentStepId: z.string().uuid('Invalid step ID').optional()
305
+ });
306
+
307
+ export const GenerateProfessionalPDFInputSchema = z.object({
308
+ projectId: z.string().uuid('Invalid project ID'),
309
+ reportType: z.enum(['SYSTEM_MANIFEST', 'INVESTOR_REPORT'])
310
+ });
311
+
312
+ export const ArchaeologicalScanInputSchema = z.object({
313
+ projectId: z.string().uuid('Invalid project ID'),
314
+ gitLog: z.string().describe('Git log output in format: hash:X\\ndate:X\\nauthor:X\\nmessage\\n---COMMIT---\\n...'),
315
+ fileTree: z.array(z.string()).describe('Array of file/directory paths in the repository')
316
+ });
317
+
318
+ export const ImportGhostFeaturesInputSchema = z.object({
319
+ projectId: z.string().uuid('Invalid project ID'),
320
+ features: z.array(z.object({
321
+ id: z.string(),
322
+ title: z.string(),
323
+ description: z.string(),
324
+ status: z.literal('COMPLETED'),
325
+ source: z.enum(['git', 'filesystem', 'combined']),
326
+ evidence: z.array(z.string()),
327
+ estimatedCompletionDate: z.string(),
328
+ priority: z.number()
329
+ }))
330
+ });
331
+
332
+ // =============================================================================
333
+ // STRUCTURAL SHIELD (EINAR & SVEN)
334
+ // =============================================================================
335
+
336
+ export const AnalyzeDependencyGraphInputSchema = z.object({
337
+ path: z.string().min(1, 'Path is required').default('src')
338
+ });
339
+
340
+ export const AuditRlsStatusInputSchema = z.object({
341
+ projectId: z.string().uuid('Invalid project ID').optional()
342
+ });
343
+
344
+ export type AnalyzeDependencyGraphInput = z.infer<typeof AnalyzeDependencyGraphInputSchema>;
345
+ export type AuditRlsStatusInput = z.infer<typeof AuditRlsStatusInputSchema>;
346
+
347
+ // =============================================================================
348
+ // INTELLIGENCE CORE (MAJA & ASTRID)
349
+ // =============================================================================
350
+
351
+ export const QueryProjectBrainInputSchema = QueryBrainInputSchema; // Reuse existing structure
352
+
353
+ export const FetchPackageHealthInputSchema = z.object({
354
+ packageName: z.string().min(1, 'Package name is required')
355
+ });
356
+
357
+ export type QueryProjectBrainInput = z.infer<typeof QueryProjectBrainInputSchema>;
358
+ export type FetchPackageHealthInput = z.infer<typeof FetchPackageHealthInputSchema>;
359
+
360
+ // =============================================================================
361
+ // ACTIVE MEMORY & PLANNING (MAJA & KINE)
362
+ // =============================================================================
363
+
364
+ export const SaveToProjectBrainInputSchema = z.object({
365
+ projectId: z.string().uuid('Invalid project ID'),
366
+ title: z.string().min(1, 'Title is required'),
367
+ content: z.string().min(1, 'Content is required'),
368
+ category: z.enum(['DECISION', 'ARCHITECTURE', 'NOTE', 'LESSON_LEARNED']).default('NOTE'),
369
+ tags: z.array(z.string()).optional().default([])
370
+ });
371
+
372
+ export const UpdateRoadmapStatusInputSchema = z.object({
373
+ projectId: z.string().uuid('Invalid project ID'),
374
+ chunkId: z.string().uuid('Invalid chunk ID'),
375
+ status: z.enum(['TODO', 'IN_PROGRESS', 'COMPLETED'])
376
+ });
377
+
378
+ export const AddRoadmapChunkInputSchema = z.object({
379
+ projectId: z.string().uuid('Invalid project ID'),
380
+ title: z.string().min(1, 'Title is required'),
381
+ description: z.string().optional(),
382
+ featureId: z.string().optional(),
383
+ priority: z.enum(['LOW', 'MEDIUM', 'HIGH']).default('MEDIUM')
384
+ });
385
+
386
+ export type SaveToProjectBrainInput = z.infer<typeof SaveToProjectBrainInputSchema>;
387
+ export type UpdateRoadmapStatusInput = z.infer<typeof UpdateRoadmapStatusInputSchema>;
388
+ export type AddRoadmapChunkInput = z.infer<typeof AddRoadmapChunkInputSchema>;
389
+
390
+ // =============================================================================
391
+ // UI/UX & RESEARCH (LINUS & ASTRID)
392
+ // =============================================================================
393
+
394
+ export const AnalyzeUiComponentInputSchema = z.object({
395
+ filePath: z.string().describe('Absolute path to the component file (.tsx, .css)')
396
+ });
397
+
398
+ export const ApplyDesignSystemInputSchema = z.object({
399
+ filePath: z.string().describe('Absolute path to the file to fix')
400
+ });
401
+
402
+ export const FetchUiLibraryDocsInputSchema = z.object({
403
+ componentName: z.string().describe('Name of the component (e.g. "button", "card")'),
404
+ library: z.enum(['shadcn', 'lucide']).default('shadcn')
405
+ });
406
+
407
+ export type AnalyzeUiComponentInput = z.infer<typeof AnalyzeUiComponentInputSchema>;
408
+ export type ApplyDesignSystemInput = z.infer<typeof ApplyDesignSystemInputSchema>;
409
+ export type FetchUiLibraryDocsInput = z.infer<typeof FetchUiLibraryDocsInputSchema>;
410
+
411
+ export const GenerateCursorRulesInputSchema = z.object({
412
+ projectId: z.string().uuid('Invalid project ID')
413
+ });
414
+
415
+ export type GenerateCursorRulesInput = z.infer<typeof GenerateCursorRulesInputSchema>;
@@ -0,0 +1,10 @@
1
+
2
+ /**
3
+ * MCP Utilities
4
+ */
5
+
6
+ export function extractFirstSentence(text: string): string {
7
+ if (!text) return '';
8
+ const match = text.match(/^[^.!?]*[.!?]/);
9
+ return match ? match[0].trim() : text.slice(0, 100) + '...';
10
+ }