@promptprojectmanager/mcp-server 4.4.3 → 4.5.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.
package/dist/index.js CHANGED
@@ -68,19 +68,19 @@ function parseUpdateArgs(args) {
68
68
  if (!ticketSlug || !content) return null;
69
69
  return { ticketSlug, content };
70
70
  }
71
- function parseRunPromptArgs(args) {
71
+ function parsePromptsRunArgs(args) {
72
72
  const parsed = args;
73
73
  const slug = typeof parsed?.slug === "string" ? parsed.slug : void 0;
74
74
  if (!slug) return null;
75
75
  return { slug };
76
76
  }
77
- function parseSystemPromptsArgs(args) {
77
+ function parsePromptsListArgs(args) {
78
78
  const parsed = args;
79
79
  return {
80
80
  search: typeof parsed?.search === "string" ? parsed.search : void 0
81
81
  };
82
82
  }
83
- function parseSystemPromptsUpdateArgs(args) {
83
+ function parsePromptsUpdateArgs(args) {
84
84
  const parsed = args;
85
85
  const slug = typeof parsed?.slug === "string" ? parsed.slug : void 0;
86
86
  if (!slug) return null;
@@ -173,7 +173,6 @@ async function startServer(config, convexClientRaw) {
173
173
  config.projectToken
174
174
  );
175
175
  console.error(`[MCP] Project token mode: loaded ${accountScopedPrompts.length} prompts`);
176
- const projectsForTickets = [{ slug: tokenProjectSlug, name: tokenProjectSlug }];
177
176
  console.error(`[MCP] Found ${accountScopedPrompts.length} account-scoped prompts (global tools)`);
178
177
  console.error(`[MCP] Ticket project scope: ${tokenProjectSlug} (token-scoped)`);
179
178
  if (accountScopedPrompts.length === 0) {
@@ -181,78 +180,78 @@ async function startServer(config, convexClientRaw) {
181
180
  "[MCP] WARNING: No prompts found. Create prompts in the 'prompts' section to expose them via MCP."
182
181
  );
183
182
  }
184
- const projectSlugs = new Set(projectsForTickets.map((p) => p.slug));
185
- const dynamicTicketTools = [];
186
- for (const projectSlug of projectSlugs) {
187
- dynamicTicketTools.push({
188
- name: `${projectSlug}_tickets_work`,
183
+ const projectSlug = tokenProjectSlug;
184
+ const dynamicTicketTools = [
185
+ // Unified work command (replaces both tickets_open and tickets_work)
186
+ {
187
+ name: `tickets_work`,
189
188
  description: `Get work from the "${projectSlug}" project. With no args: gets next ticket from open queue. With ticket slug/number: opens or resumes that specific ticket.`,
190
189
  slashDescription: `Get next ticket from queue, or work on specific ticket by slug/number`,
191
190
  projectSlug,
192
191
  type: "work"
193
- });
194
- dynamicTicketTools.push({
195
- name: `${projectSlug}_tickets_close`,
192
+ },
193
+ {
194
+ name: `tickets_close`,
196
195
  description: `Mark a working ticket as completed in the "${projectSlug}" project`,
197
196
  slashDescription: `Mark a working ticket as completed`,
198
197
  projectSlug,
199
198
  type: "close"
200
- });
201
- dynamicTicketTools.push({
202
- name: `${projectSlug}_tickets_create`,
199
+ },
200
+ {
201
+ name: `tickets_create`,
203
202
  description: `Create a new ticket in the "${projectSlug}" project queue`,
204
203
  slashDescription: `Create a new ticket in the backlog queue`,
205
204
  projectSlug,
206
205
  type: "create"
207
- });
208
- dynamicTicketTools.push({
209
- name: `${projectSlug}_tickets_search`,
206
+ },
207
+ {
208
+ name: `tickets_search`,
210
209
  description: `Search for tickets by content in the "${projectSlug}" project`,
211
210
  slashDescription: `Search for tickets by content`,
212
211
  projectSlug,
213
212
  type: "search"
214
- });
215
- dynamicTicketTools.push({
216
- name: `${projectSlug}_tickets_get`,
213
+ },
214
+ {
215
+ name: `tickets_get`,
217
216
  description: `Get a specific ticket by number or slug from "${projectSlug}" (read-only)`,
218
217
  slashDescription: `Get a specific ticket by number or slug (read-only)`,
219
218
  projectSlug,
220
219
  type: "get"
221
- });
222
- dynamicTicketTools.push({
223
- name: `${projectSlug}_tickets_list`,
220
+ },
221
+ {
222
+ name: `tickets_list`,
224
223
  description: `List active tickets in the "${projectSlug}" project (backlog + open + working)`,
225
224
  slashDescription: `List active tickets (backlog + open + working)`,
226
225
  projectSlug,
227
226
  type: "list"
228
- });
229
- dynamicTicketTools.push({
230
- name: `${projectSlug}_tickets_update`,
227
+ },
228
+ {
229
+ name: `tickets_update`,
231
230
  description: `Update a ticket in the "${projectSlug}" project by appending content with timestamp`,
232
231
  slashDescription: `Update a ticket by appending content with timestamp`,
233
232
  projectSlug,
234
233
  type: "update"
235
- });
236
- }
237
- console.error(`[MCP] Registering ${dynamicTicketTools.length} ticket tools for ${projectSlugs.size} project(s)...`);
238
- const globalSystemTools = [
234
+ }
235
+ ];
236
+ console.error(`[MCP] Registering ${dynamicTicketTools.length} ticket tools...`);
237
+ const globalPromptTools = [
239
238
  {
240
- name: "system_run_prompt",
241
- description: "Execute a prompt by slug. Use system_prompts to list available prompts.",
242
- slashDescription: "Execute a prompt by slug. Use system_prompts to list available prompts."
239
+ name: "prompts_run",
240
+ description: "Execute a prompt by slug. Use prompts_list to list available prompts.",
241
+ slashDescription: "Execute a prompt by slug. Use prompts_list to list available prompts."
243
242
  },
244
243
  {
245
- name: "system_prompts",
244
+ name: "prompts_list",
246
245
  description: "List all available prompts",
247
246
  slashDescription: "List all available prompts"
248
247
  },
249
248
  {
250
- name: "system_prompts_update",
249
+ name: "prompts_update",
251
250
  description: "Read or update a prompt. Without content: returns prompt with context and editing guidance. With content: saves new version.",
252
251
  slashDescription: "Read or update a prompt by slug"
253
252
  }
254
253
  ];
255
- console.error(`[MCP] Registering ${globalSystemTools.length} global system tools...`);
254
+ console.error(`[MCP] Registering ${globalPromptTools.length} global prompt tools...`);
256
255
  const dynamicPromptTools = [];
257
256
  for (const prompt of accountScopedPrompts) {
258
257
  const folderPrefix = prompt.folderSlug ? `[${prompt.folderSlug}] ` : "";
@@ -295,16 +294,16 @@ async function startServer(config, convexClientRaw) {
295
294
  name: tt.name,
296
295
  description: tt.slashDescription
297
296
  }));
298
- const systemPromptsSchemas = globalSystemTools.map((st) => ({
297
+ const promptToolSchemas = globalPromptTools.map((st) => ({
299
298
  name: st.name,
300
299
  description: st.slashDescription
301
300
  }));
302
301
  return {
303
302
  prompts: [
304
- ...systemPromptsSchemas,
305
- // system_prompts, system_run_prompt (global)
303
+ ...promptToolSchemas,
304
+ // prompts_run, prompts_list, prompts_update (global)
306
305
  ...ticketPromptSchemas
307
- // project_tickets_* (project-scoped)
306
+ // tickets_* (token-scoped)
308
307
  ]
309
308
  };
310
309
  });
@@ -326,26 +325,25 @@ async function startServer(config, convexClientRaw) {
326
325
  };
327
326
  }
328
327
  const ticketTool = dynamicTicketTools.find((tt) => tt.name === promptName);
329
- const ticketWorkMatch = promptName.match(/^(.+)_tickets_work\s+(.+)$/);
328
+ const ticketWorkMatch = promptName.match(/^tickets_work\s+(.+)$/);
330
329
  if (ticketTool || ticketWorkMatch) {
331
330
  let promptContent;
332
331
  let description;
333
332
  if (ticketWorkMatch) {
334
- const projectSlug = ticketWorkMatch[1];
335
- const ticketArg = ticketWorkMatch[2].trim();
333
+ const ticketArg = ticketWorkMatch[1].trim();
336
334
  description = `Work on ticket "${ticketArg}" from "${projectSlug}"`;
337
335
  promptContent = `Get work on ticket "${ticketArg}" from the "${projectSlug}" project.
338
336
 
339
- Call the \`${projectSlug}_tickets_work\` tool with ticketSlug: "${ticketArg}".
337
+ Call the \`tickets_work\` tool with ticketSlug: "${ticketArg}".
340
338
 
341
339
  This will open the ticket if it's in the open queue, or resume it if already working.`;
342
340
  } else if (ticketTool.type === "work") {
343
341
  description = ticketTool.description;
344
342
  promptContent = `Get work from the "${ticketTool.projectSlug}" project.
345
343
 
346
- Call the \`${ticketTool.name}\` tool to get the next ticket from the open queue.
344
+ Call the \`tickets_work\` tool to get the next ticket from the open queue.
347
345
 
348
- You can also specify a ticket: /ppm:${ticketTool.projectSlug}_tickets_work <number-or-slug>
346
+ You can also specify a ticket: /ppm:tickets_work <number-or-slug>
349
347
 
350
348
  This unified command handles both opening new tickets and resuming in-progress work.`;
351
349
  } else if (ticketTool.type === "create") {
@@ -397,7 +395,7 @@ Call the \`${ticketTool.name}\` tool with the ticket number or slug.`;
397
395
  ]
398
396
  };
399
397
  }
400
- if (promptName === "system_prompts") {
398
+ if (promptName === "prompts_list") {
401
399
  return {
402
400
  description: "List all available prompts",
403
401
  messages: [
@@ -407,14 +405,14 @@ Call the \`${ticketTool.name}\` tool with the ticket number or slug.`;
407
405
  type: "text",
408
406
  text: `List all available prompts.
409
407
 
410
- Call the \`system_prompts\` tool with optional \`search\` parameter to filter results.`
408
+ Call the \`prompts_list\` tool with optional \`search\` parameter to filter results.`
411
409
  }
412
410
  }
413
411
  ]
414
412
  };
415
413
  }
416
- const runPromptMatch = promptName.match(/^system_run_prompt\s+(.+)$/);
417
- if (promptName === "system_run_prompt" || runPromptMatch) {
414
+ const runPromptMatch = promptName.match(/^prompts_run\s+(.+)$/);
415
+ if (promptName === "prompts_run" || runPromptMatch) {
418
416
  let promptContent;
419
417
  let description;
420
418
  if (runPromptMatch) {
@@ -422,19 +420,19 @@ Call the \`system_prompts\` tool with optional \`search\` parameter to filter re
422
420
  description = `Execute prompt "${promptSlug}"`;
423
421
  promptContent = `Execute the "${promptSlug}" prompt.
424
422
 
425
- Call the \`system_run_prompt\` tool with slug: "${promptSlug}".`;
423
+ Call the \`prompts_run\` tool with slug: "${promptSlug}".`;
426
424
  } else {
427
425
  description = "Execute a prompt by slug";
428
426
  promptContent = `Execute a prompt by slug.
429
427
 
430
428
  ## Usage
431
- Call the \`system_run_prompt\` tool with the prompt slug.
429
+ Call the \`prompts_run\` tool with the prompt slug.
432
430
 
433
431
  ## Available Prompts
434
- Use \`system_prompts\` to list all available prompts.
432
+ Use \`prompts_list\` to list all available prompts.
435
433
 
436
434
  ## Example
437
- /ppm:system_run_prompt code-review
435
+ /ppm:prompts_run code-review
438
436
 
439
437
  This will execute the "code-review" prompt.`;
440
438
  }
@@ -451,7 +449,7 @@ This will execute the "code-review" prompt.`;
451
449
  ]
452
450
  };
453
451
  }
454
- throw new Error(`Unknown prompt: ${promptName}. Use system_run_prompt to execute prompts.`);
452
+ throw new Error(`Unknown prompt: ${promptName}. Use prompts_run to execute prompts.`);
455
453
  });
456
454
  server.setRequestHandler(ListToolsRequestSchema, async () => {
457
455
  const tools = [
@@ -558,10 +556,10 @@ This will execute the "code-review" prompt.`;
558
556
  inputSchema
559
557
  };
560
558
  }),
561
- // Global system_run_prompt tool
559
+ // Global prompts_run tool
562
560
  {
563
- name: "system_run_prompt",
564
- description: "Execute a prompt by slug. Use system_prompts to list available prompts.",
561
+ name: "prompts_run",
562
+ description: "Execute a prompt by slug. Use prompts_list to list available prompts.",
565
563
  inputSchema: {
566
564
  type: "object",
567
565
  properties: {
@@ -573,9 +571,9 @@ This will execute the "code-review" prompt.`;
573
571
  required: ["slug"]
574
572
  }
575
573
  },
576
- // Global system_prompts tool
574
+ // Global prompts_list tool
577
575
  {
578
- name: "system_prompts",
576
+ name: "prompts_list",
579
577
  description: "List all available prompts",
580
578
  inputSchema: {
581
579
  type: "object",
@@ -587,9 +585,9 @@ This will execute the "code-review" prompt.`;
587
585
  }
588
586
  }
589
587
  },
590
- // Global system_prompts_update tool (CLI-based prompt editing)
588
+ // Global prompts_update tool (CLI-based prompt editing)
591
589
  {
592
- name: "system_prompts_update",
590
+ name: "prompts_update",
593
591
  description: "Read or update a prompt. Without content: returns prompt with context and editing guidance. With content: saves new version.",
594
592
  inputSchema: {
595
593
  type: "object",
@@ -625,8 +623,8 @@ This will execute the "code-review" prompt.`;
625
623
  });
626
624
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
627
625
  const toolName = request.params.name;
628
- if (toolName === "system_run_prompt") {
629
- const parsedArgs = parseRunPromptArgs(request.params.arguments);
626
+ if (toolName === "prompts_run") {
627
+ const parsedArgs = parsePromptsRunArgs(request.params.arguments);
630
628
  if (!parsedArgs) {
631
629
  return {
632
630
  content: [
@@ -634,7 +632,7 @@ This will execute the "code-review" prompt.`;
634
632
  type: "text",
635
633
  text: `Error: Missing 'slug' parameter. Provide prompt slug (e.g., 'code-review', 'plan').
636
634
 
637
- Use \`system_prompts\` to list available prompts.`
635
+ Use \`prompts_list\` to list available prompts.`
638
636
  }
639
637
  ],
640
638
  isError: true
@@ -668,7 +666,7 @@ Use \`system_prompts\` to list available prompts.`
668
666
 
669
667
  ${suggestionsList}
670
668
 
671
- Example: \`system_run_prompt ${error.suggestions[0]}\``
669
+ Example: \`prompts_run ${error.suggestions[0]}\``
672
670
  }
673
671
  ],
674
672
  isError: true
@@ -687,8 +685,8 @@ Example: \`system_run_prompt ${error.suggestions[0]}\``
687
685
  };
688
686
  }
689
687
  }
690
- if (toolName === "system_prompts") {
691
- const { search: searchTerm } = parseSystemPromptsArgs(request.params.arguments);
688
+ if (toolName === "prompts_list") {
689
+ const { search: searchTerm } = parsePromptsListArgs(request.params.arguments);
692
690
  let filteredPrompts = [...accountScopedPrompts];
693
691
  if (searchTerm) {
694
692
  const lowerSearch = searchTerm.toLowerCase();
@@ -716,8 +714,8 @@ No prompts found.`
716
714
  ]
717
715
  };
718
716
  }
719
- if (toolName === "system_prompts_update") {
720
- const parsedArgs = parseSystemPromptsUpdateArgs(request.params.arguments);
717
+ if (toolName === "prompts_update") {
718
+ const parsedArgs = parsePromptsUpdateArgs(request.params.arguments);
721
719
  if (!parsedArgs) {
722
720
  return {
723
721
  content: [
@@ -726,8 +724,8 @@ No prompts found.`
726
724
  text: `Error: Missing 'slug' parameter. Provide prompt slug to read or update.
727
725
 
728
726
  Usage:
729
- - Read mode: system_prompts_update { slug: "my-prompt" }
730
- - Write mode: system_prompts_update { slug: "my-prompt", content: "new content..." }`
727
+ - Read mode: prompts_update { slug: "my-prompt" }
728
+ - Write mode: prompts_update { slug: "my-prompt", content: "new content..." }`
731
729
  }
732
730
  ],
733
731
  isError: true
@@ -797,7 +795,7 @@ ${JSON.stringify(readResult.context, null, 2)}
797
795
  ${readResult.editingGuidance}
798
796
 
799
797
  ---
800
- _To update this prompt, call system_prompts_update with content parameter._`
798
+ _To update this prompt, call prompts_update with content parameter._`
801
799
  }
802
800
  ]
803
801
  };
@@ -823,7 +821,7 @@ Updated: ${writeResult.updatedAt}`
823
821
  };
824
822
  } catch (error) {
825
823
  const errorMessage = error instanceof Error ? error.message : "Unknown error";
826
- console.error(`[MCP] system_prompts_update error:`, error);
824
+ console.error(`[MCP] prompts_update error:`, error);
827
825
  return {
828
826
  content: [
829
827
  {
@@ -1297,7 +1295,7 @@ _Ticket content has been appended with your update._`
1297
1295
  };
1298
1296
  }
1299
1297
  }
1300
- throw new Error(`Unknown tool: ${toolName}. Use system_run_prompt to execute prompts by name, or check available tools.`);
1298
+ throw new Error(`Unknown tool: ${toolName}. Use prompts_run to execute prompts by name, or check available tools.`);
1301
1299
  });
1302
1300
  const transport = new StdioServerTransport();
1303
1301
  await server.connect(transport);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/convex-client.ts","../src/server.ts","../src/types.ts","../src/prompt-builder.ts"],"sourcesContent":["import minimist from \"minimist\";\nimport { createConvexClient } from \"./convex-client.js\";\nimport { startServer } from \"./server.js\";\nimport type { ServerConfig } from \"./types.js\";\n\n/**\n * Main entry point for the MCP server\n */\nasync function main() {\n try {\n // Parse CLI arguments\n const argv = minimist(process.argv.slice(2));\n const isDev = argv.dev === true;\n\n // Check for project token authentication\n const projectToken = process.env.PPM_PROJECT_TOKEN;\n\n if (!projectToken) {\n console.error(\n \"[MCP] ERROR: Missing authentication. Set PPM_PROJECT_TOKEN environment variable.\"\n );\n console.error(\n \"[MCP] Example:\"\n );\n console.error(\"[MCP] export PPM_PROJECT_TOKEN=wst_... # Get from project settings\");\n process.exit(1);\n }\n\n console.error(\"[MCP] Auth mode: Project Token\");\n\n // Create Convex client\n const convexClient = createConvexClient(isDev);\n const convexUrl = isDev\n ? \"https://hallowed-shrimp-344.convex.cloud\"\n : \"https://trustworthy-squirrel-735.convex.cloud\";\n\n // Build server config\n const config: ServerConfig = {\n projectToken,\n isDev,\n convexUrl,\n };\n\n // Start server - this will keep the process alive via stdio transport\n await startServer(config, convexClient);\n\n // This line should never be reached if the promise in startServer never resolves\n console.error(\"[MCP] WARNING: startServer promise resolved unexpectedly!\");\n } catch (error) {\n console.error(\n \"[MCP] FATAL ERROR:\",\n error instanceof Error ? error.message : \"Unknown error\"\n );\n if (error instanceof Error && error.stack) {\n console.error(error.stack);\n }\n process.exit(1);\n }\n}\n\n// Handle graceful shutdown\nprocess.on(\"SIGINT\", () => {\n console.error(\"[MCP] Received SIGINT, shutting down gracefully...\");\n process.exit(0);\n});\n\nprocess.on(\"SIGTERM\", () => {\n console.error(\"[MCP] Received SIGTERM, shutting down gracefully...\");\n process.exit(0);\n});\n\nmain();\n","import { ConvexHttpClient } from \"convex/browser\";\n\nconst PROD_URL = \"https://trustworthy-squirrel-735.convex.cloud\";\nconst DEV_URL = \"https://hallowed-shrimp-344.convex.cloud\";\n\n/**\n * Create a Convex HTTP client for the appropriate deployment\n */\nexport function createConvexClient(isDev: boolean): ConvexHttpClient {\n const url = isDev ? DEV_URL : PROD_URL;\n return new ConvexHttpClient(url);\n}\n","import { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport {\n CallToolRequestSchema,\n GetPromptRequestSchema,\n ListPromptsRequestSchema,\n ListToolsRequestSchema,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport type { ConvexHttpClient } from \"convex/browser\";\nimport type {\n ServerConfig,\n ProjectTokenValidation,\n WorkTicketResult,\n AccessDeniedResult,\n CreateTicketResult,\n CloseTicketResult,\n UpdateTicketResult,\n GetTicketResult,\n ListTicketItem,\n SearchTicketItem,\n} from \"./types.js\";\nimport {\n parseWorkArgs,\n parseCloseArgs,\n parseCreateArgs,\n parseSearchArgs,\n parseGetArgs,\n parseUpdateArgs,\n parseRunPromptArgs,\n parseSystemPromptsArgs,\n parseSystemPromptsUpdateArgs,\n isAccessDenied,\n} from \"./types.js\";\nimport type { UpdatePromptResult } from \"./types.js\";\n\n/**\n * Helper type for Convex HTTP client with string-based function references.\n * This allows us to use string function names while maintaining type safety\n * for the response types through explicit casting.\n */\ntype ConvexClientWithStringRefs = ConvexHttpClient & {\n mutation: <T>(name: string, args: Record<string, unknown>) => Promise<T>;\n query: <T>(name: string, args: Record<string, unknown>) => Promise<T>;\n};\n\nimport { fetchAndExecuteAccountScopedPrompt, AmbiguousPromptError, fetchAccountScopedPromptMetadataByToken } from \"./prompt-builder.js\";\n// Note: McpCommandMetadata removed - commands resource eliminated\n// Note: buildPromptNameFromMetadata, fetchAndExecutePrompt removed - prompts now account-scoped\n\n/**\n * Build authentication arguments for Convex mutations/queries.\n * All auth is now via project token.\n */\nfunction buildAuthArgs(config: ServerConfig): { projectToken: string } {\n return { projectToken: config.projectToken };\n}\n\n/**\n * Built-in system tools (no global tools - all are project-scoped now)\n */\nconst SYSTEM_TOOLS: {\n name: string;\n description: string;\n inputSchema: object;\n promptContent: string;\n isSlashCommand: boolean; // true = appears as /ppm:name, false = tool only\n}[] = [\n // All tools are now project-scoped (see dynamic*Tools arrays below)\n];\n\n/**\n * Initialize and start the MCP server\n */\nexport async function startServer(\n config: ServerConfig,\n convexClientRaw: ConvexHttpClient\n): Promise<void> {\n // Cast to our typed client interface for string-based function references\n const convexClient = convexClientRaw as ConvexClientWithStringRefs;\n // Validate project token authentication\n console.error(\"[MCP] Validating project token...\");\n const validation = await validateProjectToken(convexClient, config.projectToken);\n if (!validation.valid) {\n throw new Error(`Invalid project token: ${validation.error}`);\n }\n const tokenProjectSlug = validation.projectSlug;\n console.error(`[MCP] Project token validated for project \"${tokenProjectSlug}\"`);\n // Token mode provides full prompt access via the token owner's account\n\n // Fetch account-scoped prompts (global MCP tools)\n // Account-scoped prompts: Available as global tools (e.g., \"code-review\")\n // Token mode: Single project for tickets (determined by token)\n console.error(\"[MCP] Fetching prompt metadata...\");\n\n // Project token mode - full prompt access via token's owner\n // The token's ownerId is used to look up all account-scoped prompts\n const accountScopedPrompts = await fetchAccountScopedPromptMetadataByToken(\n convexClient,\n config.projectToken\n );\n console.error(`[MCP] Project token mode: loaded ${accountScopedPrompts.length} prompts`);\n\n // Single project for tickets (determined by token)\n const projectsForTickets = [{ slug: tokenProjectSlug!, name: tokenProjectSlug! }];\n\n console.error(`[MCP] Found ${accountScopedPrompts.length} account-scoped prompts (global tools)`);\n console.error(`[MCP] Ticket project scope: ${tokenProjectSlug} (token-scoped)`);\n\n if (accountScopedPrompts.length === 0) {\n console.error(\n \"[MCP] WARNING: No prompts found. Create prompts in the 'prompts' section to expose them via MCP.\"\n );\n }\n\n // Build dynamic ticket tools per project\n // Ticket tools remain project-scoped (e.g., \"projects_tickets_work\")\n const projectSlugs = new Set(projectsForTickets.map((p) => p.slug));\n const dynamicTicketTools: { name: string; description: string; slashDescription: string; projectSlug: string; type: 'work' | 'close' | 'create' | 'search' | 'get' | 'list' | 'update' }[] = [];\n\n for (const projectSlug of projectSlugs) {\n // Unified work command (replaces both tickets_open and tickets_work)\n dynamicTicketTools.push({\n name: `${projectSlug}_tickets_work`,\n description: `Get work from the \"${projectSlug}\" project. With no args: gets next ticket from open queue. With ticket slug/number: opens or resumes that specific ticket.`,\n slashDescription: `Get next ticket from queue, or work on specific ticket by slug/number`,\n projectSlug,\n type: 'work',\n });\n dynamicTicketTools.push({\n name: `${projectSlug}_tickets_close`,\n description: `Mark a working ticket as completed in the \"${projectSlug}\" project`,\n slashDescription: `Mark a working ticket as completed`,\n projectSlug,\n type: 'close',\n });\n dynamicTicketTools.push({\n name: `${projectSlug}_tickets_create`,\n description: `Create a new ticket in the \"${projectSlug}\" project queue`,\n slashDescription: `Create a new ticket in the backlog queue`,\n projectSlug,\n type: 'create',\n });\n dynamicTicketTools.push({\n name: `${projectSlug}_tickets_search`,\n description: `Search for tickets by content in the \"${projectSlug}\" project`,\n slashDescription: `Search for tickets by content`,\n projectSlug,\n type: 'search',\n });\n dynamicTicketTools.push({\n name: `${projectSlug}_tickets_get`,\n description: `Get a specific ticket by number or slug from \"${projectSlug}\" (read-only)`,\n slashDescription: `Get a specific ticket by number or slug (read-only)`,\n projectSlug,\n type: 'get',\n });\n dynamicTicketTools.push({\n name: `${projectSlug}_tickets_list`,\n description: `List active tickets in the \"${projectSlug}\" project (backlog + open + working)`,\n slashDescription: `List active tickets (backlog + open + working)`,\n projectSlug,\n type: 'list',\n });\n dynamicTicketTools.push({\n name: `${projectSlug}_tickets_update`,\n description: `Update a ticket in the \"${projectSlug}\" project by appending content with timestamp`,\n slashDescription: `Update a ticket by appending content with timestamp`,\n projectSlug,\n type: 'update',\n });\n }\n\n console.error(`[MCP] Registering ${dynamicTicketTools.length} ticket tools for ${projectSlugs.size} project(s)...`);\n\n // Build global system tools (not project-scoped)\n // run_prompt and system_prompts are now global since prompts are account-scoped\n const globalSystemTools: { name: string; description: string; slashDescription: string }[] = [\n {\n name: \"system_run_prompt\",\n description: \"Execute a prompt by slug. Use system_prompts to list available prompts.\",\n slashDescription: \"Execute a prompt by slug. Use system_prompts to list available prompts.\",\n },\n {\n name: \"system_prompts\",\n description: \"List all available prompts\",\n slashDescription: \"List all available prompts\",\n },\n {\n name: \"system_prompts_update\",\n description: \"Read or update a prompt. Without content: returns prompt with context and editing guidance. With content: saves new version.\",\n slashDescription: \"Read or update a prompt by slug\",\n },\n ];\n\n console.error(`[MCP] Registering ${globalSystemTools.length} global system tools...`);\n\n // Build dynamic per-prompt tools (each prompt as its own tool)\n // Now global: Direct invocation like `code-review` instead of `projects:code-review`\n const dynamicPromptTools: {\n name: string; // \"code-review\" (global, no project prefix)\n description: string;\n promptSlug: string;\n }[] = [];\n\n for (const prompt of accountScopedPrompts) {\n // Build description with optional folder prefix\n const folderPrefix = prompt.folderSlug ? `[${prompt.folderSlug}] ` : '';\n const baseDescription = prompt.description || `Execute the \"${prompt.slug}\" prompt`;\n\n dynamicPromptTools.push({\n name: prompt.slug, // Global tool name, no project prefix\n description: folderPrefix + baseDescription,\n promptSlug: prompt.slug,\n });\n }\n\n console.error(`[MCP] Registering ${dynamicPromptTools.length} per-prompt tools (global)...`);\n\n // Note: Commands resource removed - users create slash commands directly from flattened prompts\n\n // Check for duplicate prompt names (now global, no project prefix)\n const promptNames = new Set<string>();\n const duplicates: string[] = [];\n accountScopedPrompts.forEach((p) => {\n if (promptNames.has(p.slug)) {\n duplicates.push(p.slug);\n }\n promptNames.add(p.slug);\n });\n\n if (duplicates.length > 0) {\n console.error(\n `[MCP] WARNING: Duplicate prompt slugs detected: ${duplicates.join(\", \")}. Only the first occurrence will be registered.`\n );\n }\n\n // Create MCP server\n const server = new Server(\n {\n name: \"ppm-mcp-server\",\n version: \"3.1.0\",\n },\n {\n capabilities: {\n prompts: {},\n tools: {},\n },\n }\n );\n\n // Register list_prompts handler\n // Prompts are now global (account-scoped), tickets remain project-scoped\n server.setRequestHandler(ListPromptsRequestSchema, async () => {\n // Build ticket prompt schemas (project-scoped)\n const ticketPromptSchemas = dynamicTicketTools.map((tt) => ({\n name: tt.name,\n description: tt.slashDescription,\n }));\n\n // Build global system prompt schemas\n const systemPromptsSchemas = globalSystemTools.map((st) => ({\n name: st.name,\n description: st.slashDescription,\n }));\n\n return {\n prompts: [\n ...systemPromptsSchemas, // system_prompts, system_run_prompt (global)\n ...ticketPromptSchemas, // project_tickets_* (project-scoped)\n ],\n };\n });\n\n // Register get_prompt handler\n server.setRequestHandler(GetPromptRequestSchema, async (request) => {\n const promptName = request.params.name;\n\n // Check for system tools first (as prompts for slash command access)\n const systemTool = SYSTEM_TOOLS.find((st) => st.name === promptName);\n if (systemTool) {\n return {\n description: systemTool.description,\n messages: [\n {\n role: \"user\" as const,\n content: {\n type: \"text\" as const,\n text: systemTool.promptContent,\n },\n },\n ],\n };\n }\n\n // Check for ticket commands (Ticket 135, 149, 153 - handle as slash commands)\n // Handle exact matches and _work with optional argument (e.g., \"project_tickets_work 102\")\n const ticketTool = dynamicTicketTools.find((tt) => tt.name === promptName);\n const ticketWorkMatch = promptName.match(/^(.+)_tickets_work\\s+(.+)$/);\n\n if (ticketTool || ticketWorkMatch) {\n let promptContent: string;\n let description: string;\n\n if (ticketWorkMatch) {\n // Handle tickets_work with specific ticket argument\n const projectSlug = ticketWorkMatch[1];\n const ticketArg = ticketWorkMatch[2].trim();\n description = `Work on ticket \"${ticketArg}\" from \"${projectSlug}\"`;\n promptContent = `Get work on ticket \"${ticketArg}\" from the \"${projectSlug}\" project.\\n\\nCall the \\`${projectSlug}_tickets_work\\` tool with ticketSlug: \"${ticketArg}\".\\n\\nThis will open the ticket if it's in the open queue, or resume it if already working.`;\n } else if (ticketTool!.type === 'work') {\n // Unified work command (replaces open + work)\n description = ticketTool!.description;\n promptContent = `Get work from the \"${ticketTool!.projectSlug}\" project.\\n\\nCall the \\`${ticketTool!.name}\\` tool to get the next ticket from the open queue.\\n\\nYou can also specify a ticket: /ppm:${ticketTool!.projectSlug}_tickets_work <number-or-slug>\\n\\nThis unified command handles both opening new tickets and resuming in-progress work.`;\n } else if (ticketTool!.type === 'create') {\n // Ticket 149: tickets:create slash command\n description = ticketTool!.description;\n promptContent = `Create a new ticket in the \"${ticketTool!.projectSlug}\" project queue.\n\n## How This Works\nThe user provides **instructions** (not raw content). You interpret those instructions to generate the ticket.\n\n## Instructions\n1. **Read the user's request** - they may reference a file, ask you to summarize a session, or describe what they want\n2. **Process the input** - read files, extract relevant sections, summarize as needed\n3. **Generate ticket content** with a clear, descriptive title as the first line\n4. **Call the tool** with the generated content\n\n## Content Format\n\\`\\`\\`\n[Clear descriptive title - this becomes the slug]\n\n[Body content - tasks, description, context, etc.]\n\\`\\`\\`\n\n## Examples\n- \"create a ticket from /path/to/plan.md\" → Read file, use as ticket content\n- \"summarize our brainstorm into a ticket\" → Extract key points from conversation\n- \"create a ticket for the auth bug we discussed\" → Generate from session context\n- \"just the tasks from this file\" → Extract only task items\n\nCall the \\`${ticketTool!.name}\\` tool with the final generated content.`;\n } else if (ticketTool!.type === 'close') {\n description = ticketTool!.description;\n promptContent = `Close a working ticket in the \"${ticketTool!.projectSlug}\" project.\\n\\nCall the \\`${ticketTool!.name}\\` tool with the ticket number or slug.`;\n } else {\n // Fallback for unknown type\n description = ticketTool!.description;\n promptContent = `Use the \\`${ticketTool!.name}\\` tool.`;\n }\n\n return {\n description,\n messages: [\n {\n role: \"user\" as const,\n content: {\n type: \"text\" as const,\n text: promptContent,\n },\n },\n ],\n };\n }\n\n // Check for global system_prompts command\n if (promptName === \"system_prompts\") {\n return {\n description: \"List all available prompts\",\n messages: [\n {\n role: \"user\" as const,\n content: {\n type: \"text\" as const,\n text: `List all available prompts.\n\nCall the \\`system_prompts\\` tool with optional \\`search\\` parameter to filter results.`,\n },\n },\n ],\n };\n }\n\n // Check for global system_run_prompt command (with optional prompt slug argument)\n const runPromptMatch = promptName.match(/^system_run_prompt\\s+(.+)$/);\n\n if (promptName === \"system_run_prompt\" || runPromptMatch) {\n let promptContent: string;\n let description: string;\n\n if (runPromptMatch) {\n // Handle run_prompt with specific prompt slug argument\n const promptSlug = runPromptMatch[1].trim();\n description = `Execute prompt \"${promptSlug}\"`;\n promptContent = `Execute the \"${promptSlug}\" prompt.\\n\\nCall the \\`system_run_prompt\\` tool with slug: \"${promptSlug}\".`;\n } else {\n // Base run_prompt without argument - show help\n description = \"Execute a prompt by slug\";\n promptContent = `Execute a prompt by slug.\n\n## Usage\nCall the \\`system_run_prompt\\` tool with the prompt slug.\n\n## Available Prompts\nUse \\`system_prompts\\` to list all available prompts.\n\n## Example\n/ppm:system_run_prompt code-review\n\nThis will execute the \"code-review\" prompt.`;\n }\n\n return {\n description,\n messages: [\n {\n role: \"user\" as const,\n content: {\n type: \"text\" as const,\n text: promptContent,\n },\n },\n ],\n };\n }\n\n // Note: Command handling removed - commands resource eliminated\n\n // Unknown prompt\n throw new Error(`Unknown prompt: ${promptName}. Use system_run_prompt to execute prompts.`);\n });\n\n // Register list_tools handler\n // Note: Per-prompt tool registration removed in favor of project-scoped run_prompt tools\n // This reduces token overhead and allows dynamic prompt discovery without server restart\n server.setRequestHandler(ListToolsRequestSchema, async () => {\n // Build tools array: system tools + ticket tools + project:run_prompt tools\n // Individual prompts no longer registered as separate tools\n const tools = [\n // System tools with full input schemas\n ...SYSTEM_TOOLS.map((st) => ({\n name: st.name,\n description: st.description,\n inputSchema: st.inputSchema,\n })),\n // Dynamic ticket tools per project (Ticket 135, 149, 151, 153, unified work)\n ...dynamicTicketTools.map((tt) => {\n // Build inputSchema based on tool type\n let inputSchema: { type: \"object\"; properties: Record<string, object>; required: string[] };\n\n if (tt.type === 'close') {\n // close requires a ticketSlug\n inputSchema = {\n type: \"object\" as const,\n properties: {\n ticketSlug: {\n type: \"string\",\n description: \"Ticket number (e.g., '102') or full slug (e.g., '102-fix-auth')\",\n },\n },\n required: [\"ticketSlug\"],\n };\n } else if (tt.type === 'work') {\n // Unified work command (replaces open + work)\n inputSchema = {\n type: \"object\" as const,\n properties: {\n ticketSlug: {\n type: \"string\",\n description: \"Optional: Ticket number (e.g., '102') or full slug. If not provided, gets next ticket from open queue.\",\n },\n },\n required: [],\n };\n } else if (tt.type === 'create') {\n inputSchema = {\n type: \"object\" as const,\n properties: {\n content: {\n type: \"string\",\n description: \"The generated ticket content. First line should be a clear descriptive title (becomes the slug). Body contains tasks, description, context as needed.\",\n },\n tags: {\n type: \"array\",\n items: { type: \"string\" },\n description: \"Optional array of tag slugs to assign (e.g., [\\\"bug\\\", \\\"urgent\\\"]). Invalid tags are ignored.\",\n },\n },\n required: [\"content\"],\n };\n } else if (tt.type === 'search') {\n // Ticket 155: tickets:search\n inputSchema = {\n type: \"object\" as const,\n properties: {\n query: {\n type: \"string\",\n description: \"Search query (min 3 characters)\",\n },\n },\n required: [\"query\"],\n };\n } else if (tt.type === 'get') {\n // Ticket 155: tickets:get (read-only inspection)\n inputSchema = {\n type: \"object\" as const,\n properties: {\n ticketSlug: {\n type: \"string\",\n description: \"Ticket number (e.g., '102') or full slug\",\n },\n },\n required: [\"ticketSlug\"],\n };\n } else if (tt.type === 'list') {\n // Ticket 155: tickets:list (no parameters - shows active queue)\n inputSchema = {\n type: \"object\" as const,\n properties: {},\n required: [],\n };\n } else if (tt.type === 'update') {\n // Ticket 293: tickets:update (append content to ticket)\n inputSchema = {\n type: \"object\" as const,\n properties: {\n ticketSlug: {\n type: \"string\",\n description: \"Ticket number (e.g., '102') or full slug (e.g., '102-fix-auth')\",\n },\n content: {\n type: \"string\",\n description: \"Update content to append to the ticket (markdown supported)\",\n },\n },\n required: [\"ticketSlug\", \"content\"],\n };\n } else {\n // Fallback: no params\n inputSchema = {\n type: \"object\" as const,\n properties: {},\n required: [],\n };\n }\n\n return {\n name: tt.name,\n description: tt.description,\n inputSchema,\n };\n }),\n // Global system_run_prompt tool\n {\n name: \"system_run_prompt\",\n description: \"Execute a prompt by slug. Use system_prompts to list available prompts.\",\n inputSchema: {\n type: \"object\" as const,\n properties: {\n slug: {\n type: \"string\",\n description: \"Prompt slug to execute (e.g., 'code-review', 'plan')\",\n },\n },\n required: [\"slug\"],\n },\n },\n // Global system_prompts tool\n {\n name: \"system_prompts\",\n description: \"List all available prompts\",\n inputSchema: {\n type: \"object\" as const,\n properties: {\n search: {\n type: \"string\",\n description: \"Optional search term to filter prompts by name or description (case-insensitive)\",\n },\n },\n },\n },\n // Global system_prompts_update tool (CLI-based prompt editing)\n {\n name: \"system_prompts_update\",\n description: \"Read or update a prompt. Without content: returns prompt with context and editing guidance. With content: saves new version.\",\n inputSchema: {\n type: \"object\" as const,\n properties: {\n slug: {\n type: \"string\",\n description: \"Prompt slug to read or update (supports fuzzy matching)\",\n },\n content: {\n type: \"string\",\n description: \"New prompt content (omit for read-only mode)\",\n },\n changeLogMessage: {\n type: \"string\",\n description: \"Optional changelog message for the version\",\n },\n },\n required: [\"slug\"],\n },\n },\n // Dynamic per-prompt tools (each prompt as its own global tool)\n ...dynamicPromptTools.map((pt) => ({\n name: pt.name,\n description: pt.description,\n inputSchema: {\n type: \"object\" as const,\n properties: {},\n required: [],\n },\n })),\n ];\n\n return { tools };\n });\n\n // Register call_tool handler\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const toolName = request.params.name;\n\n // Handle global system_run_prompt tool\n if (toolName === \"system_run_prompt\") {\n const parsedArgs = parseRunPromptArgs(request.params.arguments);\n\n if (!parsedArgs) {\n return {\n content: [\n {\n type: \"text\",\n text: `Error: Missing 'slug' parameter. Provide prompt slug (e.g., 'code-review', 'plan').\n\nUse \\`system_prompts\\` to list available prompts.`,\n },\n ],\n isError: true,\n };\n }\n\n const { slug: promptSlug } = parsedArgs;\n\n // Execute the prompt using account-scoped lookup\n try {\n const result = await fetchAndExecuteAccountScopedPrompt(\n promptSlug,\n config,\n convexClient\n );\n\n // Convert prompt result to tool result format\n const promptText = result.messages\n .map((msg) => msg.content.text)\n .join('\\n\\n');\n\n return {\n content: [\n {\n type: \"text\",\n text: promptText,\n },\n ],\n };\n } catch (error) {\n // Handle ambiguous prompt matches with helpful suggestions\n if (error instanceof AmbiguousPromptError) {\n const suggestionsList = error.suggestions.map((s) => ` • ${s}`).join(\"\\n\");\n console.error(`[MCP] ${toolName} ambiguous match:`, error.suggestions);\n return {\n content: [\n {\n type: \"text\",\n text: `Multiple prompts match \"${promptSlug}\". Please specify one of:\\n\\n${suggestionsList}\\n\\nExample: \\`system_run_prompt ${error.suggestions[0]}\\``,\n },\n ],\n isError: true,\n };\n }\n\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] ${toolName} error:`, error);\n return {\n content: [\n {\n type: \"text\",\n text: `Error executing prompt \"${promptSlug}\": ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n }\n\n // Handle global system_prompts tool (account-scoped prompt listing)\n // Uses cached metadata (lightweight) - no need to fetch full content for listing\n if (toolName === \"system_prompts\") {\n const { search: searchTerm } = parseSystemPromptsArgs(request.params.arguments);\n\n // Use account-scoped prompts (global, no project filter)\n let filteredPrompts = [...accountScopedPrompts];\n\n // Filter by search term if provided\n if (searchTerm) {\n const lowerSearch = searchTerm.toLowerCase();\n filteredPrompts = filteredPrompts.filter(\n (p) =>\n p.slug.toLowerCase().includes(lowerSearch) ||\n p.description?.toLowerCase().includes(lowerSearch)\n );\n }\n\n // Sort prompts by slug\n filteredPrompts.sort((a, b) => a.slug.localeCompare(b.slug));\n\n // Build user prompts list\n const userPromptList = filteredPrompts\n .map((p) => {\n const desc = p.description || \"No description\";\n return `• ${p.slug}\\n Description: ${desc}`;\n })\n .join(\"\\n\\n\");\n\n const summary = searchTerm\n ? `Found ${filteredPrompts.length} prompt(s) matching \"${searchTerm}\":`\n : `Available prompts (${filteredPrompts.length} total):`;\n\n return {\n content: [\n {\n type: \"text\",\n text: userPromptList\n ? `${summary}\\n\\n${userPromptList}`\n : `${summary}\\n\\nNo prompts found.`,\n },\n ],\n };\n }\n\n // Handle global system_prompts_update tool (CLI-based prompt editing)\n if (toolName === \"system_prompts_update\") {\n const parsedArgs = parseSystemPromptsUpdateArgs(request.params.arguments);\n\n if (!parsedArgs) {\n return {\n content: [\n {\n type: \"text\",\n text: `Error: Missing 'slug' parameter. Provide prompt slug to read or update.\n\nUsage:\n- Read mode: system_prompts_update { slug: \"my-prompt\" }\n- Write mode: system_prompts_update { slug: \"my-prompt\", content: \"new content...\" }`,\n },\n ],\n isError: true,\n };\n }\n\n try {\n const result = await convexClient.mutation<UpdatePromptResult>(\n \"mcp_prompts:updateMcpPrompt\",\n {\n ...buildAuthArgs(config),\n promptSlug: parsedArgs.slug,\n content: parsedArgs.content,\n changeLogMessage: parsedArgs.changeLogMessage,\n }\n );\n\n if (!result.success) {\n // Handle error (not found, ambiguous)\n const errorResult = result as { success: false; error: string; suggestions?: string[] };\n if (errorResult.suggestions) {\n const suggestionsList = errorResult.suggestions.map((s) => ` • ${s}`).join(\"\\n\");\n return {\n content: [\n {\n type: \"text\",\n text: `${errorResult.error}\\n\\nDid you mean one of these?\\n${suggestionsList}`,\n },\n ],\n isError: true,\n };\n }\n return {\n content: [{ type: \"text\", text: errorResult.error }],\n isError: true,\n };\n }\n\n // Handle read mode response\n if (result.mode === \"read\") {\n const readResult = result as {\n mode: \"read\";\n matchType: string;\n slug: string;\n description: string;\n content: string;\n version: number;\n context: object;\n editingGuidance: string;\n hasAiTags?: boolean;\n aiTags?: string[];\n };\n\n // Format the response with all context\n const matchNote = readResult.matchType !== \"exact\"\n ? `\\n_Note: Matched via ${readResult.matchType} match_`\n : \"\";\n\n // AI tag notice if tags detected\n const aiTagNotice = readResult.hasAiTags\n ? `\\n\\n## ⚡ AI Tags Detected\\nThis prompt contains AI tags that need processing: **${readResult.aiTags?.join(\", \")}**\\n\\nProcess these tags by reading the instructions in each tag, generating appropriate content, and saving the result with the content parameter.`\n : \"\";\n\n return {\n content: [\n {\n type: \"text\",\n text: `# Prompt: ${readResult.slug} (v${readResult.version})${matchNote}${aiTagNotice}\n\n## Description\n${readResult.description || \"No description\"}\n\n## Content\n\\`\\`\\`\n${readResult.content}\n\\`\\`\\`\n\n## Available Resources\n\\`\\`\\`json\n${JSON.stringify(readResult.context, null, 2)}\n\\`\\`\\`\n\n${readResult.editingGuidance}\n\n---\n_To update this prompt, call system_prompts_update with content parameter._`,\n },\n ],\n };\n }\n\n // Handle write mode response\n if (result.mode === \"write\") {\n const writeResult = result as {\n mode: \"write\";\n slug: string;\n version: number;\n updatedAt: string;\n message: string;\n };\n\n return {\n content: [\n {\n type: \"text\",\n text: `✅ ${writeResult.message}\n\nPrompt: ${writeResult.slug}\nVersion: ${writeResult.version}\nUpdated: ${writeResult.updatedAt}`,\n },\n ],\n };\n }\n\n // Shouldn't reach here, but handle gracefully\n return {\n content: [{ type: \"text\", text: \"Unknown response format\" }],\n isError: true,\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] system_prompts_update error:`, error);\n return {\n content: [\n {\n type: \"text\",\n text: `Error updating prompt: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n }\n\n // Handle ticket tool invocations (Ticket 135, 153, unified work)\n const ticketTool = dynamicTicketTools.find((tt) => tt.name === toolName);\n if (ticketTool) {\n if (ticketTool.type === 'work') {\n // Unified work command (replaces both open and work)\n const { ticketSlug } = parseWorkArgs(request.params.arguments);\n\n try {\n // Call mutation (not query) - this can move ticket from open to working\n const result = await convexClient.mutation(\n \"mcp_tickets:workMcpTicket\",\n {\n ...buildAuthArgs(config),\n projectSlug: ticketTool.projectSlug,\n ticketSlug, // Optional: specific ticket to work on\n }\n ) as WorkTicketResult | AccessDeniedResult | null;\n\n if (!result) {\n const message = ticketSlug\n ? `Ticket \"${ticketSlug}\" not found or not in open/working status in project \"${ticketTool.projectSlug}\".`\n : `No open tickets in project \"${ticketTool.projectSlug}\".`;\n return {\n content: [\n {\n type: \"text\",\n text: message,\n },\n ],\n };\n }\n\n // Handle access denied response\n if (isAccessDenied(result)) {\n return {\n content: [\n {\n type: \"text\",\n text: `Access denied: ${result.error}`,\n },\n ],\n isError: true,\n };\n }\n\n // Now TypeScript knows result is WorkTicketResult\n const workResult = result as WorkTicketResult;\n const startedInfo = workResult.startedAt\n ? `\\nStarted: ${new Date(workResult.startedAt).toISOString()}`\n : '';\n\n // Different footer based on whether this was a fresh open or resumption\n const statusNote = workResult.wasOpened\n ? `_Ticket moved to working status. ${workResult.remainingTickets} ticket(s) remaining in queue._`\n : `_Resuming work on this ticket. ${workResult.remainingTickets} ticket(s) in queue._`;\n\n return {\n content: [\n {\n type: \"text\",\n text: `# Ticket: ${workResult.slug} [WORKING]${startedInfo}\n\n${workResult.content}\n\n---\n${statusNote}`,\n },\n ],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] tickets_work error:`, error);\n return {\n content: [\n {\n type: \"text\",\n text: `Error getting work: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n } else if (ticketTool.type === 'create') {\n // Handle tickets:create (Ticket 149)\n const parsedArgs = parseCreateArgs(request.params.arguments);\n\n if (!parsedArgs) {\n return {\n content: [\n {\n type: \"text\",\n text: `Error: Missing content parameter. Provide the ticket content (first line becomes the slug).`,\n },\n ],\n isError: true,\n };\n }\n\n const { content, tags } = parsedArgs;\n\n try {\n const result = await convexClient.mutation(\n \"mcp_tickets:createMcpTicket\",\n {\n ...buildAuthArgs(config),\n projectSlug: ticketTool.projectSlug,\n content,\n tags,\n }\n ) as CreateTicketResult;\n\n return {\n content: [\n {\n type: \"text\",\n text: `✅ Created ticket [${result.slug}] in backlog for project \"${ticketTool.projectSlug}\".\n\nPosition: #${result.position} of ${result.totalBacklog} backlog tickets\nPreview: ${result.preview}\n\n_Ticket created in backlog. Use \\`tickets_work ${result.slug}\\` to move it to working status._`,\n },\n ],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] tickets_create error:`, error);\n return {\n content: [\n {\n type: \"text\",\n text: `Error creating ticket: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n } else if (ticketTool.type === 'close') {\n // Ticket 151: Handle tickets:close\n const parsedArgs = parseCloseArgs(request.params.arguments);\n\n if (!parsedArgs) {\n return {\n content: [\n {\n type: \"text\",\n text: `Error: Missing ticketSlug parameter. Usage: Provide a ticket number (e.g., \"102\") or full slug (e.g., \"102-fix-auth\").`,\n },\n ],\n isError: true,\n };\n }\n\n const { ticketSlug } = parsedArgs;\n\n try {\n const result = await convexClient.mutation(\n \"mcp_tickets:closeMcpTicket\",\n {\n ...buildAuthArgs(config),\n projectSlug: ticketTool.projectSlug,\n ticketSlug,\n }\n ) as CloseTicketResult;\n\n return {\n content: [\n {\n type: \"text\",\n text: `✅ Ticket [${result.slug}] closed in project \"${ticketTool.projectSlug}\".\n\nClosed at: ${new Date(result.closedAt).toISOString()}\n\n_Reminder: Ensure all embedded \\`[RUN_PROMPT ...]\\` directives were executed before closing._`,\n },\n ],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] tickets_close error:`, error);\n return {\n content: [\n {\n type: \"text\",\n text: `Error closing ticket: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n } else if (ticketTool.type === 'search') {\n // Ticket 155: Handle tickets:search\n const parsedArgs = parseSearchArgs(request.params.arguments);\n\n if (!parsedArgs) {\n return {\n content: [\n {\n type: \"text\",\n text: `Error: Search query must be at least 3 characters`,\n },\n ],\n isError: true,\n };\n }\n\n const { query } = parsedArgs;\n\n try {\n const result = await convexClient.query(\n \"mcp_tickets:searchMcpTickets\",\n {\n ...buildAuthArgs(config),\n projectSlug: ticketTool.projectSlug,\n query,\n }\n ) as SearchTicketItem[];\n\n if (result.length === 0) {\n return {\n content: [\n {\n type: \"text\",\n text: `No tickets found matching \"${query}\" in project \"${ticketTool.projectSlug}\".`,\n },\n ],\n };\n }\n\n // Format as list with status badges\n const formattedList = result\n .map((t) => {\n const statusBadge = t.status === 'open' ? '🟢' : t.status === 'working' ? '🟡' : t.status === 'closed' ? '⚫' : '⚪';\n const num = t.ticketNumber ? `#${t.ticketNumber}` : '';\n return `${statusBadge} ${num} ${t.slug}\\n ${t.matchSnippet}`;\n })\n .join('\\n\\n');\n\n return {\n content: [\n {\n type: \"text\",\n text: `Found ${result.length} ticket(s) matching \"${query}\":\\n\\n${formattedList}`,\n },\n ],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] tickets_search error:`, error);\n return {\n content: [\n {\n type: \"text\",\n text: `Error searching tickets: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n } else if (ticketTool.type === 'get') {\n // Ticket 155: Handle tickets:get (read-only inspection)\n const parsedArgs = parseGetArgs(request.params.arguments);\n\n if (!parsedArgs) {\n return {\n content: [\n {\n type: \"text\",\n text: `Error: Missing ticketSlug parameter. Provide a ticket number (e.g., \"102\") or full slug.`,\n },\n ],\n isError: true,\n };\n }\n\n const { ticketSlug } = parsedArgs;\n\n try {\n // Uses existing getMcpTicket query - read-only inspection\n const result = await convexClient.query(\n \"mcp_tickets:getMcpTicket\",\n {\n ...buildAuthArgs(config),\n projectSlug: ticketTool.projectSlug,\n ticketSlug,\n }\n ) as GetTicketResult | AccessDeniedResult | null;\n\n if (!result) {\n return {\n content: [\n {\n type: \"text\",\n text: `Ticket \"${ticketSlug}\" not found in project \"${ticketTool.projectSlug}\".`,\n },\n ],\n };\n }\n\n // Handle access denied response\n if (isAccessDenied(result)) {\n return {\n content: [\n {\n type: \"text\",\n text: `Access denied: ${result.error}`,\n },\n ],\n isError: true,\n };\n }\n\n // Now TypeScript knows result is GetTicketResult\n const getResult = result as GetTicketResult;\n\n // Build status line\n const statusBadge = getResult.status.toUpperCase();\n const startedInfo = getResult.startedAt ? `\\nStarted: ${new Date(getResult.startedAt).toISOString()}` : '';\n const closedInfo = getResult.closedAt ? `\\nClosed: ${new Date(getResult.closedAt).toISOString()}` : '';\n\n return {\n content: [\n {\n type: \"text\",\n text: `# Ticket: ${getResult.slug} [${statusBadge}]${startedInfo}${closedInfo}\n\n${getResult.content}\n\n---\n_Read-only inspection. Use tickets_work to start working on this ticket._`,\n },\n ],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] tickets_get error:`, error);\n return {\n content: [\n {\n type: \"text\",\n text: `Error getting ticket: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n } else if (ticketTool.type === 'list') {\n // Ticket 155: Handle tickets:list (active queue)\n try {\n const result = await convexClient.query(\n \"mcp_tickets:listMcpTickets\",\n {\n ...buildAuthArgs(config),\n projectSlug: ticketTool.projectSlug,\n }\n ) as ListTicketItem[];\n\n if (result.length === 0) {\n return {\n content: [\n {\n type: \"text\",\n text: `No active tickets in project \"${ticketTool.projectSlug}\". All tickets are closed or archived.`,\n },\n ],\n };\n }\n\n // Group by status for clear display\n const openTickets = result.filter(t => t.status === 'open');\n const workingTickets = result.filter(t => t.status === 'working');\n const backlogTickets = result.filter(t => t.status === 'backlog');\n\n const formatTicketLine = (t: typeof result[0]) => {\n const num = t.ticketNumber ? `#${t.ticketNumber}` : '';\n return ` ${t.position}. ${num} ${t.slug}\\n ${t.preview}`;\n };\n\n const sections: string[] = [];\n\n if (openTickets.length > 0) {\n sections.push(`**🟢 Open (${openTickets.length})**\\n${openTickets.map(formatTicketLine).join('\\n')}`);\n }\n if (workingTickets.length > 0) {\n sections.push(`**🟡 Working (${workingTickets.length})**\\n${workingTickets.map(formatTicketLine).join('\\n')}`);\n }\n if (backlogTickets.length > 0) {\n sections.push(`**⚪ Backlog (${backlogTickets.length})**\\n${backlogTickets.map(formatTicketLine).join('\\n')}`);\n }\n\n return {\n content: [\n {\n type: \"text\",\n text: `# Active Queue: ${ticketTool.projectSlug}\\n\\n${result.length} ticket(s) in queue\\n\\n${sections.join('\\n\\n')}`,\n },\n ],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] tickets_list error:`, error);\n return {\n content: [\n {\n type: \"text\",\n text: `Error listing tickets: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n } else if (ticketTool.type === 'update') {\n // Ticket 293: Handle tickets:update (append content to ticket)\n const parsedArgs = parseUpdateArgs(request.params.arguments);\n\n if (!parsedArgs) {\n // Determine which argument is missing for a more specific error message\n const args = request.params.arguments as Record<string, unknown> | undefined;\n const hasTicketSlug = typeof args?.ticketSlug === 'string' && args.ticketSlug;\n const hasContent = typeof args?.content === 'string' && args.content;\n\n if (!hasTicketSlug) {\n return {\n content: [\n {\n type: \"text\",\n text: `Error: Missing ticketSlug parameter. Provide a ticket number (e.g., \"102\") or full slug.`,\n },\n ],\n isError: true,\n };\n }\n if (!hasContent) {\n return {\n content: [\n {\n type: \"text\",\n text: `Error: Missing content parameter. Provide the update content to append to the ticket.`,\n },\n ],\n isError: true,\n };\n }\n // Should not reach here, but just in case\n return {\n content: [\n {\n type: \"text\",\n text: `Error: Missing required parameters.`,\n },\n ],\n isError: true,\n };\n }\n\n const { ticketSlug, content } = parsedArgs;\n\n try {\n const result = await convexClient.mutation(\n \"mcp_tickets:updateMcpTicket\",\n {\n ...buildAuthArgs(config),\n projectSlug: ticketTool.projectSlug,\n ticketSlug,\n content,\n }\n ) as UpdateTicketResult;\n\n return {\n content: [\n {\n type: \"text\",\n text: `✅ Ticket [${result.slug}] updated in project \"${ticketTool.projectSlug}\".\nUpdated at: ${new Date(result.updatedAt).toISOString()}\n_Ticket content has been appended with your update._`,\n },\n ],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] tickets_update error:`, error);\n return {\n content: [\n {\n type: \"text\",\n text: `Error updating ticket: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n }\n }\n\n // Handle per-prompt tool invocations (e.g., \"code-review\" - now global)\n const promptTool = dynamicPromptTools.find((pt) => pt.name === toolName);\n if (promptTool) {\n try {\n const result = await fetchAndExecuteAccountScopedPrompt(\n promptTool.promptSlug,\n config,\n convexClient\n );\n\n const promptText = result.messages\n .map((msg) => msg.content.text)\n .join('\\n\\n');\n\n return {\n content: [{ type: \"text\", text: promptText }],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] ${toolName} error:`, error);\n return {\n content: [{ type: \"text\", text: `Error executing prompt: ${errorMessage}` }],\n isError: true,\n };\n }\n }\n\n // Unknown tool\n throw new Error(`Unknown tool: ${toolName}. Use system_run_prompt to execute prompts by name, or check available tools.`);\n });\n\n // Start server with stdio transport\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n console.error(\"[MCP] Server started successfully\");\n console.error(`[MCP] Deployment: ${config.isDev ? \"DEVELOPMENT\" : \"PRODUCTION\"}`);\n console.error(`[MCP] Convex URL: ${config.convexUrl}`);\n console.error(`[MCP] Data mode: REAL-TIME (fetches fresh data on each invocation)`);\n\n // List all prompts\n const allPromptNames = [...Array.from(promptNames)].sort();\n console.error(`[MCP] Prompts available: ${allPromptNames.join(\", \")}`);\n console.error(`[MCP] - Total prompts: ${promptNames.size}`);\n\n // Keep the event loop alive with a heartbeat\n // This prevents Node from exiting when there are no active handles\n setInterval(() => {\n // Heartbeat every 60 seconds to keep process alive\n }, 60000);\n\n // Return a promise that never resolves to keep the server running\n return new Promise<void>(() => {\n // The transport handles stdin/stdout communication\n // The interval above keeps the event loop active\n });\n}\n\n/**\n * Validate project token with Convex\n *\n * Project tokens are now stored directly on the projects table.\n * Returns project details if valid, null if invalid.\n */\n/**\n * Response type from validateProjectToken query\n */\ninterface ValidateProjectTokenResponse {\n valid: boolean;\n projectId?: string;\n projectSlug?: string;\n ownerId?: string;\n}\n\nasync function validateProjectToken(\n client: ConvexHttpClient,\n token: string\n): Promise<ProjectTokenValidation> {\n try {\n // Cast to our typed client for string-based function references\n const typedClient = client as ConvexClientWithStringRefs;\n const result = await typedClient.query<ValidateProjectTokenResponse | null>(\n \"projects:validateProjectToken\",\n { token }\n );\n\n if (result && result.valid) {\n return {\n valid: true,\n projectId: result.projectId,\n projectSlug: result.projectSlug,\n ownerId: result.ownerId,\n };\n }\n return { valid: false, error: \"Invalid project token\" };\n } catch (error) {\n return {\n valid: false,\n error: error instanceof Error ? error.message : \"Unknown error\",\n };\n }\n}\n\n// Note: fetchMcpPromptMetadata removed - prompts now account-scoped\n// Note: fetchMcpCommands and buildCommandName removed - commands resource eliminated\n// Note: API key-based functions removed - all auth now uses project tokens\n","// ============================================================================\n// MCP Tool Argument Types\n// ============================================================================\n\n/**\n * Arguments for the tickets_work tool\n * Optional ticketSlug: if provided, work on specific ticket; otherwise get next from queue\n */\nexport interface WorkToolArgs {\n ticketSlug?: string;\n}\n\n/**\n * Arguments for the tickets_close tool\n * Required ticketSlug to identify which ticket to close\n */\nexport interface CloseToolArgs {\n ticketSlug: string;\n}\n\n/**\n * Arguments for the tickets_create tool\n * Required content where first line becomes the slug\n * Optional tags array for assigning existing tags at creation\n */\nexport interface CreateToolArgs {\n content: string;\n tags?: string[];\n}\n\n/**\n * Arguments for the tickets_search tool\n * Required query string (min 3 characters)\n */\nexport interface SearchToolArgs {\n query: string;\n}\n\n/**\n * Arguments for the tickets_get tool\n * Required ticketSlug to identify which ticket to retrieve\n */\nexport interface GetToolArgs {\n ticketSlug: string;\n}\n\n/**\n * Arguments for the tickets_list tool\n * No arguments required - returns all active tickets\n */\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface ListToolArgs {}\n\n/**\n * Arguments for the tickets_update tool\n * Required ticketSlug and content to append\n */\nexport interface UpdateToolArgs {\n ticketSlug: string;\n content: string;\n}\n\n/**\n * Arguments for the system_run_prompt tool\n * Required slug to identify which prompt to execute\n */\nexport interface RunPromptToolArgs {\n slug: string;\n}\n\n/**\n * Arguments for the system_prompts tool\n * Optional search term to filter prompts\n */\nexport interface SystemPromptsToolArgs {\n search?: string;\n}\n\n/**\n * Arguments for the system_prompts_update tool\n * Required slug, optional content for write mode\n */\nexport interface SystemPromptsUpdateToolArgs {\n slug: string;\n content?: string;\n changeLogMessage?: string;\n}\n\n// ============================================================================\n// Convex Response Types (for MCP mutations/queries)\n// ============================================================================\n\n/**\n * Response from workMcpTicket mutation\n */\nexport interface WorkTicketResult {\n slug: string;\n ticketNumber?: number;\n status: string;\n content: string;\n startedAt?: number;\n remainingTickets: number;\n wasOpened: boolean;\n}\n\n/**\n * Access denied response from ticket operations\n */\nexport interface AccessDeniedResult {\n error: string;\n accessDenied: true;\n}\n\n/**\n * Type guard to check if a result is an access denied response\n */\nexport function isAccessDenied(result: unknown): result is AccessDeniedResult {\n return (\n typeof result === 'object' &&\n result !== null &&\n 'accessDenied' in result &&\n (result as AccessDeniedResult).accessDenied === true\n );\n}\n\n/**\n * Response from createMcpTicket mutation\n */\nexport interface CreateTicketResult {\n slug: string;\n ticketNumber?: number;\n preview: string;\n position: number;\n totalBacklog: number;\n}\n\n/**\n * Response from closeMcpTicket mutation\n */\nexport interface CloseTicketResult {\n slug: string;\n status: string;\n closedAt: number;\n}\n\n/**\n * Response from updateMcpTicket mutation\n */\nexport interface UpdateTicketResult {\n slug: string;\n ticketNumber?: number;\n status: string;\n updatedAt: number;\n}\n\n/**\n * Response from getMcpTicket query\n */\nexport interface GetTicketResult {\n slug: string;\n ticketNumber?: number;\n content: string;\n status: string;\n createdAt: number;\n startedAt?: number;\n closedAt?: number;\n}\n\n/**\n * Response from listMcpTickets query\n */\nexport interface ListTicketItem {\n position: number;\n slug: string;\n ticketNumber?: number;\n status: string;\n preview: string;\n createdAt: number;\n startedAt?: number;\n}\n\n/**\n * Response from searchMcpTickets query\n */\nexport interface SearchTicketItem {\n slug: string;\n ticketNumber?: number;\n status: string;\n matchSnippet: string;\n createdAt: number;\n}\n\n/**\n * Response from prompt metadata query\n */\nexport interface PromptMetadata {\n slug: string;\n description?: string;\n folderSlug?: string;\n}\n\n/**\n * Response from updateMcpPrompt mutation (read mode)\n */\nexport interface UpdatePromptReadResult {\n success: true;\n mode: \"read\";\n matchType: \"exact\" | \"prefix\" | \"contains\";\n slug: string;\n description: string;\n content: string;\n version: number;\n context: {\n variables: Array<{ slug: string; reference: string; type: string; description: string }>;\n snippets: Array<{ slug: string; reference: string; description: string }>;\n prompts: Array<{ slug: string; reference: string; description: string }>;\n };\n editingGuidance: string;\n}\n\n/**\n * Response from updateMcpPrompt mutation (write mode)\n */\nexport interface UpdatePromptWriteResult {\n success: true;\n mode: \"write\";\n slug: string;\n version: number;\n updatedAt: string;\n message: string;\n}\n\n/**\n * Response from updateMcpPrompt mutation (error)\n */\nexport interface UpdatePromptErrorResult {\n success: false;\n error: string;\n suggestions?: string[];\n}\n\n/**\n * Union type for all updateMcpPrompt responses\n */\nexport type UpdatePromptResult =\n | UpdatePromptReadResult\n | UpdatePromptWriteResult\n | UpdatePromptErrorResult;\n\n/**\n * Account-scoped prompt match result types\n */\nexport type AccountScopedPromptResult =\n | { matchType: \"exact\" | \"prefix\" | \"contains\"; slug: string; description?: string; flattenedPrompt?: string }\n | { matchType: \"ambiguous\"; suggestions: string[] }\n | null;\n\n// ============================================================================\n// Argument Parsing Functions\n// ============================================================================\n\n/**\n * Parse and validate arguments for tickets_work tool\n */\nexport function parseWorkArgs(args: unknown): WorkToolArgs {\n const parsed = args as Record<string, unknown> | undefined;\n return {\n ticketSlug: typeof parsed?.ticketSlug === 'string' ? parsed.ticketSlug : undefined,\n };\n}\n\n/**\n * Parse and validate arguments for tickets_close tool\n * Returns null if required argument is missing\n */\nexport function parseCloseArgs(args: unknown): CloseToolArgs | null {\n const parsed = args as Record<string, unknown> | undefined;\n const ticketSlug = typeof parsed?.ticketSlug === 'string' ? parsed.ticketSlug : undefined;\n if (!ticketSlug) return null;\n return { ticketSlug };\n}\n\n/**\n * Parse and validate arguments for tickets_create tool\n * Returns null if required argument is missing\n */\nexport function parseCreateArgs(args: unknown): CreateToolArgs | null {\n const parsed = args as Record<string, unknown> | undefined;\n const content = typeof parsed?.content === 'string' ? parsed.content : undefined;\n if (!content) return null;\n\n // Parse optional tags array - filter to valid strings only\n let tags: string[] | undefined;\n if (Array.isArray(parsed?.tags)) {\n tags = parsed.tags.filter((t): t is string => typeof t === 'string');\n if (tags.length === 0) tags = undefined;\n }\n\n return { content, tags };\n}\n\n/**\n * Parse and validate arguments for tickets_search tool\n * Returns null if required argument is missing or too short\n */\nexport function parseSearchArgs(args: unknown): SearchToolArgs | null {\n const parsed = args as Record<string, unknown> | undefined;\n const query = typeof parsed?.query === 'string' ? parsed.query : undefined;\n if (!query || query.length < 3) return null;\n return { query };\n}\n\n/**\n * Parse and validate arguments for tickets_get tool\n * Returns null if required argument is missing\n */\nexport function parseGetArgs(args: unknown): GetToolArgs | null {\n const parsed = args as Record<string, unknown> | undefined;\n const ticketSlug = typeof parsed?.ticketSlug === 'string' ? parsed.ticketSlug : undefined;\n if (!ticketSlug) return null;\n return { ticketSlug };\n}\n\n/**\n * Parse arguments for tickets_list tool (no args required)\n */\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nexport function parseListArgs(args: unknown): ListToolArgs {\n return {};\n}\n\n/**\n * Parse and validate arguments for tickets_update tool\n * Returns null if any required argument is missing\n */\nexport function parseUpdateArgs(args: unknown): UpdateToolArgs | null {\n const parsed = args as Record<string, unknown> | undefined;\n const ticketSlug = typeof parsed?.ticketSlug === 'string' ? parsed.ticketSlug : undefined;\n const content = typeof parsed?.content === 'string' ? parsed.content : undefined;\n if (!ticketSlug || !content) return null;\n return { ticketSlug, content };\n}\n\n/**\n * Parse and validate arguments for system_run_prompt tool\n * Returns null if required argument is missing\n */\nexport function parseRunPromptArgs(args: unknown): RunPromptToolArgs | null {\n const parsed = args as Record<string, unknown> | undefined;\n const slug = typeof parsed?.slug === 'string' ? parsed.slug : undefined;\n if (!slug) return null;\n return { slug };\n}\n\n/**\n * Parse arguments for system_prompts tool\n */\nexport function parseSystemPromptsArgs(args: unknown): SystemPromptsToolArgs {\n const parsed = args as Record<string, unknown> | undefined;\n return {\n search: typeof parsed?.search === 'string' ? parsed.search : undefined,\n };\n}\n\n/**\n * Parse and validate arguments for system_prompts_update tool\n * Returns null if required slug is missing\n */\nexport function parseSystemPromptsUpdateArgs(args: unknown): SystemPromptsUpdateToolArgs | null {\n const parsed = args as Record<string, unknown> | undefined;\n const slug = typeof parsed?.slug === 'string' ? parsed.slug : undefined;\n if (!slug) return null;\n return {\n slug,\n content: typeof parsed?.content === 'string' ? parsed.content : undefined,\n changeLogMessage: typeof parsed?.changeLogMessage === 'string' ? parsed.changeLogMessage : undefined,\n };\n}\n\n// ============================================================================\n// Legacy Types (kept for backwards compatibility)\n// ============================================================================\n\n/**\n * @deprecated Legacy type - prompts are now account-scoped (no projectSlug)\n * Kept for backwards compatibility during transition period.\n */\nexport interface McpPrompt {\n slug: string;\n name: string;\n description?: string;\n flattenedPrompt?: string;\n folderPath?: string; // Folder organization (used for ZIP downloads, not slash commands)\n projectSlug?: string; // @deprecated - prompts are now account-scoped\n projectName?: string; // @deprecated - prompts are now account-scoped\n}\n\n/**\n * @deprecated Legacy type - prompts are now account-scoped (no projectSlug)\n * Kept for backwards compatibility during transition period.\n */\nexport interface McpPromptMetadata {\n slug: string;\n description?: string;\n projectSlug?: string; // @deprecated - prompts are now account-scoped\n}\n\n// Note: McpCommandMetadata and McpCommandFull removed - commands resource eliminated\n\n/**\n * @deprecated Legacy type - prompts are now account-scoped (no projectSlug)\n * Kept for backwards compatibility during transition period.\n */\nexport interface McpPromptFull {\n slug: string;\n description?: string;\n flattenedPrompt?: string;\n projectSlug?: string; // @deprecated - prompts are now account-scoped\n}\n\n/**\n * @deprecated Legacy type - use account-scoped prompt queries instead\n * Kept for backwards compatibility during transition period.\n */\nexport type PromptMatchResult =\n | { matchType: \"exact\" | \"prefix\" | \"contains\"; slug: string; description?: string; flattenedPrompt?: string; projectSlug: string }\n | { matchType: \"ambiguous\"; suggestions: string[] }\n | null;\n\n/**\n * Configuration for the MCP server\n *\n * Authentication: Project Token (PPM_PROJECT_TOKEN)\n * Provides scoped access to a single project for both prompts and tickets.\n * The token's ownerId determines which prompts are accessible (account-scoped).\n *\n * Note: API Key auth (PPM_API_KEY) has been deprecated.\n * All authentication now uses project tokens.\n */\nexport interface ServerConfig {\n projectToken: string; // Required - scoped access to a single project\n isDev: boolean;\n convexUrl: string;\n}\n\n/**\n * Result of project token validation\n * Token is now stored directly on the projects table (single token per project)\n */\nexport interface ProjectTokenValidation {\n valid: boolean;\n projectId?: string;\n projectSlug?: string;\n ownerId?: string;\n error?: string;\n}\n","import type { ConvexHttpClient } from \"convex/browser\";\nimport type { ServerConfig, PromptMetadata, AccountScopedPromptResult } from \"./types.js\";\n\n/**\n * Helper type for Convex HTTP client with string-based function references.\n * This allows us to use string function names while maintaining type safety\n * for the response types through explicit casting.\n */\ntype ConvexClientWithStringRefs = ConvexHttpClient & {\n query: <T>(name: string, args: Record<string, unknown>) => Promise<T>;\n};\n\n/**\n * Sanitizes a string for use in MCP tool names.\n * Ensures output matches MCP protocol pattern: ^[a-zA-Z0-9_-]{1,64}$\n *\n * @param str - Raw string to sanitize (project slug, folder path, or prompt slug)\n * @returns Sanitized string safe for MCP tool names\n */\nexport function sanitizeForMcp(str: string): string {\n return str\n .trim() // Remove leading/trailing whitespace first\n .toLowerCase() // Convert to lowercase for consistency\n .replace(/[^a-z0-9_-]/g, '-') // Replace ALL invalid chars (including spaces!) with hyphens\n .replace(/-+/g, '-') // Collapse multiple consecutive hyphens into one\n .replace(/^-+|-+$/g, '') // Remove leading and trailing hyphens\n .trim(); // Final trim to ensure no whitespace\n}\n\n// Note: The following legacy functions have been removed as prompts are now account-scoped:\n// - buildPromptName (was project:prompt format, now prompts are global)\n// - buildPromptNameFromMetadata (was project:prompt format)\n// - buildPromptSchema (used buildPromptName)\n// - buildPromptHandler (used project-scoped getMcpPrompts query)\n// - fetchAndExecutePrompt (used project-scoped getMcpPromptBySlug query)\n//\n// Use fetchAndExecuteAccountScopedPrompt instead for all prompt execution.\n\n/**\n * Custom error class for ambiguous prompt matches\n * Contains suggestions for the user to choose from\n */\nexport class AmbiguousPromptError extends Error {\n suggestions: string[];\n\n constructor(promptSlug: string, suggestions: string[]) {\n super(`Multiple prompts match \"${promptSlug}\". Please be more specific.`);\n this.name = \"AmbiguousPromptError\";\n this.suggestions = suggestions;\n }\n}\n\n/**\n * Fetch account-scoped prompt metadata using project token auth.\n *\n * @param client - Convex HTTP client\n * @param projectToken - Project token for authentication\n * @returns Account-scoped prompts for this project\n */\nexport async function fetchAccountScopedPromptMetadataByToken(\n client: ConvexHttpClient,\n projectToken: string\n): Promise<PromptMetadata[]> {\n try {\n // Cast to our typed client for string-based function references\n const typedClient = client as ConvexClientWithStringRefs;\n const metadata = await typedClient.query<PromptMetadata[]>(\n \"mcp_prompts:getAccountScopedPromptMetadataByToken\",\n { projectToken }\n );\n return metadata;\n } catch (error) {\n throw new Error(\n `Failed to fetch prompts via project token: ${error instanceof Error ? error.message : \"Unknown error\"}`\n );\n }\n}\n\n// Note: AccountScopedPromptResult type is imported from ./types.js\n\n/**\n * Fetch a single account-scoped prompt and execute it\n *\n * Account-scoped prompts are global (no project prefix required).\n * This is for prompts that don't have a projectId.\n *\n * Uses project token authentication: access via token's ownerId.\n *\n * Matching algorithm supports exact, prefix, and contains matching.\n */\nexport async function fetchAndExecuteAccountScopedPrompt(\n promptSlug: string,\n config: ServerConfig,\n convexClient: ConvexHttpClient\n): Promise<{ messages: Array<{ role: string; content: { type: \"text\"; text: string } }> }> {\n // Cast to our typed client for string-based function references\n const typedClient = convexClient as ConvexClientWithStringRefs;\n\n // Project token auth - access via token's owner\n const result = await typedClient.query<AccountScopedPromptResult>(\n \"mcp_prompts:getAccountScopedPromptBySlugByToken\",\n {\n projectToken: config.projectToken,\n promptSlug,\n }\n );\n\n // Handle null - no matches found\n if (!result) {\n throw new Error(\n `Prompt \"${promptSlug}\" not found. Use system:prompts to list available prompts.`\n );\n }\n\n // Handle ambiguous matches - throw custom error with suggestions\n if (result.matchType === \"ambiguous\") {\n throw new AmbiguousPromptError(promptSlug, result.suggestions);\n }\n\n // Handle successful match (exact, prefix, or contains)\n if (!result.flattenedPrompt) {\n throw new Error(\n `Prompt \"${result.slug}\" has no flattened content. Please re-save the prompt to regenerate it.`\n );\n }\n\n // Log match type for debugging\n const matchNote = result.matchType === \"exact\"\n ? \"exact match\"\n : result.matchType === \"prefix\"\n ? `prefix match → ${result.slug}`\n : `contains match → ${result.slug}`;\n console.error(`[MCP] Fetched account-scoped prompt: ${promptSlug} (${matchNote})`);\n\n // Return raw flattened prompt (client will parse YAML front matter)\n return {\n messages: [\n {\n role: \"user\",\n content: {\n type: \"text\" as const,\n text: result.flattenedPrompt,\n },\n },\n ],\n };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/convex-client.ts","../src/server.ts","../src/types.ts","../src/prompt-builder.ts"],"sourcesContent":["import minimist from \"minimist\";\nimport { createConvexClient } from \"./convex-client.js\";\nimport { startServer } from \"./server.js\";\nimport type { ServerConfig } from \"./types.js\";\n\n/**\n * Main entry point for the MCP server\n */\nasync function main() {\n try {\n // Parse CLI arguments\n const argv = minimist(process.argv.slice(2));\n const isDev = argv.dev === true;\n\n // Check for project token authentication\n const projectToken = process.env.PPM_PROJECT_TOKEN;\n\n if (!projectToken) {\n console.error(\n \"[MCP] ERROR: Missing authentication. Set PPM_PROJECT_TOKEN environment variable.\"\n );\n console.error(\n \"[MCP] Example:\"\n );\n console.error(\"[MCP] export PPM_PROJECT_TOKEN=wst_... # Get from project settings\");\n process.exit(1);\n }\n\n console.error(\"[MCP] Auth mode: Project Token\");\n\n // Create Convex client\n const convexClient = createConvexClient(isDev);\n const convexUrl = isDev\n ? \"https://hallowed-shrimp-344.convex.cloud\"\n : \"https://trustworthy-squirrel-735.convex.cloud\";\n\n // Build server config\n const config: ServerConfig = {\n projectToken,\n isDev,\n convexUrl,\n };\n\n // Start server - this will keep the process alive via stdio transport\n await startServer(config, convexClient);\n\n // This line should never be reached if the promise in startServer never resolves\n console.error(\"[MCP] WARNING: startServer promise resolved unexpectedly!\");\n } catch (error) {\n console.error(\n \"[MCP] FATAL ERROR:\",\n error instanceof Error ? error.message : \"Unknown error\"\n );\n if (error instanceof Error && error.stack) {\n console.error(error.stack);\n }\n process.exit(1);\n }\n}\n\n// Handle graceful shutdown\nprocess.on(\"SIGINT\", () => {\n console.error(\"[MCP] Received SIGINT, shutting down gracefully...\");\n process.exit(0);\n});\n\nprocess.on(\"SIGTERM\", () => {\n console.error(\"[MCP] Received SIGTERM, shutting down gracefully...\");\n process.exit(0);\n});\n\nmain();\n","import { ConvexHttpClient } from \"convex/browser\";\n\nconst PROD_URL = \"https://trustworthy-squirrel-735.convex.cloud\";\nconst DEV_URL = \"https://hallowed-shrimp-344.convex.cloud\";\n\n/**\n * Create a Convex HTTP client for the appropriate deployment\n */\nexport function createConvexClient(isDev: boolean): ConvexHttpClient {\n const url = isDev ? DEV_URL : PROD_URL;\n return new ConvexHttpClient(url);\n}\n","import { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport {\n CallToolRequestSchema,\n GetPromptRequestSchema,\n ListPromptsRequestSchema,\n ListToolsRequestSchema,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport type { ConvexHttpClient } from \"convex/browser\";\nimport type {\n ServerConfig,\n ProjectTokenValidation,\n WorkTicketResult,\n AccessDeniedResult,\n CreateTicketResult,\n CloseTicketResult,\n UpdateTicketResult,\n GetTicketResult,\n ListTicketItem,\n SearchTicketItem,\n} from \"./types.js\";\nimport {\n parseWorkArgs,\n parseCloseArgs,\n parseCreateArgs,\n parseSearchArgs,\n parseGetArgs,\n parseUpdateArgs,\n parsePromptsRunArgs,\n parsePromptsListArgs,\n parsePromptsUpdateArgs,\n isAccessDenied,\n} from \"./types.js\";\nimport type { UpdatePromptResult } from \"./types.js\";\n\n/**\n * Helper type for Convex HTTP client with string-based function references.\n * This allows us to use string function names while maintaining type safety\n * for the response types through explicit casting.\n */\ntype ConvexClientWithStringRefs = ConvexHttpClient & {\n mutation: <T>(name: string, args: Record<string, unknown>) => Promise<T>;\n query: <T>(name: string, args: Record<string, unknown>) => Promise<T>;\n};\n\nimport { fetchAndExecuteAccountScopedPrompt, AmbiguousPromptError, fetchAccountScopedPromptMetadataByToken } from \"./prompt-builder.js\";\n// Note: McpCommandMetadata removed - commands resource eliminated\n// Note: buildPromptNameFromMetadata, fetchAndExecutePrompt removed - prompts now account-scoped\n\n/**\n * Build authentication arguments for Convex mutations/queries.\n * All auth is now via project token.\n */\nfunction buildAuthArgs(config: ServerConfig): { projectToken: string } {\n return { projectToken: config.projectToken };\n}\n\n/**\n * Built-in system tools (no global tools - all are project-scoped now)\n */\nconst SYSTEM_TOOLS: {\n name: string;\n description: string;\n inputSchema: object;\n promptContent: string;\n isSlashCommand: boolean; // true = appears as /ppm:name, false = tool only\n}[] = [\n // All tools are now project-scoped (see dynamic*Tools arrays below)\n];\n\n/**\n * Initialize and start the MCP server\n */\nexport async function startServer(\n config: ServerConfig,\n convexClientRaw: ConvexHttpClient\n): Promise<void> {\n // Cast to our typed client interface for string-based function references\n const convexClient = convexClientRaw as ConvexClientWithStringRefs;\n // Validate project token authentication\n console.error(\"[MCP] Validating project token...\");\n const validation = await validateProjectToken(convexClient, config.projectToken);\n if (!validation.valid) {\n throw new Error(`Invalid project token: ${validation.error}`);\n }\n const tokenProjectSlug = validation.projectSlug;\n console.error(`[MCP] Project token validated for project \"${tokenProjectSlug}\"`);\n // Token mode provides full prompt access via the token owner's account\n\n // Fetch account-scoped prompts (global MCP tools)\n // Account-scoped prompts: Available as global tools (e.g., \"code-review\")\n // Token mode: Single project for tickets (determined by token)\n console.error(\"[MCP] Fetching prompt metadata...\");\n\n // Project token mode - full prompt access via token's owner\n // The token's ownerId is used to look up all account-scoped prompts\n const accountScopedPrompts = await fetchAccountScopedPromptMetadataByToken(\n convexClient,\n config.projectToken\n );\n console.error(`[MCP] Project token mode: loaded ${accountScopedPrompts.length} prompts`);\n\n console.error(`[MCP] Found ${accountScopedPrompts.length} account-scoped prompts (global tools)`);\n console.error(`[MCP] Ticket project scope: ${tokenProjectSlug} (token-scoped)`);\n\n if (accountScopedPrompts.length === 0) {\n console.error(\n \"[MCP] WARNING: No prompts found. Create prompts in the 'prompts' section to expose them via MCP.\"\n );\n }\n\n // Build dynamic ticket tools (single set since token scopes to one project)\n // Tool names no longer prefixed with project slug - token already scopes access\n const projectSlug = tokenProjectSlug!;\n const dynamicTicketTools: { name: string; description: string; slashDescription: string; projectSlug: string; type: 'work' | 'close' | 'create' | 'search' | 'get' | 'list' | 'update' }[] = [\n // Unified work command (replaces both tickets_open and tickets_work)\n {\n name: `tickets_work`,\n description: `Get work from the \"${projectSlug}\" project. With no args: gets next ticket from open queue. With ticket slug/number: opens or resumes that specific ticket.`,\n slashDescription: `Get next ticket from queue, or work on specific ticket by slug/number`,\n projectSlug,\n type: 'work',\n },\n {\n name: `tickets_close`,\n description: `Mark a working ticket as completed in the \"${projectSlug}\" project`,\n slashDescription: `Mark a working ticket as completed`,\n projectSlug,\n type: 'close',\n },\n {\n name: `tickets_create`,\n description: `Create a new ticket in the \"${projectSlug}\" project queue`,\n slashDescription: `Create a new ticket in the backlog queue`,\n projectSlug,\n type: 'create',\n },\n {\n name: `tickets_search`,\n description: `Search for tickets by content in the \"${projectSlug}\" project`,\n slashDescription: `Search for tickets by content`,\n projectSlug,\n type: 'search',\n },\n {\n name: `tickets_get`,\n description: `Get a specific ticket by number or slug from \"${projectSlug}\" (read-only)`,\n slashDescription: `Get a specific ticket by number or slug (read-only)`,\n projectSlug,\n type: 'get',\n },\n {\n name: `tickets_list`,\n description: `List active tickets in the \"${projectSlug}\" project (backlog + open + working)`,\n slashDescription: `List active tickets (backlog + open + working)`,\n projectSlug,\n type: 'list',\n },\n {\n name: `tickets_update`,\n description: `Update a ticket in the \"${projectSlug}\" project by appending content with timestamp`,\n slashDescription: `Update a ticket by appending content with timestamp`,\n projectSlug,\n type: 'update',\n },\n ];\n\n console.error(`[MCP] Registering ${dynamicTicketTools.length} ticket tools...`);\n\n // Build global prompt tools (not project-scoped)\n // Renamed from system_* to prompts_* for clarity\n const globalPromptTools: { name: string; description: string; slashDescription: string }[] = [\n {\n name: \"prompts_run\",\n description: \"Execute a prompt by slug. Use prompts_list to list available prompts.\",\n slashDescription: \"Execute a prompt by slug. Use prompts_list to list available prompts.\",\n },\n {\n name: \"prompts_list\",\n description: \"List all available prompts\",\n slashDescription: \"List all available prompts\",\n },\n {\n name: \"prompts_update\",\n description: \"Read or update a prompt. Without content: returns prompt with context and editing guidance. With content: saves new version.\",\n slashDescription: \"Read or update a prompt by slug\",\n },\n ];\n\n console.error(`[MCP] Registering ${globalPromptTools.length} global prompt tools...`);\n\n // Build dynamic per-prompt tools (each prompt as its own tool)\n // Now global: Direct invocation like `code-review` instead of `projects:code-review`\n const dynamicPromptTools: {\n name: string; // \"code-review\" (global, no project prefix)\n description: string;\n promptSlug: string;\n }[] = [];\n\n for (const prompt of accountScopedPrompts) {\n // Build description with optional folder prefix\n const folderPrefix = prompt.folderSlug ? `[${prompt.folderSlug}] ` : '';\n const baseDescription = prompt.description || `Execute the \"${prompt.slug}\" prompt`;\n\n dynamicPromptTools.push({\n name: prompt.slug, // Global tool name, no project prefix\n description: folderPrefix + baseDescription,\n promptSlug: prompt.slug,\n });\n }\n\n console.error(`[MCP] Registering ${dynamicPromptTools.length} per-prompt tools (global)...`);\n\n // Note: Commands resource removed - users create slash commands directly from flattened prompts\n\n // Check for duplicate prompt names (now global, no project prefix)\n const promptNames = new Set<string>();\n const duplicates: string[] = [];\n accountScopedPrompts.forEach((p) => {\n if (promptNames.has(p.slug)) {\n duplicates.push(p.slug);\n }\n promptNames.add(p.slug);\n });\n\n if (duplicates.length > 0) {\n console.error(\n `[MCP] WARNING: Duplicate prompt slugs detected: ${duplicates.join(\", \")}. Only the first occurrence will be registered.`\n );\n }\n\n // Create MCP server\n const server = new Server(\n {\n name: \"ppm-mcp-server\",\n version: \"3.1.0\",\n },\n {\n capabilities: {\n prompts: {},\n tools: {},\n },\n }\n );\n\n // Register list_prompts handler\n // Prompts are now global (account-scoped), tickets remain project-scoped\n server.setRequestHandler(ListPromptsRequestSchema, async () => {\n // Build ticket prompt schemas (project-scoped)\n const ticketPromptSchemas = dynamicTicketTools.map((tt) => ({\n name: tt.name,\n description: tt.slashDescription,\n }));\n\n // Build global prompt schemas\n const promptToolSchemas = globalPromptTools.map((st) => ({\n name: st.name,\n description: st.slashDescription,\n }));\n\n return {\n prompts: [\n ...promptToolSchemas, // prompts_run, prompts_list, prompts_update (global)\n ...ticketPromptSchemas, // tickets_* (token-scoped)\n ],\n };\n });\n\n // Register get_prompt handler\n server.setRequestHandler(GetPromptRequestSchema, async (request) => {\n const promptName = request.params.name;\n\n // Check for system tools first (as prompts for slash command access)\n const systemTool = SYSTEM_TOOLS.find((st) => st.name === promptName);\n if (systemTool) {\n return {\n description: systemTool.description,\n messages: [\n {\n role: \"user\" as const,\n content: {\n type: \"text\" as const,\n text: systemTool.promptContent,\n },\n },\n ],\n };\n }\n\n // Check for ticket commands (Ticket 135, 149, 153 - handle as slash commands)\n // Handle exact matches and tickets_work with optional argument (e.g., \"tickets_work 102\")\n const ticketTool = dynamicTicketTools.find((tt) => tt.name === promptName);\n const ticketWorkMatch = promptName.match(/^tickets_work\\s+(.+)$/);\n\n if (ticketTool || ticketWorkMatch) {\n let promptContent: string;\n let description: string;\n\n if (ticketWorkMatch) {\n // Handle tickets_work with specific ticket argument\n const ticketArg = ticketWorkMatch[1].trim();\n description = `Work on ticket \"${ticketArg}\" from \"${projectSlug}\"`;\n promptContent = `Get work on ticket \"${ticketArg}\" from the \"${projectSlug}\" project.\\n\\nCall the \\`tickets_work\\` tool with ticketSlug: \"${ticketArg}\".\\n\\nThis will open the ticket if it's in the open queue, or resume it if already working.`;\n } else if (ticketTool!.type === 'work') {\n // Unified work command (replaces open + work)\n description = ticketTool!.description;\n promptContent = `Get work from the \"${ticketTool!.projectSlug}\" project.\\n\\nCall the \\`tickets_work\\` tool to get the next ticket from the open queue.\\n\\nYou can also specify a ticket: /ppm:tickets_work <number-or-slug>\\n\\nThis unified command handles both opening new tickets and resuming in-progress work.`;\n } else if (ticketTool!.type === 'create') {\n // Ticket 149: tickets:create slash command\n description = ticketTool!.description;\n promptContent = `Create a new ticket in the \"${ticketTool!.projectSlug}\" project queue.\n\n## How This Works\nThe user provides **instructions** (not raw content). You interpret those instructions to generate the ticket.\n\n## Instructions\n1. **Read the user's request** - they may reference a file, ask you to summarize a session, or describe what they want\n2. **Process the input** - read files, extract relevant sections, summarize as needed\n3. **Generate ticket content** with a clear, descriptive title as the first line\n4. **Call the tool** with the generated content\n\n## Content Format\n\\`\\`\\`\n[Clear descriptive title - this becomes the slug]\n\n[Body content - tasks, description, context, etc.]\n\\`\\`\\`\n\n## Examples\n- \"create a ticket from /path/to/plan.md\" → Read file, use as ticket content\n- \"summarize our brainstorm into a ticket\" → Extract key points from conversation\n- \"create a ticket for the auth bug we discussed\" → Generate from session context\n- \"just the tasks from this file\" → Extract only task items\n\nCall the \\`${ticketTool!.name}\\` tool with the final generated content.`;\n } else if (ticketTool!.type === 'close') {\n description = ticketTool!.description;\n promptContent = `Close a working ticket in the \"${ticketTool!.projectSlug}\" project.\\n\\nCall the \\`${ticketTool!.name}\\` tool with the ticket number or slug.`;\n } else {\n // Fallback for unknown type\n description = ticketTool!.description;\n promptContent = `Use the \\`${ticketTool!.name}\\` tool.`;\n }\n\n return {\n description,\n messages: [\n {\n role: \"user\" as const,\n content: {\n type: \"text\" as const,\n text: promptContent,\n },\n },\n ],\n };\n }\n\n // Check for global prompts_list command\n if (promptName === \"prompts_list\") {\n return {\n description: \"List all available prompts\",\n messages: [\n {\n role: \"user\" as const,\n content: {\n type: \"text\" as const,\n text: `List all available prompts.\n\nCall the \\`prompts_list\\` tool with optional \\`search\\` parameter to filter results.`,\n },\n },\n ],\n };\n }\n\n // Check for global prompts_run command (with optional prompt slug argument)\n const runPromptMatch = promptName.match(/^prompts_run\\s+(.+)$/);\n\n if (promptName === \"prompts_run\" || runPromptMatch) {\n let promptContent: string;\n let description: string;\n\n if (runPromptMatch) {\n // Handle prompts_run with specific prompt slug argument\n const promptSlug = runPromptMatch[1].trim();\n description = `Execute prompt \"${promptSlug}\"`;\n promptContent = `Execute the \"${promptSlug}\" prompt.\\n\\nCall the \\`prompts_run\\` tool with slug: \"${promptSlug}\".`;\n } else {\n // Base prompts_run without argument - show help\n description = \"Execute a prompt by slug\";\n promptContent = `Execute a prompt by slug.\n\n## Usage\nCall the \\`prompts_run\\` tool with the prompt slug.\n\n## Available Prompts\nUse \\`prompts_list\\` to list all available prompts.\n\n## Example\n/ppm:prompts_run code-review\n\nThis will execute the \"code-review\" prompt.`;\n }\n\n return {\n description,\n messages: [\n {\n role: \"user\" as const,\n content: {\n type: \"text\" as const,\n text: promptContent,\n },\n },\n ],\n };\n }\n\n // Note: Command handling removed - commands resource eliminated\n\n // Unknown prompt\n throw new Error(`Unknown prompt: ${promptName}. Use prompts_run to execute prompts.`);\n });\n\n // Register list_tools handler\n // Note: Per-prompt tool registration removed in favor of project-scoped run_prompt tools\n // This reduces token overhead and allows dynamic prompt discovery without server restart\n server.setRequestHandler(ListToolsRequestSchema, async () => {\n // Build tools array: system tools + ticket tools + project:run_prompt tools\n // Individual prompts no longer registered as separate tools\n const tools = [\n // System tools with full input schemas\n ...SYSTEM_TOOLS.map((st) => ({\n name: st.name,\n description: st.description,\n inputSchema: st.inputSchema,\n })),\n // Dynamic ticket tools per project (Ticket 135, 149, 151, 153, unified work)\n ...dynamicTicketTools.map((tt) => {\n // Build inputSchema based on tool type\n let inputSchema: { type: \"object\"; properties: Record<string, object>; required: string[] };\n\n if (tt.type === 'close') {\n // close requires a ticketSlug\n inputSchema = {\n type: \"object\" as const,\n properties: {\n ticketSlug: {\n type: \"string\",\n description: \"Ticket number (e.g., '102') or full slug (e.g., '102-fix-auth')\",\n },\n },\n required: [\"ticketSlug\"],\n };\n } else if (tt.type === 'work') {\n // Unified work command (replaces open + work)\n inputSchema = {\n type: \"object\" as const,\n properties: {\n ticketSlug: {\n type: \"string\",\n description: \"Optional: Ticket number (e.g., '102') or full slug. If not provided, gets next ticket from open queue.\",\n },\n },\n required: [],\n };\n } else if (tt.type === 'create') {\n inputSchema = {\n type: \"object\" as const,\n properties: {\n content: {\n type: \"string\",\n description: \"The generated ticket content. First line should be a clear descriptive title (becomes the slug). Body contains tasks, description, context as needed.\",\n },\n tags: {\n type: \"array\",\n items: { type: \"string\" },\n description: \"Optional array of tag slugs to assign (e.g., [\\\"bug\\\", \\\"urgent\\\"]). Invalid tags are ignored.\",\n },\n },\n required: [\"content\"],\n };\n } else if (tt.type === 'search') {\n // Ticket 155: tickets:search\n inputSchema = {\n type: \"object\" as const,\n properties: {\n query: {\n type: \"string\",\n description: \"Search query (min 3 characters)\",\n },\n },\n required: [\"query\"],\n };\n } else if (tt.type === 'get') {\n // Ticket 155: tickets:get (read-only inspection)\n inputSchema = {\n type: \"object\" as const,\n properties: {\n ticketSlug: {\n type: \"string\",\n description: \"Ticket number (e.g., '102') or full slug\",\n },\n },\n required: [\"ticketSlug\"],\n };\n } else if (tt.type === 'list') {\n // Ticket 155: tickets:list (no parameters - shows active queue)\n inputSchema = {\n type: \"object\" as const,\n properties: {},\n required: [],\n };\n } else if (tt.type === 'update') {\n // Ticket 293: tickets:update (append content to ticket)\n inputSchema = {\n type: \"object\" as const,\n properties: {\n ticketSlug: {\n type: \"string\",\n description: \"Ticket number (e.g., '102') or full slug (e.g., '102-fix-auth')\",\n },\n content: {\n type: \"string\",\n description: \"Update content to append to the ticket (markdown supported)\",\n },\n },\n required: [\"ticketSlug\", \"content\"],\n };\n } else {\n // Fallback: no params\n inputSchema = {\n type: \"object\" as const,\n properties: {},\n required: [],\n };\n }\n\n return {\n name: tt.name,\n description: tt.description,\n inputSchema,\n };\n }),\n // Global prompts_run tool\n {\n name: \"prompts_run\",\n description: \"Execute a prompt by slug. Use prompts_list to list available prompts.\",\n inputSchema: {\n type: \"object\" as const,\n properties: {\n slug: {\n type: \"string\",\n description: \"Prompt slug to execute (e.g., 'code-review', 'plan')\",\n },\n },\n required: [\"slug\"],\n },\n },\n // Global prompts_list tool\n {\n name: \"prompts_list\",\n description: \"List all available prompts\",\n inputSchema: {\n type: \"object\" as const,\n properties: {\n search: {\n type: \"string\",\n description: \"Optional search term to filter prompts by name or description (case-insensitive)\",\n },\n },\n },\n },\n // Global prompts_update tool (CLI-based prompt editing)\n {\n name: \"prompts_update\",\n description: \"Read or update a prompt. Without content: returns prompt with context and editing guidance. With content: saves new version.\",\n inputSchema: {\n type: \"object\" as const,\n properties: {\n slug: {\n type: \"string\",\n description: \"Prompt slug to read or update (supports fuzzy matching)\",\n },\n content: {\n type: \"string\",\n description: \"New prompt content (omit for read-only mode)\",\n },\n changeLogMessage: {\n type: \"string\",\n description: \"Optional changelog message for the version\",\n },\n },\n required: [\"slug\"],\n },\n },\n // Dynamic per-prompt tools (each prompt as its own global tool)\n ...dynamicPromptTools.map((pt) => ({\n name: pt.name,\n description: pt.description,\n inputSchema: {\n type: \"object\" as const,\n properties: {},\n required: [],\n },\n })),\n ];\n\n return { tools };\n });\n\n // Register call_tool handler\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const toolName = request.params.name;\n\n // Handle global prompts_run tool\n if (toolName === \"prompts_run\") {\n const parsedArgs = parsePromptsRunArgs(request.params.arguments);\n\n if (!parsedArgs) {\n return {\n content: [\n {\n type: \"text\",\n text: `Error: Missing 'slug' parameter. Provide prompt slug (e.g., 'code-review', 'plan').\n\nUse \\`prompts_list\\` to list available prompts.`,\n },\n ],\n isError: true,\n };\n }\n\n const { slug: promptSlug } = parsedArgs;\n\n // Execute the prompt using account-scoped lookup\n try {\n const result = await fetchAndExecuteAccountScopedPrompt(\n promptSlug,\n config,\n convexClient\n );\n\n // Convert prompt result to tool result format\n const promptText = result.messages\n .map((msg) => msg.content.text)\n .join('\\n\\n');\n\n return {\n content: [\n {\n type: \"text\",\n text: promptText,\n },\n ],\n };\n } catch (error) {\n // Handle ambiguous prompt matches with helpful suggestions\n if (error instanceof AmbiguousPromptError) {\n const suggestionsList = error.suggestions.map((s) => ` • ${s}`).join(\"\\n\");\n console.error(`[MCP] ${toolName} ambiguous match:`, error.suggestions);\n return {\n content: [\n {\n type: \"text\",\n text: `Multiple prompts match \"${promptSlug}\". Please specify one of:\\n\\n${suggestionsList}\\n\\nExample: \\`prompts_run ${error.suggestions[0]}\\``,\n },\n ],\n isError: true,\n };\n }\n\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] ${toolName} error:`, error);\n return {\n content: [\n {\n type: \"text\",\n text: `Error executing prompt \"${promptSlug}\": ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n }\n\n // Handle global prompts_list tool (account-scoped prompt listing)\n // Uses cached metadata (lightweight) - no need to fetch full content for listing\n if (toolName === \"prompts_list\") {\n const { search: searchTerm } = parsePromptsListArgs(request.params.arguments);\n\n // Use account-scoped prompts (global, no project filter)\n let filteredPrompts = [...accountScopedPrompts];\n\n // Filter by search term if provided\n if (searchTerm) {\n const lowerSearch = searchTerm.toLowerCase();\n filteredPrompts = filteredPrompts.filter(\n (p) =>\n p.slug.toLowerCase().includes(lowerSearch) ||\n p.description?.toLowerCase().includes(lowerSearch)\n );\n }\n\n // Sort prompts by slug\n filteredPrompts.sort((a, b) => a.slug.localeCompare(b.slug));\n\n // Build user prompts list\n const userPromptList = filteredPrompts\n .map((p) => {\n const desc = p.description || \"No description\";\n return `• ${p.slug}\\n Description: ${desc}`;\n })\n .join(\"\\n\\n\");\n\n const summary = searchTerm\n ? `Found ${filteredPrompts.length} prompt(s) matching \"${searchTerm}\":`\n : `Available prompts (${filteredPrompts.length} total):`;\n\n return {\n content: [\n {\n type: \"text\",\n text: userPromptList\n ? `${summary}\\n\\n${userPromptList}`\n : `${summary}\\n\\nNo prompts found.`,\n },\n ],\n };\n }\n\n // Handle global prompts_update tool (CLI-based prompt editing)\n if (toolName === \"prompts_update\") {\n const parsedArgs = parsePromptsUpdateArgs(request.params.arguments);\n\n if (!parsedArgs) {\n return {\n content: [\n {\n type: \"text\",\n text: `Error: Missing 'slug' parameter. Provide prompt slug to read or update.\n\nUsage:\n- Read mode: prompts_update { slug: \"my-prompt\" }\n- Write mode: prompts_update { slug: \"my-prompt\", content: \"new content...\" }`,\n },\n ],\n isError: true,\n };\n }\n\n try {\n const result = await convexClient.mutation<UpdatePromptResult>(\n \"mcp_prompts:updateMcpPrompt\",\n {\n ...buildAuthArgs(config),\n promptSlug: parsedArgs.slug,\n content: parsedArgs.content,\n changeLogMessage: parsedArgs.changeLogMessage,\n }\n );\n\n if (!result.success) {\n // Handle error (not found, ambiguous)\n const errorResult = result as { success: false; error: string; suggestions?: string[] };\n if (errorResult.suggestions) {\n const suggestionsList = errorResult.suggestions.map((s) => ` • ${s}`).join(\"\\n\");\n return {\n content: [\n {\n type: \"text\",\n text: `${errorResult.error}\\n\\nDid you mean one of these?\\n${suggestionsList}`,\n },\n ],\n isError: true,\n };\n }\n return {\n content: [{ type: \"text\", text: errorResult.error }],\n isError: true,\n };\n }\n\n // Handle read mode response\n if (result.mode === \"read\") {\n const readResult = result as {\n mode: \"read\";\n matchType: string;\n slug: string;\n description: string;\n content: string;\n version: number;\n context: object;\n editingGuidance: string;\n hasAiTags?: boolean;\n aiTags?: string[];\n };\n\n // Format the response with all context\n const matchNote = readResult.matchType !== \"exact\"\n ? `\\n_Note: Matched via ${readResult.matchType} match_`\n : \"\";\n\n // AI tag notice if tags detected\n const aiTagNotice = readResult.hasAiTags\n ? `\\n\\n## ⚡ AI Tags Detected\\nThis prompt contains AI tags that need processing: **${readResult.aiTags?.join(\", \")}**\\n\\nProcess these tags by reading the instructions in each tag, generating appropriate content, and saving the result with the content parameter.`\n : \"\";\n\n return {\n content: [\n {\n type: \"text\",\n text: `# Prompt: ${readResult.slug} (v${readResult.version})${matchNote}${aiTagNotice}\n\n## Description\n${readResult.description || \"No description\"}\n\n## Content\n\\`\\`\\`\n${readResult.content}\n\\`\\`\\`\n\n## Available Resources\n\\`\\`\\`json\n${JSON.stringify(readResult.context, null, 2)}\n\\`\\`\\`\n\n${readResult.editingGuidance}\n\n---\n_To update this prompt, call prompts_update with content parameter._`,\n },\n ],\n };\n }\n\n // Handle write mode response\n if (result.mode === \"write\") {\n const writeResult = result as {\n mode: \"write\";\n slug: string;\n version: number;\n updatedAt: string;\n message: string;\n };\n\n return {\n content: [\n {\n type: \"text\",\n text: `✅ ${writeResult.message}\n\nPrompt: ${writeResult.slug}\nVersion: ${writeResult.version}\nUpdated: ${writeResult.updatedAt}`,\n },\n ],\n };\n }\n\n // Shouldn't reach here, but handle gracefully\n return {\n content: [{ type: \"text\", text: \"Unknown response format\" }],\n isError: true,\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] prompts_update error:`, error);\n return {\n content: [\n {\n type: \"text\",\n text: `Error updating prompt: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n }\n\n // Handle ticket tool invocations (Ticket 135, 153, unified work)\n const ticketTool = dynamicTicketTools.find((tt) => tt.name === toolName);\n if (ticketTool) {\n if (ticketTool.type === 'work') {\n // Unified work command (replaces both open and work)\n const { ticketSlug } = parseWorkArgs(request.params.arguments);\n\n try {\n // Call mutation (not query) - this can move ticket from open to working\n const result = await convexClient.mutation(\n \"mcp_tickets:workMcpTicket\",\n {\n ...buildAuthArgs(config),\n projectSlug: ticketTool.projectSlug,\n ticketSlug, // Optional: specific ticket to work on\n }\n ) as WorkTicketResult | AccessDeniedResult | null;\n\n if (!result) {\n const message = ticketSlug\n ? `Ticket \"${ticketSlug}\" not found or not in open/working status in project \"${ticketTool.projectSlug}\".`\n : `No open tickets in project \"${ticketTool.projectSlug}\".`;\n return {\n content: [\n {\n type: \"text\",\n text: message,\n },\n ],\n };\n }\n\n // Handle access denied response\n if (isAccessDenied(result)) {\n return {\n content: [\n {\n type: \"text\",\n text: `Access denied: ${result.error}`,\n },\n ],\n isError: true,\n };\n }\n\n // Now TypeScript knows result is WorkTicketResult\n const workResult = result as WorkTicketResult;\n const startedInfo = workResult.startedAt\n ? `\\nStarted: ${new Date(workResult.startedAt).toISOString()}`\n : '';\n\n // Different footer based on whether this was a fresh open or resumption\n const statusNote = workResult.wasOpened\n ? `_Ticket moved to working status. ${workResult.remainingTickets} ticket(s) remaining in queue._`\n : `_Resuming work on this ticket. ${workResult.remainingTickets} ticket(s) in queue._`;\n\n return {\n content: [\n {\n type: \"text\",\n text: `# Ticket: ${workResult.slug} [WORKING]${startedInfo}\n\n${workResult.content}\n\n---\n${statusNote}`,\n },\n ],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] tickets_work error:`, error);\n return {\n content: [\n {\n type: \"text\",\n text: `Error getting work: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n } else if (ticketTool.type === 'create') {\n // Handle tickets:create (Ticket 149)\n const parsedArgs = parseCreateArgs(request.params.arguments);\n\n if (!parsedArgs) {\n return {\n content: [\n {\n type: \"text\",\n text: `Error: Missing content parameter. Provide the ticket content (first line becomes the slug).`,\n },\n ],\n isError: true,\n };\n }\n\n const { content, tags } = parsedArgs;\n\n try {\n const result = await convexClient.mutation(\n \"mcp_tickets:createMcpTicket\",\n {\n ...buildAuthArgs(config),\n projectSlug: ticketTool.projectSlug,\n content,\n tags,\n }\n ) as CreateTicketResult;\n\n return {\n content: [\n {\n type: \"text\",\n text: `✅ Created ticket [${result.slug}] in backlog for project \"${ticketTool.projectSlug}\".\n\nPosition: #${result.position} of ${result.totalBacklog} backlog tickets\nPreview: ${result.preview}\n\n_Ticket created in backlog. Use \\`tickets_work ${result.slug}\\` to move it to working status._`,\n },\n ],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] tickets_create error:`, error);\n return {\n content: [\n {\n type: \"text\",\n text: `Error creating ticket: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n } else if (ticketTool.type === 'close') {\n // Ticket 151: Handle tickets:close\n const parsedArgs = parseCloseArgs(request.params.arguments);\n\n if (!parsedArgs) {\n return {\n content: [\n {\n type: \"text\",\n text: `Error: Missing ticketSlug parameter. Usage: Provide a ticket number (e.g., \"102\") or full slug (e.g., \"102-fix-auth\").`,\n },\n ],\n isError: true,\n };\n }\n\n const { ticketSlug } = parsedArgs;\n\n try {\n const result = await convexClient.mutation(\n \"mcp_tickets:closeMcpTicket\",\n {\n ...buildAuthArgs(config),\n projectSlug: ticketTool.projectSlug,\n ticketSlug,\n }\n ) as CloseTicketResult;\n\n return {\n content: [\n {\n type: \"text\",\n text: `✅ Ticket [${result.slug}] closed in project \"${ticketTool.projectSlug}\".\n\nClosed at: ${new Date(result.closedAt).toISOString()}\n\n_Reminder: Ensure all embedded \\`[RUN_PROMPT ...]\\` directives were executed before closing._`,\n },\n ],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] tickets_close error:`, error);\n return {\n content: [\n {\n type: \"text\",\n text: `Error closing ticket: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n } else if (ticketTool.type === 'search') {\n // Ticket 155: Handle tickets:search\n const parsedArgs = parseSearchArgs(request.params.arguments);\n\n if (!parsedArgs) {\n return {\n content: [\n {\n type: \"text\",\n text: `Error: Search query must be at least 3 characters`,\n },\n ],\n isError: true,\n };\n }\n\n const { query } = parsedArgs;\n\n try {\n const result = await convexClient.query(\n \"mcp_tickets:searchMcpTickets\",\n {\n ...buildAuthArgs(config),\n projectSlug: ticketTool.projectSlug,\n query,\n }\n ) as SearchTicketItem[];\n\n if (result.length === 0) {\n return {\n content: [\n {\n type: \"text\",\n text: `No tickets found matching \"${query}\" in project \"${ticketTool.projectSlug}\".`,\n },\n ],\n };\n }\n\n // Format as list with status badges\n const formattedList = result\n .map((t) => {\n const statusBadge = t.status === 'open' ? '🟢' : t.status === 'working' ? '🟡' : t.status === 'closed' ? '⚫' : '⚪';\n const num = t.ticketNumber ? `#${t.ticketNumber}` : '';\n return `${statusBadge} ${num} ${t.slug}\\n ${t.matchSnippet}`;\n })\n .join('\\n\\n');\n\n return {\n content: [\n {\n type: \"text\",\n text: `Found ${result.length} ticket(s) matching \"${query}\":\\n\\n${formattedList}`,\n },\n ],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] tickets_search error:`, error);\n return {\n content: [\n {\n type: \"text\",\n text: `Error searching tickets: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n } else if (ticketTool.type === 'get') {\n // Ticket 155: Handle tickets:get (read-only inspection)\n const parsedArgs = parseGetArgs(request.params.arguments);\n\n if (!parsedArgs) {\n return {\n content: [\n {\n type: \"text\",\n text: `Error: Missing ticketSlug parameter. Provide a ticket number (e.g., \"102\") or full slug.`,\n },\n ],\n isError: true,\n };\n }\n\n const { ticketSlug } = parsedArgs;\n\n try {\n // Uses existing getMcpTicket query - read-only inspection\n const result = await convexClient.query(\n \"mcp_tickets:getMcpTicket\",\n {\n ...buildAuthArgs(config),\n projectSlug: ticketTool.projectSlug,\n ticketSlug,\n }\n ) as GetTicketResult | AccessDeniedResult | null;\n\n if (!result) {\n return {\n content: [\n {\n type: \"text\",\n text: `Ticket \"${ticketSlug}\" not found in project \"${ticketTool.projectSlug}\".`,\n },\n ],\n };\n }\n\n // Handle access denied response\n if (isAccessDenied(result)) {\n return {\n content: [\n {\n type: \"text\",\n text: `Access denied: ${result.error}`,\n },\n ],\n isError: true,\n };\n }\n\n // Now TypeScript knows result is GetTicketResult\n const getResult = result as GetTicketResult;\n\n // Build status line\n const statusBadge = getResult.status.toUpperCase();\n const startedInfo = getResult.startedAt ? `\\nStarted: ${new Date(getResult.startedAt).toISOString()}` : '';\n const closedInfo = getResult.closedAt ? `\\nClosed: ${new Date(getResult.closedAt).toISOString()}` : '';\n\n return {\n content: [\n {\n type: \"text\",\n text: `# Ticket: ${getResult.slug} [${statusBadge}]${startedInfo}${closedInfo}\n\n${getResult.content}\n\n---\n_Read-only inspection. Use tickets_work to start working on this ticket._`,\n },\n ],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] tickets_get error:`, error);\n return {\n content: [\n {\n type: \"text\",\n text: `Error getting ticket: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n } else if (ticketTool.type === 'list') {\n // Ticket 155: Handle tickets:list (active queue)\n try {\n const result = await convexClient.query(\n \"mcp_tickets:listMcpTickets\",\n {\n ...buildAuthArgs(config),\n projectSlug: ticketTool.projectSlug,\n }\n ) as ListTicketItem[];\n\n if (result.length === 0) {\n return {\n content: [\n {\n type: \"text\",\n text: `No active tickets in project \"${ticketTool.projectSlug}\". All tickets are closed or archived.`,\n },\n ],\n };\n }\n\n // Group by status for clear display\n const openTickets = result.filter(t => t.status === 'open');\n const workingTickets = result.filter(t => t.status === 'working');\n const backlogTickets = result.filter(t => t.status === 'backlog');\n\n const formatTicketLine = (t: typeof result[0]) => {\n const num = t.ticketNumber ? `#${t.ticketNumber}` : '';\n return ` ${t.position}. ${num} ${t.slug}\\n ${t.preview}`;\n };\n\n const sections: string[] = [];\n\n if (openTickets.length > 0) {\n sections.push(`**🟢 Open (${openTickets.length})**\\n${openTickets.map(formatTicketLine).join('\\n')}`);\n }\n if (workingTickets.length > 0) {\n sections.push(`**🟡 Working (${workingTickets.length})**\\n${workingTickets.map(formatTicketLine).join('\\n')}`);\n }\n if (backlogTickets.length > 0) {\n sections.push(`**⚪ Backlog (${backlogTickets.length})**\\n${backlogTickets.map(formatTicketLine).join('\\n')}`);\n }\n\n return {\n content: [\n {\n type: \"text\",\n text: `# Active Queue: ${ticketTool.projectSlug}\\n\\n${result.length} ticket(s) in queue\\n\\n${sections.join('\\n\\n')}`,\n },\n ],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] tickets_list error:`, error);\n return {\n content: [\n {\n type: \"text\",\n text: `Error listing tickets: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n } else if (ticketTool.type === 'update') {\n // Ticket 293: Handle tickets:update (append content to ticket)\n const parsedArgs = parseUpdateArgs(request.params.arguments);\n\n if (!parsedArgs) {\n // Determine which argument is missing for a more specific error message\n const args = request.params.arguments as Record<string, unknown> | undefined;\n const hasTicketSlug = typeof args?.ticketSlug === 'string' && args.ticketSlug;\n const hasContent = typeof args?.content === 'string' && args.content;\n\n if (!hasTicketSlug) {\n return {\n content: [\n {\n type: \"text\",\n text: `Error: Missing ticketSlug parameter. Provide a ticket number (e.g., \"102\") or full slug.`,\n },\n ],\n isError: true,\n };\n }\n if (!hasContent) {\n return {\n content: [\n {\n type: \"text\",\n text: `Error: Missing content parameter. Provide the update content to append to the ticket.`,\n },\n ],\n isError: true,\n };\n }\n // Should not reach here, but just in case\n return {\n content: [\n {\n type: \"text\",\n text: `Error: Missing required parameters.`,\n },\n ],\n isError: true,\n };\n }\n\n const { ticketSlug, content } = parsedArgs;\n\n try {\n const result = await convexClient.mutation(\n \"mcp_tickets:updateMcpTicket\",\n {\n ...buildAuthArgs(config),\n projectSlug: ticketTool.projectSlug,\n ticketSlug,\n content,\n }\n ) as UpdateTicketResult;\n\n return {\n content: [\n {\n type: \"text\",\n text: `✅ Ticket [${result.slug}] updated in project \"${ticketTool.projectSlug}\".\nUpdated at: ${new Date(result.updatedAt).toISOString()}\n_Ticket content has been appended with your update._`,\n },\n ],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] tickets_update error:`, error);\n return {\n content: [\n {\n type: \"text\",\n text: `Error updating ticket: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n }\n }\n\n // Handle per-prompt tool invocations (e.g., \"code-review\" - now global)\n const promptTool = dynamicPromptTools.find((pt) => pt.name === toolName);\n if (promptTool) {\n try {\n const result = await fetchAndExecuteAccountScopedPrompt(\n promptTool.promptSlug,\n config,\n convexClient\n );\n\n const promptText = result.messages\n .map((msg) => msg.content.text)\n .join('\\n\\n');\n\n return {\n content: [{ type: \"text\", text: promptText }],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] ${toolName} error:`, error);\n return {\n content: [{ type: \"text\", text: `Error executing prompt: ${errorMessage}` }],\n isError: true,\n };\n }\n }\n\n // Unknown tool\n throw new Error(`Unknown tool: ${toolName}. Use prompts_run to execute prompts by name, or check available tools.`);\n });\n\n // Start server with stdio transport\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n console.error(\"[MCP] Server started successfully\");\n console.error(`[MCP] Deployment: ${config.isDev ? \"DEVELOPMENT\" : \"PRODUCTION\"}`);\n console.error(`[MCP] Convex URL: ${config.convexUrl}`);\n console.error(`[MCP] Data mode: REAL-TIME (fetches fresh data on each invocation)`);\n\n // List all prompts\n const allPromptNames = [...Array.from(promptNames)].sort();\n console.error(`[MCP] Prompts available: ${allPromptNames.join(\", \")}`);\n console.error(`[MCP] - Total prompts: ${promptNames.size}`);\n\n // Keep the event loop alive with a heartbeat\n // This prevents Node from exiting when there are no active handles\n setInterval(() => {\n // Heartbeat every 60 seconds to keep process alive\n }, 60000);\n\n // Return a promise that never resolves to keep the server running\n return new Promise<void>(() => {\n // The transport handles stdin/stdout communication\n // The interval above keeps the event loop active\n });\n}\n\n/**\n * Validate project token with Convex\n *\n * Project tokens are now stored directly on the projects table.\n * Returns project details if valid, null if invalid.\n */\n/**\n * Response type from validateProjectToken query\n */\ninterface ValidateProjectTokenResponse {\n valid: boolean;\n projectId?: string;\n projectSlug?: string;\n ownerId?: string;\n}\n\nasync function validateProjectToken(\n client: ConvexHttpClient,\n token: string\n): Promise<ProjectTokenValidation> {\n try {\n // Cast to our typed client for string-based function references\n const typedClient = client as ConvexClientWithStringRefs;\n const result = await typedClient.query<ValidateProjectTokenResponse | null>(\n \"projects:validateProjectToken\",\n { token }\n );\n\n if (result && result.valid) {\n return {\n valid: true,\n projectId: result.projectId,\n projectSlug: result.projectSlug,\n ownerId: result.ownerId,\n };\n }\n return { valid: false, error: \"Invalid project token\" };\n } catch (error) {\n return {\n valid: false,\n error: error instanceof Error ? error.message : \"Unknown error\",\n };\n }\n}\n\n// Note: fetchMcpPromptMetadata removed - prompts now account-scoped\n// Note: fetchMcpCommands and buildCommandName removed - commands resource eliminated\n// Note: API key-based functions removed - all auth now uses project tokens\n","// ============================================================================\n// MCP Tool Argument Types\n// ============================================================================\n\n/**\n * Arguments for the tickets_work tool\n * Optional ticketSlug: if provided, work on specific ticket; otherwise get next from queue\n */\nexport interface WorkToolArgs {\n ticketSlug?: string;\n}\n\n/**\n * Arguments for the tickets_close tool\n * Required ticketSlug to identify which ticket to close\n */\nexport interface CloseToolArgs {\n ticketSlug: string;\n}\n\n/**\n * Arguments for the tickets_create tool\n * Required content where first line becomes the slug\n * Optional tags array for assigning existing tags at creation\n */\nexport interface CreateToolArgs {\n content: string;\n tags?: string[];\n}\n\n/**\n * Arguments for the tickets_search tool\n * Required query string (min 3 characters)\n */\nexport interface SearchToolArgs {\n query: string;\n}\n\n/**\n * Arguments for the tickets_get tool\n * Required ticketSlug to identify which ticket to retrieve\n */\nexport interface GetToolArgs {\n ticketSlug: string;\n}\n\n/**\n * Arguments for the tickets_list tool\n * No arguments required - returns all active tickets\n */\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface ListToolArgs {}\n\n/**\n * Arguments for the tickets_update tool\n * Required ticketSlug and content to append\n */\nexport interface UpdateToolArgs {\n ticketSlug: string;\n content: string;\n}\n\n/**\n * Arguments for the prompts_run tool\n * Required slug to identify which prompt to execute\n */\nexport interface PromptsRunArgs {\n slug: string;\n}\n\n/**\n * Arguments for the prompts_list tool\n * Optional search term to filter prompts\n */\nexport interface PromptsListArgs {\n search?: string;\n}\n\n/**\n * Arguments for the prompts_update tool\n * Required slug, optional content for write mode\n */\nexport interface PromptsUpdateArgs {\n slug: string;\n content?: string;\n changeLogMessage?: string;\n}\n\n// ============================================================================\n// Convex Response Types (for MCP mutations/queries)\n// ============================================================================\n\n/**\n * Response from workMcpTicket mutation\n */\nexport interface WorkTicketResult {\n slug: string;\n ticketNumber?: number;\n status: string;\n content: string;\n startedAt?: number;\n remainingTickets: number;\n wasOpened: boolean;\n}\n\n/**\n * Access denied response from ticket operations\n */\nexport interface AccessDeniedResult {\n error: string;\n accessDenied: true;\n}\n\n/**\n * Type guard to check if a result is an access denied response\n */\nexport function isAccessDenied(result: unknown): result is AccessDeniedResult {\n return (\n typeof result === 'object' &&\n result !== null &&\n 'accessDenied' in result &&\n (result as AccessDeniedResult).accessDenied === true\n );\n}\n\n/**\n * Response from createMcpTicket mutation\n */\nexport interface CreateTicketResult {\n slug: string;\n ticketNumber?: number;\n preview: string;\n position: number;\n totalBacklog: number;\n}\n\n/**\n * Response from closeMcpTicket mutation\n */\nexport interface CloseTicketResult {\n slug: string;\n status: string;\n closedAt: number;\n}\n\n/**\n * Response from updateMcpTicket mutation\n */\nexport interface UpdateTicketResult {\n slug: string;\n ticketNumber?: number;\n status: string;\n updatedAt: number;\n}\n\n/**\n * Response from getMcpTicket query\n */\nexport interface GetTicketResult {\n slug: string;\n ticketNumber?: number;\n content: string;\n status: string;\n createdAt: number;\n startedAt?: number;\n closedAt?: number;\n}\n\n/**\n * Response from listMcpTickets query\n */\nexport interface ListTicketItem {\n position: number;\n slug: string;\n ticketNumber?: number;\n status: string;\n preview: string;\n createdAt: number;\n startedAt?: number;\n}\n\n/**\n * Response from searchMcpTickets query\n */\nexport interface SearchTicketItem {\n slug: string;\n ticketNumber?: number;\n status: string;\n matchSnippet: string;\n createdAt: number;\n}\n\n/**\n * Response from prompt metadata query\n */\nexport interface PromptMetadata {\n slug: string;\n description?: string;\n folderSlug?: string;\n}\n\n/**\n * Response from updateMcpPrompt mutation (read mode)\n */\nexport interface UpdatePromptReadResult {\n success: true;\n mode: \"read\";\n matchType: \"exact\" | \"prefix\" | \"contains\";\n slug: string;\n description: string;\n content: string;\n version: number;\n context: {\n variables: Array<{ slug: string; reference: string; type: string; description: string }>;\n snippets: Array<{ slug: string; reference: string; description: string }>;\n prompts: Array<{ slug: string; reference: string; description: string }>;\n };\n editingGuidance: string;\n}\n\n/**\n * Response from updateMcpPrompt mutation (write mode)\n */\nexport interface UpdatePromptWriteResult {\n success: true;\n mode: \"write\";\n slug: string;\n version: number;\n updatedAt: string;\n message: string;\n}\n\n/**\n * Response from updateMcpPrompt mutation (error)\n */\nexport interface UpdatePromptErrorResult {\n success: false;\n error: string;\n suggestions?: string[];\n}\n\n/**\n * Union type for all updateMcpPrompt responses\n */\nexport type UpdatePromptResult =\n | UpdatePromptReadResult\n | UpdatePromptWriteResult\n | UpdatePromptErrorResult;\n\n/**\n * Account-scoped prompt match result types\n */\nexport type AccountScopedPromptResult =\n | { matchType: \"exact\" | \"prefix\" | \"contains\"; slug: string; description?: string; flattenedPrompt?: string }\n | { matchType: \"ambiguous\"; suggestions: string[] }\n | null;\n\n// ============================================================================\n// Argument Parsing Functions\n// ============================================================================\n\n/**\n * Parse and validate arguments for tickets_work tool\n */\nexport function parseWorkArgs(args: unknown): WorkToolArgs {\n const parsed = args as Record<string, unknown> | undefined;\n return {\n ticketSlug: typeof parsed?.ticketSlug === 'string' ? parsed.ticketSlug : undefined,\n };\n}\n\n/**\n * Parse and validate arguments for tickets_close tool\n * Returns null if required argument is missing\n */\nexport function parseCloseArgs(args: unknown): CloseToolArgs | null {\n const parsed = args as Record<string, unknown> | undefined;\n const ticketSlug = typeof parsed?.ticketSlug === 'string' ? parsed.ticketSlug : undefined;\n if (!ticketSlug) return null;\n return { ticketSlug };\n}\n\n/**\n * Parse and validate arguments for tickets_create tool\n * Returns null if required argument is missing\n */\nexport function parseCreateArgs(args: unknown): CreateToolArgs | null {\n const parsed = args as Record<string, unknown> | undefined;\n const content = typeof parsed?.content === 'string' ? parsed.content : undefined;\n if (!content) return null;\n\n // Parse optional tags array - filter to valid strings only\n let tags: string[] | undefined;\n if (Array.isArray(parsed?.tags)) {\n tags = parsed.tags.filter((t): t is string => typeof t === 'string');\n if (tags.length === 0) tags = undefined;\n }\n\n return { content, tags };\n}\n\n/**\n * Parse and validate arguments for tickets_search tool\n * Returns null if required argument is missing or too short\n */\nexport function parseSearchArgs(args: unknown): SearchToolArgs | null {\n const parsed = args as Record<string, unknown> | undefined;\n const query = typeof parsed?.query === 'string' ? parsed.query : undefined;\n if (!query || query.length < 3) return null;\n return { query };\n}\n\n/**\n * Parse and validate arguments for tickets_get tool\n * Returns null if required argument is missing\n */\nexport function parseGetArgs(args: unknown): GetToolArgs | null {\n const parsed = args as Record<string, unknown> | undefined;\n const ticketSlug = typeof parsed?.ticketSlug === 'string' ? parsed.ticketSlug : undefined;\n if (!ticketSlug) return null;\n return { ticketSlug };\n}\n\n/**\n * Parse arguments for tickets_list tool (no args required)\n */\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nexport function parseListArgs(args: unknown): ListToolArgs {\n return {};\n}\n\n/**\n * Parse and validate arguments for tickets_update tool\n * Returns null if any required argument is missing\n */\nexport function parseUpdateArgs(args: unknown): UpdateToolArgs | null {\n const parsed = args as Record<string, unknown> | undefined;\n const ticketSlug = typeof parsed?.ticketSlug === 'string' ? parsed.ticketSlug : undefined;\n const content = typeof parsed?.content === 'string' ? parsed.content : undefined;\n if (!ticketSlug || !content) return null;\n return { ticketSlug, content };\n}\n\n/**\n * Parse and validate arguments for prompts_run tool\n * Returns null if required argument is missing\n */\nexport function parsePromptsRunArgs(args: unknown): PromptsRunArgs | null {\n const parsed = args as Record<string, unknown> | undefined;\n const slug = typeof parsed?.slug === 'string' ? parsed.slug : undefined;\n if (!slug) return null;\n return { slug };\n}\n\n/**\n * Parse arguments for prompts_list tool\n */\nexport function parsePromptsListArgs(args: unknown): PromptsListArgs {\n const parsed = args as Record<string, unknown> | undefined;\n return {\n search: typeof parsed?.search === 'string' ? parsed.search : undefined,\n };\n}\n\n/**\n * Parse and validate arguments for prompts_update tool\n * Returns null if required slug is missing\n */\nexport function parsePromptsUpdateArgs(args: unknown): PromptsUpdateArgs | null {\n const parsed = args as Record<string, unknown> | undefined;\n const slug = typeof parsed?.slug === 'string' ? parsed.slug : undefined;\n if (!slug) return null;\n return {\n slug,\n content: typeof parsed?.content === 'string' ? parsed.content : undefined,\n changeLogMessage: typeof parsed?.changeLogMessage === 'string' ? parsed.changeLogMessage : undefined,\n };\n}\n\n// ============================================================================\n// Legacy Types (kept for backwards compatibility)\n// ============================================================================\n\n/**\n * @deprecated Legacy type - prompts are now account-scoped (no projectSlug)\n * Kept for backwards compatibility during transition period.\n */\nexport interface McpPrompt {\n slug: string;\n name: string;\n description?: string;\n flattenedPrompt?: string;\n folderPath?: string; // Folder organization (used for ZIP downloads, not slash commands)\n projectSlug?: string; // @deprecated - prompts are now account-scoped\n projectName?: string; // @deprecated - prompts are now account-scoped\n}\n\n/**\n * @deprecated Legacy type - prompts are now account-scoped (no projectSlug)\n * Kept for backwards compatibility during transition period.\n */\nexport interface McpPromptMetadata {\n slug: string;\n description?: string;\n projectSlug?: string; // @deprecated - prompts are now account-scoped\n}\n\n// Note: McpCommandMetadata and McpCommandFull removed - commands resource eliminated\n\n/**\n * @deprecated Legacy type - prompts are now account-scoped (no projectSlug)\n * Kept for backwards compatibility during transition period.\n */\nexport interface McpPromptFull {\n slug: string;\n description?: string;\n flattenedPrompt?: string;\n projectSlug?: string; // @deprecated - prompts are now account-scoped\n}\n\n/**\n * @deprecated Legacy type - use account-scoped prompt queries instead\n * Kept for backwards compatibility during transition period.\n */\nexport type PromptMatchResult =\n | { matchType: \"exact\" | \"prefix\" | \"contains\"; slug: string; description?: string; flattenedPrompt?: string; projectSlug: string }\n | { matchType: \"ambiguous\"; suggestions: string[] }\n | null;\n\n/**\n * Configuration for the MCP server\n *\n * Authentication: Project Token (PPM_PROJECT_TOKEN)\n * Provides scoped access to a single project for both prompts and tickets.\n * The token's ownerId determines which prompts are accessible (account-scoped).\n *\n * Note: API Key auth (PPM_API_KEY) has been deprecated.\n * All authentication now uses project tokens.\n */\nexport interface ServerConfig {\n projectToken: string; // Required - scoped access to a single project\n isDev: boolean;\n convexUrl: string;\n}\n\n/**\n * Result of project token validation\n * Token is now stored directly on the projects table (single token per project)\n */\nexport interface ProjectTokenValidation {\n valid: boolean;\n projectId?: string;\n projectSlug?: string;\n ownerId?: string;\n error?: string;\n}\n","import type { ConvexHttpClient } from \"convex/browser\";\nimport type { ServerConfig, PromptMetadata, AccountScopedPromptResult } from \"./types.js\";\n\n/**\n * Helper type for Convex HTTP client with string-based function references.\n * This allows us to use string function names while maintaining type safety\n * for the response types through explicit casting.\n */\ntype ConvexClientWithStringRefs = ConvexHttpClient & {\n query: <T>(name: string, args: Record<string, unknown>) => Promise<T>;\n};\n\n/**\n * Sanitizes a string for use in MCP tool names.\n * Ensures output matches MCP protocol pattern: ^[a-zA-Z0-9_-]{1,64}$\n *\n * @param str - Raw string to sanitize (project slug, folder path, or prompt slug)\n * @returns Sanitized string safe for MCP tool names\n */\nexport function sanitizeForMcp(str: string): string {\n return str\n .trim() // Remove leading/trailing whitespace first\n .toLowerCase() // Convert to lowercase for consistency\n .replace(/[^a-z0-9_-]/g, '-') // Replace ALL invalid chars (including spaces!) with hyphens\n .replace(/-+/g, '-') // Collapse multiple consecutive hyphens into one\n .replace(/^-+|-+$/g, '') // Remove leading and trailing hyphens\n .trim(); // Final trim to ensure no whitespace\n}\n\n// Note: The following legacy functions have been removed as prompts are now account-scoped:\n// - buildPromptName (was project:prompt format, now prompts are global)\n// - buildPromptNameFromMetadata (was project:prompt format)\n// - buildPromptSchema (used buildPromptName)\n// - buildPromptHandler (used project-scoped getMcpPrompts query)\n// - fetchAndExecutePrompt (used project-scoped getMcpPromptBySlug query)\n//\n// Use fetchAndExecuteAccountScopedPrompt instead for all prompt execution.\n\n/**\n * Custom error class for ambiguous prompt matches\n * Contains suggestions for the user to choose from\n */\nexport class AmbiguousPromptError extends Error {\n suggestions: string[];\n\n constructor(promptSlug: string, suggestions: string[]) {\n super(`Multiple prompts match \"${promptSlug}\". Please be more specific.`);\n this.name = \"AmbiguousPromptError\";\n this.suggestions = suggestions;\n }\n}\n\n/**\n * Fetch account-scoped prompt metadata using project token auth.\n *\n * @param client - Convex HTTP client\n * @param projectToken - Project token for authentication\n * @returns Account-scoped prompts for this project\n */\nexport async function fetchAccountScopedPromptMetadataByToken(\n client: ConvexHttpClient,\n projectToken: string\n): Promise<PromptMetadata[]> {\n try {\n // Cast to our typed client for string-based function references\n const typedClient = client as ConvexClientWithStringRefs;\n const metadata = await typedClient.query<PromptMetadata[]>(\n \"mcp_prompts:getAccountScopedPromptMetadataByToken\",\n { projectToken }\n );\n return metadata;\n } catch (error) {\n throw new Error(\n `Failed to fetch prompts via project token: ${error instanceof Error ? error.message : \"Unknown error\"}`\n );\n }\n}\n\n// Note: AccountScopedPromptResult type is imported from ./types.js\n\n/**\n * Fetch a single account-scoped prompt and execute it\n *\n * Account-scoped prompts are global (no project prefix required).\n * This is for prompts that don't have a projectId.\n *\n * Uses project token authentication: access via token's ownerId.\n *\n * Matching algorithm supports exact, prefix, and contains matching.\n */\nexport async function fetchAndExecuteAccountScopedPrompt(\n promptSlug: string,\n config: ServerConfig,\n convexClient: ConvexHttpClient\n): Promise<{ messages: Array<{ role: string; content: { type: \"text\"; text: string } }> }> {\n // Cast to our typed client for string-based function references\n const typedClient = convexClient as ConvexClientWithStringRefs;\n\n // Project token auth - access via token's owner\n const result = await typedClient.query<AccountScopedPromptResult>(\n \"mcp_prompts:getAccountScopedPromptBySlugByToken\",\n {\n projectToken: config.projectToken,\n promptSlug,\n }\n );\n\n // Handle null - no matches found\n if (!result) {\n throw new Error(\n `Prompt \"${promptSlug}\" not found. Use system:prompts to list available prompts.`\n );\n }\n\n // Handle ambiguous matches - throw custom error with suggestions\n if (result.matchType === \"ambiguous\") {\n throw new AmbiguousPromptError(promptSlug, result.suggestions);\n }\n\n // Handle successful match (exact, prefix, or contains)\n if (!result.flattenedPrompt) {\n throw new Error(\n `Prompt \"${result.slug}\" has no flattened content. Please re-save the prompt to regenerate it.`\n );\n }\n\n // Log match type for debugging\n const matchNote = result.matchType === \"exact\"\n ? \"exact match\"\n : result.matchType === \"prefix\"\n ? `prefix match → ${result.slug}`\n : `contains match → ${result.slug}`;\n console.error(`[MCP] Fetched account-scoped prompt: ${promptSlug} (${matchNote})`);\n\n // Return raw flattened prompt (client will parse YAML front matter)\n return {\n messages: [\n {\n role: \"user\",\n content: {\n type: \"text\" as const,\n text: result.flattenedPrompt,\n },\n },\n ],\n };\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@promptprojectmanager/mcp-server",
3
- "version": "4.4.3",
3
+ "version": "4.5.0",
4
4
  "description": "MCP server that exposes Prompt Project Manager project prompts as slash commands",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",