@homenshum/convex-mcp-nodebench 0.4.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/dist/index.js +293 -4
  2. package/dist/tools/actionAuditTools.d.ts +2 -0
  3. package/dist/tools/actionAuditTools.js +180 -0
  4. package/dist/tools/authorizationTools.d.ts +2 -0
  5. package/dist/tools/authorizationTools.js +201 -0
  6. package/dist/tools/dataModelingTools.d.ts +2 -0
  7. package/dist/tools/dataModelingTools.js +168 -0
  8. package/dist/tools/deploymentTools.js +42 -2
  9. package/dist/tools/devSetupTools.d.ts +2 -0
  10. package/dist/tools/devSetupTools.js +170 -0
  11. package/dist/tools/embeddingProvider.d.ts +6 -0
  12. package/dist/tools/embeddingProvider.js +3 -0
  13. package/dist/tools/functionTools.js +24 -1
  14. package/dist/tools/httpTools.js +128 -48
  15. package/dist/tools/migrationTools.d.ts +2 -0
  16. package/dist/tools/migrationTools.js +133 -0
  17. package/dist/tools/paginationTools.d.ts +2 -0
  18. package/dist/tools/paginationTools.js +125 -0
  19. package/dist/tools/qualityGateTools.d.ts +2 -0
  20. package/dist/tools/qualityGateTools.js +204 -0
  21. package/dist/tools/queryEfficiencyTools.d.ts +2 -0
  22. package/dist/tools/queryEfficiencyTools.js +191 -0
  23. package/dist/tools/reportingTools.d.ts +2 -0
  24. package/dist/tools/reportingTools.js +240 -0
  25. package/dist/tools/schedulerTools.d.ts +2 -0
  26. package/dist/tools/schedulerTools.js +197 -0
  27. package/dist/tools/schemaTools.js +95 -1
  28. package/dist/tools/storageAuditTools.d.ts +2 -0
  29. package/dist/tools/storageAuditTools.js +148 -0
  30. package/dist/tools/toolRegistry.d.ts +4 -0
  31. package/dist/tools/toolRegistry.js +274 -11
  32. package/dist/tools/transactionSafetyTools.d.ts +2 -0
  33. package/dist/tools/transactionSafetyTools.js +166 -0
  34. package/dist/tools/typeSafetyTools.d.ts +2 -0
  35. package/dist/tools/typeSafetyTools.js +146 -0
  36. package/dist/tools/vectorSearchTools.d.ts +2 -0
  37. package/dist/tools/vectorSearchTools.js +192 -0
  38. package/dist/types.d.ts +6 -1
  39. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@
14
14
  */
15
15
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
16
16
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
17
- import { ListToolsRequestSchema, CallToolRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
17
+ import { ListToolsRequestSchema, CallToolRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
18
18
  import { getDb, seedGotchasIfEmpty } from "./db.js";
19
19
  import { schemaTools } from "./tools/schemaTools.js";
20
20
  import { functionTools } from "./tools/functionTools.js";
@@ -26,6 +26,20 @@ import { cronTools } from "./tools/cronTools.js";
26
26
  import { componentTools } from "./tools/componentTools.js";
27
27
  import { httpTools } from "./tools/httpTools.js";
28
28
  import { critterTools } from "./tools/critterTools.js";
29
+ import { authorizationTools } from "./tools/authorizationTools.js";
30
+ import { queryEfficiencyTools } from "./tools/queryEfficiencyTools.js";
31
+ import { actionAuditTools } from "./tools/actionAuditTools.js";
32
+ import { typeSafetyTools } from "./tools/typeSafetyTools.js";
33
+ import { transactionSafetyTools } from "./tools/transactionSafetyTools.js";
34
+ import { storageAuditTools } from "./tools/storageAuditTools.js";
35
+ import { paginationTools } from "./tools/paginationTools.js";
36
+ import { dataModelingTools } from "./tools/dataModelingTools.js";
37
+ import { devSetupTools } from "./tools/devSetupTools.js";
38
+ import { migrationTools } from "./tools/migrationTools.js";
39
+ import { reportingTools } from "./tools/reportingTools.js";
40
+ import { vectorSearchTools } from "./tools/vectorSearchTools.js";
41
+ import { schedulerTools } from "./tools/schedulerTools.js";
42
+ import { qualityGateTools } from "./tools/qualityGateTools.js";
29
43
  import { CONVEX_GOTCHAS } from "./gotchaSeed.js";
30
44
  import { REGISTRY } from "./tools/toolRegistry.js";
31
45
  import { initEmbeddingIndex } from "./tools/embeddingProvider.js";
@@ -41,6 +55,20 @@ const ALL_TOOLS = [
41
55
  ...componentTools,
42
56
  ...httpTools,
43
57
  ...critterTools,
58
+ ...authorizationTools,
59
+ ...queryEfficiencyTools,
60
+ ...actionAuditTools,
61
+ ...typeSafetyTools,
62
+ ...transactionSafetyTools,
63
+ ...storageAuditTools,
64
+ ...paginationTools,
65
+ ...dataModelingTools,
66
+ ...devSetupTools,
67
+ ...migrationTools,
68
+ ...reportingTools,
69
+ ...vectorSearchTools,
70
+ ...schedulerTools,
71
+ ...qualityGateTools,
44
72
  ];
45
73
  const toolMap = new Map();
46
74
  for (const tool of ALL_TOOLS) {
@@ -49,20 +77,52 @@ for (const tool of ALL_TOOLS) {
49
77
  // ── Server setup ────────────────────────────────────────────────────
50
78
  const server = new Server({
51
79
  name: "convex-mcp-nodebench",
52
- version: "0.1.0",
80
+ version: "0.8.0",
53
81
  }, {
54
82
  capabilities: {
55
83
  tools: {},
84
+ resources: {},
85
+ prompts: {},
56
86
  },
57
87
  });
58
88
  // ── Initialize DB + seed gotchas ────────────────────────────────────
59
89
  getDb();
60
90
  seedGotchasIfEmpty(CONVEX_GOTCHAS);
61
91
  // ── Background: initialize embedding index for semantic search ───────
62
- const embeddingCorpus = REGISTRY.map((entry) => ({
92
+ // Uses Agent-as-a-Graph bipartite corpus: tool nodes + domain nodes for graph-aware retrieval
93
+ const descMap = new Map(ALL_TOOLS.map((t) => [t.name, t.description]));
94
+ // Tool nodes: individual tools with full metadata text
95
+ const toolCorpus = REGISTRY.map((entry) => ({
63
96
  name: entry.name,
64
- text: `${entry.name} ${entry.tags.join(" ")} ${entry.category} ${entry.phase}`,
97
+ text: `${entry.name} ${entry.tags.join(" ")} ${entry.category} ${entry.phase} ${descMap.get(entry.name) ?? ""}`,
98
+ nodeType: "tool",
65
99
  }));
100
+ // Domain nodes: aggregate category descriptions for upward traversal
101
+ // When a domain matches, all tools in that domain get a sibling boost
102
+ const categoryTools = new Map();
103
+ for (const entry of REGISTRY) {
104
+ const list = categoryTools.get(entry.category) ?? [];
105
+ list.push(entry.name);
106
+ categoryTools.set(entry.category, list);
107
+ }
108
+ const domainCorpus = [...categoryTools.entries()].map(([category, toolNames]) => {
109
+ const allTags = new Set();
110
+ const descs = [];
111
+ for (const tn of toolNames) {
112
+ const e = REGISTRY.find((r) => r.name === tn);
113
+ if (e)
114
+ e.tags.forEach((t) => allTags.add(t));
115
+ const d = descMap.get(tn);
116
+ if (d)
117
+ descs.push(d);
118
+ }
119
+ return {
120
+ name: `domain:${category}`,
121
+ text: `${category} domain: ${toolNames.join(" ")} ${[...allTags].join(" ")} ${descs.map(d => d.slice(0, 80)).join(" ")}`,
122
+ nodeType: "domain",
123
+ };
124
+ });
125
+ const embeddingCorpus = [...toolCorpus, ...domainCorpus];
66
126
  initEmbeddingIndex(embeddingCorpus).catch(() => {
67
127
  /* Embedding init failed — semantic search stays disabled */
68
128
  });
@@ -119,6 +179,235 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
119
179
  };
120
180
  }
121
181
  });
182
+ // ── MCP Resources ───────────────────────────────────────────────────
183
+ server.setRequestHandler(ListResourcesRequestSchema, async () => {
184
+ return {
185
+ resources: [
186
+ {
187
+ uri: "convex://project-health",
188
+ name: "Project Health Summary",
189
+ description: "Latest quality gate score, audit coverage, and issue counts across all audit types",
190
+ mimeType: "application/json",
191
+ },
192
+ {
193
+ uri: "convex://recent-audits",
194
+ name: "Recent Audit Results",
195
+ description: "Summary of the 10 most recent audit runs with issue counts and timestamps",
196
+ mimeType: "application/json",
197
+ },
198
+ {
199
+ uri: "convex://gotcha-db",
200
+ name: "Gotcha Knowledge Base",
201
+ description: "All stored Convex gotchas (seeded + user-recorded) with categories and severity",
202
+ mimeType: "application/json",
203
+ },
204
+ ],
205
+ };
206
+ });
207
+ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
208
+ const uri = request.params.uri;
209
+ const db = getDb();
210
+ if (uri === "convex://project-health") {
211
+ // Aggregate across all projects
212
+ const audits = db.prepare("SELECT audit_type, issue_count, audited_at FROM audit_results ORDER BY audited_at DESC LIMIT 50").all();
213
+ const byType = {};
214
+ for (const a of audits) {
215
+ if (!byType[a.audit_type]) {
216
+ byType[a.audit_type] = { count: a.issue_count, latest: a.audited_at };
217
+ }
218
+ }
219
+ const totalIssues = Object.values(byType).reduce((s, v) => s + v.count, 0);
220
+ const auditTypes = Object.keys(byType).length;
221
+ const latestGate = db.prepare("SELECT findings FROM deploy_checks WHERE check_type = 'quality_gate' ORDER BY checked_at DESC LIMIT 1").get();
222
+ let gateResult = null;
223
+ if (latestGate?.findings) {
224
+ try {
225
+ gateResult = JSON.parse(latestGate.findings);
226
+ }
227
+ catch { /* skip */ }
228
+ }
229
+ return {
230
+ contents: [{
231
+ uri,
232
+ mimeType: "application/json",
233
+ text: JSON.stringify({
234
+ totalIssues,
235
+ auditTypesRun: auditTypes,
236
+ issuesByType: byType,
237
+ latestQualityGate: gateResult ? { score: gateResult.score, grade: gateResult.grade, passed: gateResult.passed } : null,
238
+ toolCount: ALL_TOOLS.length,
239
+ }, null, 2),
240
+ }],
241
+ };
242
+ }
243
+ if (uri === "convex://recent-audits") {
244
+ const audits = db.prepare("SELECT id, project_dir, audit_type, issue_count, audited_at FROM audit_results ORDER BY audited_at DESC LIMIT 10").all();
245
+ return {
246
+ contents: [{
247
+ uri,
248
+ mimeType: "application/json",
249
+ text: JSON.stringify({ audits }, null, 2),
250
+ }],
251
+ };
252
+ }
253
+ if (uri === "convex://gotcha-db") {
254
+ const gotchas = db.prepare("SELECT key, category, severity, tags, source, updated_at FROM convex_gotchas ORDER BY updated_at DESC").all();
255
+ return {
256
+ contents: [{
257
+ uri,
258
+ mimeType: "application/json",
259
+ text: JSON.stringify({
260
+ totalGotchas: gotchas.length,
261
+ bySource: {
262
+ seed: gotchas.filter(g => g.source === "seed").length,
263
+ user: gotchas.filter(g => g.source === "user").length,
264
+ },
265
+ gotchas,
266
+ }, null, 2),
267
+ }],
268
+ };
269
+ }
270
+ return {
271
+ contents: [{
272
+ uri,
273
+ mimeType: "text/plain",
274
+ text: `Unknown resource: ${uri}`,
275
+ }],
276
+ };
277
+ });
278
+ // ── MCP Prompts ─────────────────────────────────────────────────────
279
+ server.setRequestHandler(ListPromptsRequestSchema, async () => {
280
+ return {
281
+ prompts: [
282
+ {
283
+ name: "full-audit",
284
+ description: "Run a complete Convex project audit: schema, functions, auth, queries, actions, type safety, transactions, storage, pagination, data modeling, dev setup, vectors, schedulers — then quality gate",
285
+ arguments: [
286
+ {
287
+ name: "projectDir",
288
+ description: "Absolute path to the project root",
289
+ required: true,
290
+ },
291
+ ],
292
+ },
293
+ {
294
+ name: "pre-deploy-checklist",
295
+ description: "Step-by-step pre-deployment verification: audit critical issues, check env vars, review migration plan, run quality gate",
296
+ arguments: [
297
+ {
298
+ name: "projectDir",
299
+ description: "Absolute path to the project root",
300
+ required: true,
301
+ },
302
+ ],
303
+ },
304
+ {
305
+ name: "security-review",
306
+ description: "Security-focused audit: authorization coverage, type safety, action safety, storage permissions",
307
+ arguments: [
308
+ {
309
+ name: "projectDir",
310
+ description: "Absolute path to the project root",
311
+ required: true,
312
+ },
313
+ ],
314
+ },
315
+ ],
316
+ };
317
+ });
318
+ server.setRequestHandler(GetPromptRequestSchema, async (request) => {
319
+ const { name, arguments: promptArgs } = request.params;
320
+ const projectDir = promptArgs?.projectDir ?? ".";
321
+ if (name === "full-audit") {
322
+ return {
323
+ description: "Complete Convex project audit sequence",
324
+ messages: [
325
+ {
326
+ role: "user",
327
+ content: {
328
+ type: "text",
329
+ text: `Run a complete audit of the Convex project at "${projectDir}". Execute these tools in order:
330
+
331
+ 1. convex_audit_schema — Check schema.ts for anti-patterns
332
+ 2. convex_audit_functions — Audit function registration and compliance
333
+ 3. convex_audit_authorization — Check auth coverage on public endpoints
334
+ 4. convex_audit_query_efficiency — Find unbounded queries and missing indexes
335
+ 5. convex_audit_actions — Validate action safety (no ctx.db, error handling)
336
+ 6. convex_check_type_safety — Find as-any casts and type issues
337
+ 7. convex_audit_transaction_safety — Detect race conditions
338
+ 8. convex_audit_storage_usage — Check file storage patterns
339
+ 9. convex_audit_pagination — Validate pagination implementations
340
+ 10. convex_audit_data_modeling — Check schema design quality
341
+ 11. convex_audit_vector_search — Validate vector search setup
342
+ 12. convex_audit_schedulers — Check scheduled function safety
343
+ 13. convex_audit_dev_setup — Verify project setup
344
+ 14. convex_quality_gate — Run configurable quality gate across all results
345
+
346
+ After running all audits, summarize:
347
+ - Total issues by severity (critical/warning/info)
348
+ - Top 5 most impactful issues to fix first
349
+ - Quality gate score and grade
350
+ - Trend direction if previous audits exist (use convex_audit_diff)`,
351
+ },
352
+ },
353
+ ],
354
+ };
355
+ }
356
+ if (name === "pre-deploy-checklist") {
357
+ return {
358
+ description: "Pre-deployment verification sequence",
359
+ messages: [
360
+ {
361
+ role: "user",
362
+ content: {
363
+ type: "text",
364
+ text: `Run pre-deployment checks for the Convex project at "${projectDir}":
365
+
366
+ 1. convex_pre_deploy_gate — Structural checks (schema, auth config, initialization)
367
+ 2. convex_check_env_vars — Verify all required env vars are set
368
+ 3. convex_audit_authorization — Ensure auth coverage is adequate
369
+ 4. convex_audit_actions — No ctx.db access in actions
370
+ 5. convex_snapshot_schema — Capture current schema state
371
+ 6. convex_schema_migration_plan — Compare against previous snapshot for breaking changes
372
+ 7. convex_quality_gate — Final quality check with thresholds
373
+
374
+ Report: DEPLOY or DO NOT DEPLOY with specific blockers to fix.`,
375
+ },
376
+ },
377
+ ],
378
+ };
379
+ }
380
+ if (name === "security-review") {
381
+ return {
382
+ description: "Security-focused audit sequence",
383
+ messages: [
384
+ {
385
+ role: "user",
386
+ content: {
387
+ type: "text",
388
+ text: `Run a security review of the Convex project at "${projectDir}":
389
+
390
+ 1. convex_audit_authorization — Auth coverage on all public endpoints
391
+ 2. convex_check_type_safety — Type safety bypasses (as any)
392
+ 3. convex_audit_actions — Action safety (ctx.db, error handling, "use node")
393
+ 4. convex_audit_storage_usage — Storage permission patterns
394
+ 5. convex_audit_pagination — Unbounded numItems (DoS risk)
395
+ 6. convex_audit_transaction_safety — Race condition risks
396
+
397
+ Focus on: unauthorized data access, unvalidated inputs, missing error boundaries, and potential data corruption vectors.`,
398
+ },
399
+ },
400
+ ],
401
+ };
402
+ }
403
+ return {
404
+ description: "Unknown prompt",
405
+ messages: [{
406
+ role: "user",
407
+ content: { type: "text", text: `Unknown prompt: ${name}` },
408
+ }],
409
+ };
410
+ });
122
411
  // ── Start server ────────────────────────────────────────────────────
123
412
  async function main() {
124
413
  const transport = new StdioServerTransport();
@@ -0,0 +1,2 @@
1
+ import type { McpTool } from "../types.js";
2
+ export declare const actionAuditTools: McpTool[];
@@ -0,0 +1,180 @@
1
+ import { readFileSync, existsSync, readdirSync } from "node:fs";
2
+ import { join, resolve } from "node:path";
3
+ import { getDb, genId } from "../db.js";
4
+ import { getQuickRef } from "./toolRegistry.js";
5
+ // ── Helpers ──────────────────────────────────────────────────────────
6
+ function findConvexDir(projectDir) {
7
+ const candidates = [join(projectDir, "convex"), join(projectDir, "src", "convex")];
8
+ for (const c of candidates) {
9
+ if (existsSync(c))
10
+ return c;
11
+ }
12
+ return null;
13
+ }
14
+ function collectTsFiles(dir) {
15
+ const results = [];
16
+ if (!existsSync(dir))
17
+ return results;
18
+ const entries = readdirSync(dir, { withFileTypes: true });
19
+ for (const entry of entries) {
20
+ const full = join(dir, entry.name);
21
+ if (entry.isDirectory() && entry.name !== "node_modules" && entry.name !== "_generated") {
22
+ results.push(...collectTsFiles(full));
23
+ }
24
+ else if (entry.isFile() && entry.name.endsWith(".ts")) {
25
+ results.push(full);
26
+ }
27
+ }
28
+ return results;
29
+ }
30
+ function auditActions(convexDir) {
31
+ const files = collectTsFiles(convexDir);
32
+ const issues = [];
33
+ let totalActions = 0;
34
+ let actionsWithDbAccess = 0;
35
+ let actionsWithoutNodeDirective = 0;
36
+ let actionsWithoutErrorHandling = 0;
37
+ let actionCallingAction = 0;
38
+ // Node APIs that require "use node" directive
39
+ const nodeApis = /\b(require|__dirname|__filename|Buffer\.|process\.env|fs\.|path\.|crypto\.|child_process|net\.|http\.|https\.)\b/;
40
+ for (const filePath of files) {
41
+ const content = readFileSync(filePath, "utf-8");
42
+ const relativePath = filePath.replace(convexDir, "").replace(/^[\\/]/, "");
43
+ const lines = content.split("\n");
44
+ const hasUseNode = /["']use node["']/.test(content);
45
+ const actionPattern = /export\s+(?:const\s+(\w+)\s*=|default)\s+(action|internalAction)\s*\(/g;
46
+ let m;
47
+ while ((m = actionPattern.exec(content)) !== null) {
48
+ const funcName = m[1] || "default";
49
+ const funcType = m[2];
50
+ totalActions++;
51
+ const startLine = content.slice(0, m.index).split("\n").length - 1;
52
+ // Extract body using brace tracking
53
+ let depth = 0;
54
+ let foundOpen = false;
55
+ let endLine = Math.min(startLine + 100, lines.length);
56
+ for (let j = startLine; j < lines.length; j++) {
57
+ for (const ch of lines[j]) {
58
+ if (ch === "{") {
59
+ depth++;
60
+ foundOpen = true;
61
+ }
62
+ if (ch === "}")
63
+ depth--;
64
+ }
65
+ if (foundOpen && depth <= 0) {
66
+ endLine = j + 1;
67
+ break;
68
+ }
69
+ }
70
+ const body = lines.slice(startLine, endLine).join("\n");
71
+ // Check 1: ctx.db access in action (FATAL — not allowed)
72
+ if (/ctx\.db\.(get|query|insert|patch|replace|delete)\s*\(/.test(body)) {
73
+ actionsWithDbAccess++;
74
+ issues.push({
75
+ severity: "critical",
76
+ location: `${relativePath}:${startLine + 1}`,
77
+ functionName: funcName,
78
+ message: `${funcType} "${funcName}" accesses ctx.db directly. Actions cannot access the database — use ctx.runQuery/ctx.runMutation instead.`,
79
+ fix: "Move DB operations into a query or mutation, then call via ctx.runQuery(internal.file.func, args) or ctx.runMutation(...)",
80
+ });
81
+ }
82
+ // Check 2: Node API usage without "use node"
83
+ if (!hasUseNode && nodeApis.test(body)) {
84
+ actionsWithoutNodeDirective++;
85
+ issues.push({
86
+ severity: "critical",
87
+ location: `${relativePath}:${startLine + 1}`,
88
+ functionName: funcName,
89
+ message: `${funcType} "${funcName}" uses Node.js APIs but file lacks "use node" directive. Will fail in Convex runtime.`,
90
+ fix: `Add "use node"; at the top of ${relativePath}`,
91
+ });
92
+ }
93
+ // Check 3: External API calls without try/catch
94
+ const hasFetch = /\bfetch\s*\(/.test(body);
95
+ const hasAxios = /\baxios\b/.test(body);
96
+ const hasExternalCall = hasFetch || hasAxios;
97
+ const hasTryCatch = /try\s*\{/.test(body);
98
+ if (hasExternalCall && !hasTryCatch) {
99
+ actionsWithoutErrorHandling++;
100
+ issues.push({
101
+ severity: "warning",
102
+ location: `${relativePath}:${startLine + 1}`,
103
+ functionName: funcName,
104
+ message: `${funcType} "${funcName}" makes external API calls without try/catch. Network failures will crash the action.`,
105
+ fix: "Wrap fetch/axios calls in try/catch and handle errors gracefully",
106
+ });
107
+ }
108
+ // Check 4: Action calling another action
109
+ if (/ctx\.runAction\s*\(/.test(body)) {
110
+ actionCallingAction++;
111
+ issues.push({
112
+ severity: "warning",
113
+ location: `${relativePath}:${startLine + 1}`,
114
+ functionName: funcName,
115
+ message: `${funcType} "${funcName}" calls ctx.runAction(). Only call action from action when crossing runtimes (V8 ↔ Node). Otherwise extract shared logic into a helper function.`,
116
+ fix: "Extract shared logic into an async helper, or use ctx.runMutation/ctx.runQuery as intermediary",
117
+ });
118
+ }
119
+ // Check 5: Very long action body (likely doing too much)
120
+ const bodyLines = endLine - startLine;
121
+ if (bodyLines > 80) {
122
+ issues.push({
123
+ severity: "info",
124
+ location: `${relativePath}:${startLine + 1}`,
125
+ functionName: funcName,
126
+ message: `${funcType} "${funcName}" is ${bodyLines} lines long. Consider splitting into smaller actions or extracting helpers.`,
127
+ fix: "Break large actions into smaller, focused functions",
128
+ });
129
+ }
130
+ }
131
+ }
132
+ return {
133
+ issues,
134
+ stats: {
135
+ totalActions,
136
+ actionsWithDbAccess,
137
+ actionsWithoutNodeDirective,
138
+ actionsWithoutErrorHandling,
139
+ actionCallingAction,
140
+ },
141
+ };
142
+ }
143
+ // ── Tool Definition ─────────────────────────────────────────────────
144
+ export const actionAuditTools = [
145
+ {
146
+ name: "convex_audit_actions",
147
+ description: 'Audit Convex actions for: ctx.db access (fatal — actions cannot access DB directly), missing "use node" directive for Node APIs, external API calls without error handling, and action-calling-action anti-patterns.',
148
+ inputSchema: {
149
+ type: "object",
150
+ properties: {
151
+ projectDir: {
152
+ type: "string",
153
+ description: "Absolute path to the project root containing a convex/ directory",
154
+ },
155
+ },
156
+ required: ["projectDir"],
157
+ },
158
+ handler: async (args) => {
159
+ const projectDir = resolve(args.projectDir);
160
+ const convexDir = findConvexDir(projectDir);
161
+ if (!convexDir) {
162
+ return { error: "No convex/ directory found" };
163
+ }
164
+ const { issues, stats } = auditActions(convexDir);
165
+ const db = getDb();
166
+ db.prepare("INSERT INTO audit_results (id, project_dir, audit_type, issues_json, issue_count) VALUES (?, ?, ?, ?, ?)").run(genId("audit"), projectDir, "action_audit", JSON.stringify(issues), issues.length);
167
+ return {
168
+ summary: {
169
+ ...stats,
170
+ totalIssues: issues.length,
171
+ critical: issues.filter((i) => i.severity === "critical").length,
172
+ warnings: issues.filter((i) => i.severity === "warning").length,
173
+ },
174
+ issues: issues.slice(0, 30),
175
+ quickRef: getQuickRef("convex_audit_actions"),
176
+ };
177
+ },
178
+ },
179
+ ];
180
+ //# sourceMappingURL=actionAuditTools.js.map
@@ -0,0 +1,2 @@
1
+ import type { McpTool } from "../types.js";
2
+ export declare const authorizationTools: McpTool[];