@rigstate/mcp 0.7.5 → 0.7.6

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.
@@ -48,8 +48,19 @@ export interface UpdateTaskStatusResponse {
48
48
  */
49
49
  export async function getPendingTasks(
50
50
  supabase: SupabaseClient,
51
+ userId: string,
51
52
  projectId: string
52
53
  ): Promise<GetPendingTasksResponse> {
54
+ // 0. Verify project access
55
+ const { data: hasAccess, error: accessError } = await supabase
56
+ .rpc('check_project_access_secure', {
57
+ p_project_id: projectId,
58
+ p_user_id: userId
59
+ });
60
+
61
+ if (accessError || !hasAccess) {
62
+ throw new Error('Project not found or access denied');
63
+ }
53
64
 
54
65
  // Fetch APPROVED tasks that are ready for execution
55
66
  const { data: tasks, error } = await supabase
@@ -143,11 +154,22 @@ export async function getPendingTasks(
143
154
  */
144
155
  export async function updateTaskStatus(
145
156
  supabase: SupabaseClient,
157
+ userId: string,
146
158
  projectId: string,
147
159
  taskId: string,
148
160
  newStatus: 'EXECUTING' | 'COMPLETED' | 'FAILED',
149
161
  executionSummary?: string
150
162
  ): Promise<UpdateTaskStatusResponse> {
163
+ // 0. Verify project access
164
+ const { data: hasAccess, error: accessError } = await supabase
165
+ .rpc('check_project_access_secure', {
166
+ p_project_id: projectId,
167
+ p_user_id: userId
168
+ });
169
+
170
+ if (accessError || !hasAccess) {
171
+ throw new Error('Project not found or access denied');
172
+ }
151
173
 
152
174
  // 1. Fetch the current task state
153
175
  const { data: currentTask, error: fetchError } = await supabase
@@ -75,15 +75,14 @@ export async function queryBrain(
75
75
  limit: number = 8,
76
76
  threshold: number = 0.5
77
77
  ): Promise<BrainQueryResponse> {
78
- // First, verify project ownership
79
- const { data: project, error: projectError } = await supabase
80
- .from('projects')
81
- .select('id')
82
- .eq('id', projectId)
83
- .eq('owner_id', userId)
84
- .single();
85
-
86
- if (projectError || !project) {
78
+ // First, verify project access
79
+ const { data: hasAccess, error: accessError } = await supabase
80
+ .rpc('check_project_access_secure', {
81
+ p_project_id: projectId,
82
+ p_user_id: userId
83
+ });
84
+
85
+ if (accessError || !hasAccess) {
87
86
  throw new Error('Project not found or access denied');
88
87
  }
89
88
 
@@ -13,14 +13,15 @@ export async function queryProjectBrain(
13
13
  const { projectId, query, limit = 5 } = input;
14
14
 
15
15
  // Verify access
16
- const { data: project, error: pError } = await supabase
17
- .from('projects')
18
- .select('id')
19
- .eq('id', projectId)
20
- .eq('owner_id', userId)
21
- .single();
22
-
23
- if (pError || !project) throw new Error('Access denied or project not found');
16
+ const { data: hasAccess, error: accessError } = await supabase
17
+ .rpc('check_project_access_secure', {
18
+ p_project_id: projectId,
19
+ p_user_id: userId
20
+ });
21
+
22
+ if (accessError || !hasAccess) {
23
+ throw new Error('Access denied or project not found');
24
+ }
24
25
 
25
26
  // Simple keyword search
26
27
  const terms = query.toLowerCase().split(/\s+/).filter(t => t.length > 2);
@@ -110,15 +110,14 @@ export async function runArchitectureAudit(
110
110
  filePath: string,
111
111
  content: string
112
112
  ): Promise<ArchitectureAuditResponse> {
113
- // First, verify project ownership
114
- const { data: project, error: projectError } = await supabase
115
- .from('projects')
116
- .select('id, name')
117
- .eq('id', projectId)
118
- .eq('owner_id', userId)
119
- .single();
120
-
121
- if (projectError || !project) {
113
+ // First, verify project access
114
+ const { data: hasAccess, error: accessError } = await supabase
115
+ .rpc('check_project_access_secure', {
116
+ p_project_id: projectId,
117
+ p_user_id: userId
118
+ });
119
+
120
+ if (accessError || !hasAccess) {
122
121
  throw new Error('Project not found or access denied');
123
122
  }
124
123
 
@@ -44,18 +44,24 @@ export async function saveDecision(
44
44
  category: string = 'decision',
45
45
  tags: string[] = []
46
46
  ): Promise<SaveDecisionResponse> {
47
- // First, verify project ownership
48
- const { data: project, error: projectError } = await supabase
49
- .from('projects')
50
- .select('id, name')
51
- .eq('id', projectId)
52
- .eq('owner_id', userId)
53
- .single();
47
+ // 1. Verify project access
48
+ const { data: hasAccess, error: accessError } = await supabase
49
+ .rpc('check_project_access_secure', {
50
+ p_project_id: projectId,
51
+ p_user_id: userId
52
+ });
54
53
 
55
- if (projectError || !project) {
54
+ if (accessError || !hasAccess) {
56
55
  throw new Error('Project not found or access denied');
57
56
  }
58
57
 
58
+ // 2. Fetch project name for the response message
59
+ const { data: project } = await supabase
60
+ .from('projects')
61
+ .select('name')
62
+ .eq('id', projectId)
63
+ .single();
64
+
59
65
  // Build the full content with title, decision, and rationale
60
66
  const contentParts = [`# ${title}`, '', decision];
61
67
  if (rationale) {
@@ -98,6 +104,6 @@ export async function saveDecision(
98
104
  return {
99
105
  success: true,
100
106
  memoryId: memory.id,
101
- message: `✅ Decision "${title}" saved to project "${project.name}" with importance 9/10`
107
+ message: `✅ Decision "${title}" saved to project "${project?.name || projectId}" with importance 9/10`
102
108
  };
103
109
  }
@@ -14,7 +14,7 @@ registry.register({
14
14
  description: `Sven's Tool: Security Shield. Audits the database to ensure Row Level Security (RLS) is enforced.`,
15
15
  schema: AuditRlsStatusInputSchema,
16
16
  handler: async (args, context) => {
17
- const result = await auditRlsStatus(context.supabase, args);
17
+ const result = await auditRlsStatus(context.supabase, context.userId, args);
18
18
  return { content: [{ type: 'text', text: result.summary || 'No summary available' }] };
19
19
  }
20
20
  });
@@ -24,7 +24,7 @@ registry.register({
24
24
  description: `Frank's Tool: Security Oracle. Performs a diagnostic security audit against the Fortress Matrix.`,
25
25
  schema: AuditSecurityIntegrityInputSchema,
26
26
  handler: async (args, context) => {
27
- const result = await auditSecurityIntegrity(context.supabase, args);
27
+ const result = await auditSecurityIntegrity(context.supabase, context.userId, args);
28
28
  return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
29
29
  }
30
30
  });
@@ -35,8 +35,20 @@ registry.register({
35
35
  */
36
36
  export async function auditRlsStatus(
37
37
  supabase: SupabaseClient,
38
+ userId: string,
38
39
  input: AuditRlsStatusInput
39
40
  ) {
41
+ // 0. Verify project access
42
+ const { data: hasAccess, error: accessError } = await supabase
43
+ .rpc('check_project_access_secure', {
44
+ p_project_id: input.projectId,
45
+ p_user_id: userId
46
+ });
47
+
48
+ if (accessError || !hasAccess) {
49
+ throw new Error('Project not found or access denied');
50
+ }
51
+
40
52
  try {
41
53
  const { data, error } = await supabase.rpc('execute_sql', {
42
54
  query: `
@@ -97,8 +109,20 @@ export async function auditRlsStatus(
97
109
  */
98
110
  export async function auditSecurityIntegrity(
99
111
  supabase: SupabaseClient,
112
+ userId: string,
100
113
  input: { projectId: string, filePath: string, content: string }
101
114
  ) {
115
+ // 0. Verify project access
116
+ const { data: hasAccess, error: accessError } = await supabase
117
+ .rpc('check_project_access_secure', {
118
+ p_project_id: input.projectId,
119
+ p_user_id: userId
120
+ });
121
+
122
+ if (accessError || !hasAccess) {
123
+ throw new Error('Project not found or access denied');
124
+ }
125
+
102
126
  const violations: any[] = [];
103
127
  const { content, filePath } = input;
104
128
 
@@ -42,18 +42,24 @@ export async function submitIdea(
42
42
  category: string = 'feature',
43
43
  tags: string[] = []
44
44
  ): Promise<SubmitIdeaResponse> {
45
- // First, verify project ownership
46
- const { data: project, error: projectError } = await supabase
47
- .from('projects')
48
- .select('id, name')
49
- .eq('id', projectId)
50
- .eq('owner_id', userId)
51
- .single();
45
+ // 1. Verify project access
46
+ const { data: hasAccess, error: accessError } = await supabase
47
+ .rpc('check_project_access_secure', {
48
+ p_project_id: projectId,
49
+ p_user_id: userId
50
+ });
52
51
 
53
- if (projectError || !project) {
52
+ if (accessError || !hasAccess) {
54
53
  throw new Error('Project not found or access denied');
55
54
  }
56
55
 
56
+ // 2. Fetch project name for the response message
57
+ const { data: project } = await supabase
58
+ .from('projects')
59
+ .select('name')
60
+ .eq('id', projectId)
61
+ .single();
62
+
57
63
  // Insert the idea into saved_ideas
58
64
  const { data: idea, error: insertError } = await supabase
59
65
  .from('saved_ideas')
@@ -86,6 +92,6 @@ export async function submitIdea(
86
92
  return {
87
93
  success: true,
88
94
  ideaId: idea.id,
89
- message: `💡 Idea "${title}" submitted to Idea Lab for project "${project.name}". Status: Draft (awaiting review)`
95
+ message: `💡 Idea "${title}" submitted to Idea Lab for project "${project?.name || projectId}". Status: Draft (awaiting review)`
90
96
  };
91
97
  }
@@ -22,7 +22,7 @@ registry.register({
22
22
  based on project context and user settings.`,
23
23
  schema: GenerateCursorRulesInputSchema,
24
24
  handler: async (args, context) => {
25
- const result = await syncIdeRules(context.supabase, args.projectId);
25
+ const result = await syncIdeRules(context.supabase, context.userId, args.projectId);
26
26
 
27
27
  // Format response: Main file content + information about modular files
28
28
  let responseText = `FileName: ${result.fileName}\n\nContent:\n${result.content}`;
@@ -43,14 +43,27 @@ based on project context and user settings.`,
43
43
  * Uses the centralized rules-engine for consistent generation across all consumers.
44
44
  *
45
45
  * @param supabase - Supabase client instance
46
+ * @param userId - The user ID for access verification
46
47
  * @param projectId - The project ID to generate rules for
47
48
  * @returns Object with fileName, content, and the new modular files[] array
48
49
  */
49
50
  export async function syncIdeRules(
50
51
  supabase: SupabaseClient,
52
+ userId: string,
51
53
  projectId: string
52
54
  ): Promise<{ fileName: string; content: string; files: RuleFile[] }> {
53
- // 1. Fetch Project & Preferred IDE
55
+ // 1. Verify project access
56
+ const { data: hasAccess, error: accessError } = await supabase
57
+ .rpc('check_project_access_secure', {
58
+ p_project_id: projectId,
59
+ p_user_id: userId
60
+ });
61
+
62
+ if (accessError || !hasAccess) {
63
+ throw new Error('Project not found or access denied');
64
+ }
65
+
66
+ // 2. Fetch Project & Preferred IDE
54
67
  const { data: project, error: projectError } = await supabase
55
68
  .from('projects')
56
69
  .select('*')
@@ -58,7 +71,7 @@ export async function syncIdeRules(
58
71
  .single();
59
72
 
60
73
  if (projectError || !project) {
61
- throw new Error(`Project ${projectId} not found.`);
74
+ throw new Error(`Project ${projectId} details not found.`);
62
75
  }
63
76
 
64
77
  // 2. Determine IDE (Preference -> Fallback)
@@ -65,14 +65,13 @@ export async function refineLogic(
65
65
  const traceId = uuidv4();
66
66
 
67
67
  // Verify project access
68
- const { data: project, error: projectError } = await supabase
69
- .from('projects')
70
- .select('id, name')
71
- .eq('id', projectId)
72
- .eq('owner_id', userId)
73
- .single();
68
+ const { data: hasAccess, error: accessError } = await supabase
69
+ .rpc('check_project_access_secure', {
70
+ p_project_id: projectId,
71
+ p_user_id: userId
72
+ });
74
73
 
75
- if (projectError || !project) {
74
+ if (accessError || !hasAccess) {
76
75
  throw new Error(`Project access denied or not found: ${projectId}`);
77
76
  }
78
77
 
@@ -40,18 +40,24 @@ export async function updateRoadmap(
40
40
  chunkId?: string,
41
41
  title?: string
42
42
  ): Promise<UpdateRoadmapResponse> {
43
- // First, verify project ownership
44
- const { data: project, error: projectError } = await supabase
45
- .from('projects')
46
- .select('id, name')
47
- .eq('id', projectId)
48
- .eq('owner_id', userId)
49
- .single();
43
+ // 1. Verify project access
44
+ const { data: hasAccess, error: accessError } = await supabase
45
+ .rpc('check_project_access_secure', {
46
+ p_project_id: projectId,
47
+ p_user_id: userId
48
+ });
50
49
 
51
- if (projectError || !project) {
50
+ if (accessError || !hasAccess) {
52
51
  throw new Error('Project not found or access denied');
53
52
  }
54
53
 
54
+ // 2. Fetch project details (for response message if needed)
55
+ const { data: project } = await supabase
56
+ .from('projects')
57
+ .select('name')
58
+ .eq('id', projectId)
59
+ .single();
60
+
55
61
  // Find the roadmap chunk
56
62
  let targetChunk: { id: string; title: string; status: string } | null = null;
57
63