@promptprojectmanager/mcp-server 2.8.5 → 2.8.7

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
@@ -21,8 +21,6 @@ import {
21
21
  ListPromptsRequestSchema,
22
22
  ListToolsRequestSchema
23
23
  } from "@modelcontextprotocol/sdk/types.js";
24
- import * as fs from "fs";
25
- import * as path from "path";
26
24
 
27
25
  // src/prompt-builder.ts
28
26
  function sanitizeForMcp(str) {
@@ -348,23 +346,17 @@ async function startServer(config, convexClient) {
348
346
  const dynamicTicketTools = [];
349
347
  for (const workspaceSlug of workspaceSlugs) {
350
348
  dynamicTicketTools.push({
351
- name: `${workspaceSlug}:tickets:list`,
352
- description: `List pending tickets in the "${workspaceSlug}" workspace queue`,
349
+ name: `${workspaceSlug}:tickets:open`,
350
+ description: `Open the next ticket from the "${workspaceSlug}" workspace queue and move it to working status. Optionally specify a ticket number or slug.`,
353
351
  workspaceSlug,
354
- type: "list"
352
+ type: "open"
355
353
  });
356
354
  dynamicTicketTools.push({
357
355
  name: `${workspaceSlug}:tickets:work`,
358
- description: `Start work on a ticket with local session files in the "${workspaceSlug}" workspace. Optionally specify a ticket number or slug.`,
356
+ description: `Resume work on a ticket in the "${workspaceSlug}" workspace. Returns ticket content and all progress messages for context recovery.`,
359
357
  workspaceSlug,
360
358
  type: "work"
361
359
  });
362
- dynamicTicketTools.push({
363
- name: `${workspaceSlug}:tickets:working`,
364
- description: `List all tickets currently being worked on in the "${workspaceSlug}" workspace`,
365
- workspaceSlug,
366
- type: "working"
367
- });
368
360
  dynamicTicketTools.push({
369
361
  name: `${workspaceSlug}:tickets:close`,
370
362
  description: `Mark a working ticket as completed in the "${workspaceSlug}" workspace`,
@@ -377,12 +369,6 @@ async function startServer(config, convexClient) {
377
369
  workspaceSlug,
378
370
  type: "message"
379
371
  });
380
- dynamicTicketTools.push({
381
- name: `${workspaceSlug}:tickets:get`,
382
- description: `Get a specific ticket from the "${workspaceSlug}" workspace by number or slug`,
383
- workspaceSlug,
384
- type: "get"
385
- });
386
372
  dynamicTicketTools.push({
387
373
  name: `${workspaceSlug}:tickets:create`,
388
374
  description: `Create a new ticket in the "${workspaceSlug}" workspace queue`,
@@ -425,7 +411,7 @@ async function startServer(config, convexClient) {
425
411
  name: st.name,
426
412
  description: st.description
427
413
  }));
428
- const ticketPromptSchemas = dynamicTicketTools.filter((tt) => tt.type !== "get").map((tt) => ({
414
+ const ticketPromptSchemas = dynamicTicketTools.map((tt) => ({
429
415
  name: tt.name,
430
416
  description: tt.description
431
417
  }));
@@ -455,27 +441,31 @@ async function startServer(config, convexClient) {
455
441
  };
456
442
  }
457
443
  const ticketTool = dynamicTicketTools.find((tt) => tt.name === promptName);
458
- const ticketWorkMatch = promptName.match(/^(.+):tickets:work\s+(.+)$/);
459
- if (ticketTool || ticketWorkMatch) {
444
+ const ticketOpenMatch = promptName.match(/^(.+):tickets:open\s+(.+)$/);
445
+ if (ticketTool || ticketOpenMatch) {
460
446
  let promptContent;
461
447
  let description;
462
- if (ticketWorkMatch) {
463
- const workspaceSlug = ticketWorkMatch[1];
464
- const ticketArg = ticketWorkMatch[2].trim();
465
- description = `Start work on ticket "${ticketArg}" from "${workspaceSlug}"`;
466
- promptContent = `Start work on ticket "${ticketArg}" from the "${workspaceSlug}" workspace queue.
467
-
468
- Call the \`${workspaceSlug}:tickets:work\` tool with ticketSlug: "${ticketArg}" to start working on the specified ticket.`;
469
- } else if (ticketTool.type === "list") {
448
+ if (ticketOpenMatch) {
449
+ const workspaceSlug = ticketOpenMatch[1];
450
+ const ticketArg = ticketOpenMatch[2].trim();
451
+ description = `Open ticket "${ticketArg}" from "${workspaceSlug}"`;
452
+ promptContent = `Open ticket "${ticketArg}" from the "${workspaceSlug}" workspace queue and move it to working status.
453
+
454
+ Call the \`${workspaceSlug}:tickets:open\` tool with ticketSlug: "${ticketArg}".`;
455
+ } else if (ticketTool.type === "open") {
470
456
  description = ticketTool.description;
471
- promptContent = `List all pending tickets in the "${ticketTool.workspaceSlug}" workspace queue.
457
+ promptContent = `Open the next ticket from the "${ticketTool.workspaceSlug}" workspace queue and move it to working status.
458
+
459
+ Call the \`${ticketTool.name}\` tool to get the next ticket.
472
460
 
473
- Call the \`${ticketTool.name}\` tool to get the list.`;
461
+ You can also specify a ticket: /ppm:${ticketTool.workspaceSlug}:tickets:open <number-or-slug>`;
474
462
  } else if (ticketTool.type === "work") {
475
463
  description = ticketTool.description;
476
- promptContent = `Start work on the next ticket from the "${ticketTool.workspaceSlug}" workspace queue.
464
+ promptContent = `Resume work on a ticket in the "${ticketTool.workspaceSlug}" workspace.
465
+
466
+ Call the \`${ticketTool.name}\` tool to get ticket content and all progress messages for context recovery.
477
467
 
478
- Call the \`${ticketTool.name}\` tool to get the next ticket and move it to working status. Local session files will be created if PPM directory is configured.
468
+ If no ticket slug is provided, returns the first working ticket (by startedAt).
479
469
 
480
470
  You can also specify a ticket: /ppm:${ticketTool.workspaceSlug}:tickets:work <number-or-slug>`;
481
471
  } else if (ticketTool.type === "create") {
@@ -505,9 +495,19 @@ The user provides **instructions** (not raw content). You interpret those instru
505
495
  - "just the tasks from this file" \u2192 Extract only task items
506
496
 
507
497
  Call the \`${ticketTool.name}\` tool with the final generated content.`;
498
+ } else if (ticketTool.type === "close") {
499
+ description = ticketTool.description;
500
+ promptContent = `Close a working ticket in the "${ticketTool.workspaceSlug}" workspace.
501
+
502
+ Call the \`${ticketTool.name}\` tool with the ticket number or slug.`;
503
+ } else if (ticketTool.type === "message") {
504
+ description = ticketTool.description;
505
+ promptContent = `Add a progress message to a working or closed ticket in the "${ticketTool.workspaceSlug}" workspace.
506
+
507
+ Call the \`${ticketTool.name}\` tool with the ticket number/slug and your message.`;
508
508
  } else {
509
509
  description = ticketTool.description;
510
- promptContent = `This command is not available as a slash command. Use the \`${ticketTool.name}\` tool directly to inspect a ticket.`;
510
+ promptContent = `Use the \`${ticketTool.name}\` tool.`;
511
511
  }
512
512
  return {
513
513
  description,
@@ -543,7 +543,7 @@ Call the \`${ticketTool.name}\` tool with the final generated content.`;
543
543
  // Dynamic ticket tools per workspace (Ticket 135, 149, 151, 153)
544
544
  ...dynamicTicketTools.map((tt) => {
545
545
  let inputSchema;
546
- if (tt.type === "get" || tt.type === "close") {
546
+ if (tt.type === "close") {
547
547
  inputSchema = {
548
548
  type: "object",
549
549
  properties: {
@@ -554,13 +554,24 @@ Call the \`${ticketTool.name}\` tool with the final generated content.`;
554
554
  },
555
555
  required: ["ticketSlug"]
556
556
  };
557
+ } else if (tt.type === "open") {
558
+ inputSchema = {
559
+ type: "object",
560
+ properties: {
561
+ ticketSlug: {
562
+ type: "string",
563
+ description: "Optional: Ticket number (e.g., '102') or full slug (e.g., '102-fix-auth'). If not provided, opens the next ticket in queue."
564
+ }
565
+ },
566
+ required: []
567
+ };
557
568
  } else if (tt.type === "work") {
558
569
  inputSchema = {
559
570
  type: "object",
560
571
  properties: {
561
572
  ticketSlug: {
562
573
  type: "string",
563
- description: "Optional: Ticket number (e.g., '102') or full slug (e.g., '102-fix-auth'). If not provided, starts work on next ticket in queue."
574
+ description: "Optional: Ticket number (e.g., '102') or full slug. If not provided, returns the first working ticket by startedAt."
564
575
  }
565
576
  },
566
577
  required: []
@@ -736,44 +747,54 @@ ${sections.join("\n\n") || "No prompts found."}`
736
747
  }
737
748
  const ticketTool = dynamicTicketTools.find((tt) => tt.name === toolName);
738
749
  if (ticketTool) {
739
- if (ticketTool.type === "list") {
750
+ if (ticketTool.type === "open") {
751
+ const ticketSlug = request.params.arguments?.ticketSlug;
740
752
  try {
741
- const tickets = await convexClient.query(
742
- "mcp_tickets:getMcpTickets",
753
+ const result2 = await convexClient.mutation(
754
+ "mcp_tickets:executeMcpNextTicket",
743
755
  {
744
756
  apiKey: config.apiKey,
745
- workspaceSlug: ticketTool.workspaceSlug
757
+ workspaceSlug: ticketTool.workspaceSlug,
758
+ ticketSlug
759
+ // Optional: specific ticket to open
746
760
  }
747
761
  );
748
- if (tickets.length === 0) {
762
+ if (!result2) {
763
+ const message = ticketSlug ? `Ticket "${ticketSlug}" not found or not open in workspace "${ticketTool.workspaceSlug}".` : `No pending tickets in workspace "${ticketTool.workspaceSlug}".`;
749
764
  return {
750
765
  content: [
751
766
  {
752
767
  type: "text",
753
- text: `No pending tickets in workspace "${ticketTool.workspaceSlug}".`
768
+ text: message
754
769
  }
755
770
  ]
756
771
  };
757
772
  }
758
- const ticketList = tickets.map((t) => `${t.position}. [${t.slug}] ${t.preview}`).join("\n");
759
773
  return {
760
774
  content: [
761
775
  {
762
776
  type: "text",
763
- text: `Pending tickets in "${ticketTool.workspaceSlug}" (${tickets.length} total):
777
+ text: `# Ticket: ${result2.slug} [WORKING]
778
+
779
+ ${result2.content}
764
780
 
765
- ${ticketList}`
781
+ ---
782
+ _Ticket moved to working status. ${result2.remainingTickets} ticket(s) remaining in queue._
783
+
784
+ **Next steps:**
785
+ - Use \`${ticketTool.workspaceSlug}:tickets:message\` to log progress
786
+ - Use \`${ticketTool.workspaceSlug}:tickets:close\` when done`
766
787
  }
767
788
  ]
768
789
  };
769
790
  } catch (error) {
770
791
  const errorMessage = error instanceof Error ? error.message : "Unknown error";
771
- console.error(`[MCP] tickets:list error:`, error);
792
+ console.error(`[MCP] tickets:open error:`, error);
772
793
  return {
773
794
  content: [
774
795
  {
775
796
  type: "text",
776
- text: `Error listing tickets: ${errorMessage}`
797
+ text: `Error opening ticket: ${errorMessage}`
777
798
  }
778
799
  ],
779
800
  isError: true
@@ -782,17 +803,17 @@ ${ticketList}`
782
803
  } else if (ticketTool.type === "work") {
783
804
  const ticketSlug = request.params.arguments?.ticketSlug;
784
805
  try {
785
- const result2 = await convexClient.mutation(
806
+ const result2 = await convexClient.query(
786
807
  "mcp_tickets:workMcpTicket",
787
808
  {
788
809
  apiKey: config.apiKey,
789
810
  workspaceSlug: ticketTool.workspaceSlug,
790
811
  ticketSlug
791
- // Optional: specific ticket to work on
812
+ // Optional: specific ticket to resume
792
813
  }
793
814
  );
794
- if (!result2 || !result2.content) {
795
- const message = ticketSlug ? `Ticket "${ticketSlug}" not found or not open in workspace "${ticketTool.workspaceSlug}".` : `No pending tickets in workspace "${ticketTool.workspaceSlug}".`;
815
+ if (!result2) {
816
+ const message = ticketSlug ? `Ticket "${ticketSlug}" not found or not in working status in workspace "${ticketTool.workspaceSlug}".` : `No tickets currently being worked on in workspace "${ticketTool.workspaceSlug}".`;
796
817
  return {
797
818
  content: [
798
819
  {
@@ -802,77 +823,42 @@ ${ticketList}`
802
823
  ]
803
824
  };
804
825
  }
805
- let filesCreated = false;
806
- let ticketFilePath = null;
807
- let sessionFilePath = null;
808
- const effectivePpmDir = result2.effectivePpmDir || path.join(process.cwd(), ".ppm");
809
- const effectiveWorkspaceRoot = result2.workspaceRoot || process.cwd();
810
- try {
811
- const workingDir = path.join(effectivePpmDir, "working");
812
- const ticketFileName = `${result2.ticketNumber}-${result2.slug}.md`;
813
- const sessionFileName = `${result2.ticketNumber}-${result2.slug}.session.md`;
814
- ticketFilePath = path.join(workingDir, ticketFileName);
815
- sessionFilePath = path.join(workingDir, sessionFileName);
816
- fs.mkdirSync(workingDir, { recursive: true });
817
- fs.writeFileSync(ticketFilePath, result2.content, "utf-8");
818
- const sessionTemplate = generateSessionTemplate(
819
- result2.ticketNumber,
820
- result2.slug,
821
- effectiveWorkspaceRoot,
822
- effectivePpmDir
823
- );
824
- fs.writeFileSync(sessionFilePath, sessionTemplate, "utf-8");
825
- filesCreated = true;
826
- } catch (fsError) {
827
- console.error("[MCP] tickets:work file I/O error:", fsError);
828
- }
829
- let responseText = `# Ticket: ${result2.slug} [WORKING]
826
+ let messagesSection = "";
827
+ if (result2.messages && result2.messages.length > 0) {
828
+ const messageList = result2.messages.map((m) => `[${new Date(m.createdAt).toISOString()}] ${m.content}`).join("\n");
829
+ messagesSection = `
830
830
 
831
- `;
832
- responseText += result2.content;
833
- responseText += `
831
+ ## Progress Messages
834
832
 
835
- ---
836
- `;
837
- if (filesCreated) {
838
- responseText += `_Session files created:_
839
- `;
840
- responseText += `- Ticket: \`${ticketFilePath}\`
841
- `;
842
- responseText += `- Session: \`${sessionFilePath}\`
843
-
844
- `;
845
- responseText += `_Use session file to log progress and resume points._
846
- `;
847
- } else {
848
- responseText += `_Local session files could not be created (file system error)._
849
- `;
833
+ ${messageList}`;
850
834
  }
851
- responseText += `
852
- ${result2.remainingTickets} ticket(s) remaining in queue.
853
-
854
- `;
855
- responseText += `**Next steps:**
856
- `;
857
- responseText += `- Use \`${ticketTool.workspaceSlug}:tickets:message\` to log progress
858
- `;
859
- responseText += `- Use \`${ticketTool.workspaceSlug}:tickets:close\` when done`;
835
+ const startedInfo = result2.startedAt ? `
836
+ Started: ${new Date(result2.startedAt).toISOString()}` : "";
860
837
  return {
861
838
  content: [
862
839
  {
863
840
  type: "text",
864
- text: responseText
841
+ text: `# Ticket: ${result2.slug} [WORKING]${startedInfo}
842
+
843
+ ${result2.content}${messagesSection}
844
+
845
+ ---
846
+ _Resuming work on this ticket._
847
+
848
+ **Next steps:**
849
+ - Use \`${ticketTool.workspaceSlug}:tickets:message\` to log progress
850
+ - Use \`${ticketTool.workspaceSlug}:tickets:close\` when done`
865
851
  }
866
852
  ]
867
853
  };
868
854
  } catch (error) {
869
855
  const errorMessage = error instanceof Error ? error.message : "Unknown error";
870
- console.error("[MCP] tickets:work error:", error);
856
+ console.error(`[MCP] tickets:work error:`, error);
871
857
  return {
872
858
  content: [
873
859
  {
874
860
  type: "text",
875
- text: `Error starting ticket work: ${errorMessage}`
861
+ text: `Error resuming ticket: ${errorMessage}`
876
862
  }
877
863
  ],
878
864
  isError: true
@@ -924,53 +910,6 @@ Preview: ${result2.preview}`
924
910
  isError: true
925
911
  };
926
912
  }
927
- } else if (ticketTool.type === "working") {
928
- try {
929
- const tickets = await convexClient.query(
930
- "mcp_tickets:getMcpWorkingTickets",
931
- {
932
- apiKey: config.apiKey,
933
- workspaceSlug: ticketTool.workspaceSlug
934
- }
935
- );
936
- if (tickets.length === 0) {
937
- return {
938
- content: [
939
- {
940
- type: "text",
941
- text: `No tickets currently being worked on in workspace "${ticketTool.workspaceSlug}".`
942
- }
943
- ]
944
- };
945
- }
946
- const ticketList = tickets.map((t) => {
947
- const started = t.startedAt ? `Started: ${new Date(t.startedAt).toISOString()}` : "";
948
- return `\u2022 [${t.slug}] ${t.preview}
949
- ${started}`;
950
- }).join("\n");
951
- return {
952
- content: [
953
- {
954
- type: "text",
955
- text: `Working tickets in "${ticketTool.workspaceSlug}" (${tickets.length} in progress):
956
-
957
- ${ticketList}`
958
- }
959
- ]
960
- };
961
- } catch (error) {
962
- const errorMessage = error instanceof Error ? error.message : "Unknown error";
963
- console.error(`[MCP] tickets:working error:`, error);
964
- return {
965
- content: [
966
- {
967
- type: "text",
968
- text: `Error listing working tickets: ${errorMessage}`
969
- }
970
- ],
971
- isError: true
972
- };
973
- }
974
913
  } else if (ticketTool.type === "close") {
975
914
  const ticketSlug = request.params.arguments?.ticketSlug;
976
915
  if (!ticketSlug) {
@@ -1063,86 +1002,6 @@ Total messages: ${result2.messageCount}`
1063
1002
  isError: true
1064
1003
  };
1065
1004
  }
1066
- } else if (ticketTool.type === "get") {
1067
- const ticketSlug = request.params.arguments?.ticketSlug;
1068
- if (!ticketSlug) {
1069
- return {
1070
- content: [
1071
- {
1072
- type: "text",
1073
- text: `Error: Missing ticketSlug parameter. Usage: Provide a ticket number (e.g., "102") or full slug (e.g., "102-fix-auth").`
1074
- }
1075
- ],
1076
- isError: true
1077
- };
1078
- }
1079
- try {
1080
- const result2 = await convexClient.query(
1081
- "mcp_tickets:getMcpTicket",
1082
- {
1083
- apiKey: config.apiKey,
1084
- workspaceSlug: ticketTool.workspaceSlug,
1085
- ticketSlug
1086
- }
1087
- );
1088
- if (!result2) {
1089
- return {
1090
- content: [
1091
- {
1092
- type: "text",
1093
- text: `Ticket "${ticketSlug}" not found in workspace "${ticketTool.workspaceSlug}".`
1094
- }
1095
- ]
1096
- };
1097
- }
1098
- let statusLabel;
1099
- if (result2.status === "open") {
1100
- statusLabel = "\u{1F7E2} OPEN";
1101
- } else if (result2.status === "working") {
1102
- statusLabel = "\u{1F7E1} WORKING";
1103
- } else {
1104
- statusLabel = "\u26AB CLOSED";
1105
- }
1106
- const startedInfo = result2.startedAt ? `
1107
- Started: ${new Date(result2.startedAt).toISOString()}` : "";
1108
- const closedInfo = result2.closedAt ? `
1109
- Closed: ${new Date(result2.closedAt).toISOString()}` : "";
1110
- let messagesSection = "";
1111
- if (result2.messages && result2.messages.length > 0) {
1112
- const messageList = result2.messages.map((m) => `[${new Date(m.createdAt).toISOString()}] ${m.content}`).join("\n");
1113
- messagesSection = `
1114
-
1115
- ## Progress Messages
1116
-
1117
- ${messageList}`;
1118
- }
1119
- return {
1120
- content: [
1121
- {
1122
- type: "text",
1123
- text: `# Ticket: ${result2.slug}
1124
-
1125
- Status: ${statusLabel}${startedInfo}${closedInfo}
1126
-
1127
- ---
1128
-
1129
- ${result2.content}${messagesSection}`
1130
- }
1131
- ]
1132
- };
1133
- } catch (error) {
1134
- const errorMessage = error instanceof Error ? error.message : "Unknown error";
1135
- console.error(`[MCP] tickets:get error:`, error);
1136
- return {
1137
- content: [
1138
- {
1139
- type: "text",
1140
- text: `Error getting ticket: ${errorMessage}`
1141
- }
1142
- ],
1143
- isError: true
1144
- };
1145
- }
1146
1005
  }
1147
1006
  }
1148
1007
  const prompt = validPrompts.find((p) => buildPromptName(p) === toolName);
@@ -1203,27 +1062,6 @@ async function fetchMcpPrompts(client, apiKey, workspaces) {
1203
1062
  );
1204
1063
  }
1205
1064
  }
1206
- function generateSessionTemplate(ticketNumber, slug, workspaceRoot, effectivePpmDir) {
1207
- const timestamp = (/* @__PURE__ */ new Date()).toISOString();
1208
- return `# Session: ${ticketNumber}-${slug}
1209
- Started: ${timestamp}
1210
-
1211
- ## Context
1212
- - Ticket: ${ticketNumber}-${slug}
1213
- - Working directory: ${workspaceRoot || "Not configured"}
1214
- - PPM directory: ${effectivePpmDir || "Not configured"}
1215
-
1216
- ## Progress
1217
-
1218
- <!-- Append progress entries with timestamps -->
1219
-
1220
- ## Resume Point
1221
- <!-- Note where to pick up if interrupted -->
1222
-
1223
- ## Notes
1224
- <!-- Observations, blockers, questions -->
1225
- `;
1226
- }
1227
1065
 
1228
1066
  // src/index.ts
1229
1067
  async function main() {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/convex-client.ts","../src/server.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 // Parse --workspaces CLI argument (Ticket 144)\n // Can be: --workspaces ws1,ws2 OR --workspaces ws1 --workspaces ws2 OR -w ws1,ws2\n const workspacesArg = argv.workspaces || argv.w;\n let selectedWorkspaces: string[] = [];\n if (workspacesArg) {\n if (Array.isArray(workspacesArg)) {\n // Multiple --workspaces flags: [\"ws1\", \"ws2\"]\n selectedWorkspaces = workspacesArg.flatMap((w: string) => String(w).split(','));\n } else {\n // Single --workspaces flag with comma-separated: \"ws1,ws2\"\n selectedWorkspaces = String(workspacesArg).split(',');\n }\n // Clean up whitespace\n selectedWorkspaces = selectedWorkspaces.map((s) => s.trim()).filter(Boolean);\n }\n\n // Check for API key (PPM_API_KEY with backward compatibility for THEPROMPTEDITOR_API_KEY)\n const apiKey = process.env.PPM_API_KEY || process.env.THEPROMPTEDITOR_API_KEY;\n if (!apiKey) {\n console.error(\n \"[MCP] ERROR: Missing PPM_API_KEY environment variable\"\n );\n console.error(\n \"[MCP] Please set your API key from Prompt Project Manager Settings page:\"\n );\n console.error(\"[MCP] export PPM_API_KEY=your_api_key_here\");\n process.exit(1);\n }\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 apiKey,\n isDev,\n convexUrl,\n selectedWorkspaces, // Workspace slugs to filter (empty = all workspaces)\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 { McpPrompt, ServerConfig } from \"./types.js\";\nimport * as fs from \"fs\";\nimport * as path from \"path\";\n\n/**\n * Input for the system:optimize-prompt tool\n */\ninterface OptimizePromptInput {\n promptSlug: string;\n workspaceSlug: string;\n suggestions: Array<{\n category: string;\n priority: string;\n issue: string;\n originalText?: string;\n suggestedText: string;\n }>;\n}\n\n/**\n * Ticket info from the backend (Ticket 135)\n */\ninterface TicketInfo {\n position: number;\n slug: string;\n preview: string;\n createdAt: number;\n}\n\n/**\n * Result from tickets:work command (Ticket 153: replaces tickets:next with local file support)\n */\ninterface WorkTicketResult {\n content: string | null;\n slug: string;\n ticketNumber: number;\n status: string; // \"working\"\n remainingTickets: number;\n // Ticket 153: New workspace path fields for local file creation\n workspaceRoot: string | null;\n localPpmDir: string | null;\n effectivePpmDir: string | null; // localPpmDir OR \"{workspaceRoot}/.ppm\"\n}\n\n/**\n * Working ticket info (Ticket 151)\n */\ninterface WorkingTicketInfo {\n slug: string;\n preview: string;\n startedAt?: number;\n createdAt: number;\n}\n\n/**\n * Close ticket result (Ticket 151)\n */\ninterface CloseTicketResult {\n slug: string;\n status: string;\n closedAt: number;\n}\n\n/**\n * Message result (Ticket 151)\n */\ninterface AddMessageResult {\n ticketSlug: string;\n messageCount: number;\n createdAt: number;\n}\n\nimport { buildPromptHandler, buildPromptName, buildPromptSchema } from \"./prompt-builder.js\";\n\n/**\n * Built-in system tools (with input schemas for submission)\n *\n * Architecture:\n * - system:optimize: User-facing slash command for full prompt evaluation\n * - system:feedback: User-facing slash command for directed feedback via $ARGUMENTS\n * - system:prompts: Utility tool (works as both slash command and tool)\n * - system:optimize-prompt: Backend submission tool ONLY (not a slash command)\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 // User-facing slash command: /ppm:system:optimize\n {\n name: \"system:optimize\",\n description: \"Evaluate a prompt and submit optimization suggestions to Prompt Project Manager\",\n inputSchema: {\n type: \"object\",\n properties: {},\n },\n isSlashCommand: true,\n promptContent: `# Optimize\n\nEvaluate the prompt you just used and submit optimization suggestions.\n\n## Instructions\n\n1. **Identify the prompt** from the PREVIOUS tool call in this conversation:\n - Look at the last \\`/ppm:*\\` slash command or MCP tool you called\n - The format is \\`/ppm:workspace:prompt\\` (e.g., \\`/ppm:test:joke\\` means workspace=\\`test\\`, prompt=\\`joke\\`)\n - Extract the workspace slug (before the colon) and prompt slug (after the colon)\n - **CRITICAL**: Use the EXACT slugs from the tool name, do NOT guess or invent slugs\n\n2. **Evaluate** for issues in these categories:\n - **clarity**: Is the language clear and unambiguous?\n - **specificity**: Are instructions specific enough?\n - **structure**: Is the organization logical?\n - **completeness**: Are there missing instructions?\n - **efficiency**: Is there redundancy to remove?\n\n3. **Call the submission tool** (\\`system:optimize-prompt\\`) with:\n - \\`promptSlug\\`: The prompt's slug\n - \\`workspaceSlug\\`: The workspace slug\n - \\`suggestions\\`: Array of issues found, each with category, priority, issue, originalText (optional), suggestedText\n\n4. The tool will submit and confirm the count\n\n## Scope of Analysis\n\n**CRITICAL - READ CAREFULLY**:\n\nThe flattened prompt contains TWO parts:\n1. **MAIN PROMPT CONTENT** (at the top) - This is what you should analyze\n2. **Technical Reference Footer** (at the bottom) - **COMPLETELY IGNORE THIS ENTIRE SECTION**\n\n**The Technical Reference Footer starts with \\`# Technical Reference\\` and includes:**\n- \\`## Variable Definitions:\\` - Contains variable names, types, and descriptions\n- \\`## Snippet Definitions:\\` - Contains snippet content\n- \\`## CRITICAL RULES:\\` - System rules\n- \\`## Runtime Variables:\\` - Runtime placeholders\n\n**IGNORE ALL CONTENT IN THE TECHNICAL REFERENCE FOOTER** - even if it contains typos, grammar issues, or formatting problems. These sections are auto-generated from user-defined resources and should NOT be optimized through this tool.\n\n**Only analyze and suggest changes for content BEFORE the \\`# Technical Reference\\` heading.**\n\n## User Feedback\n\nIf the user provided feedback about issues they experienced, it appears below:\n\n**Feedback**: $ARGUMENTS\n\nWhen feedback is provided:\n- Treat it as context about real problems encountered during use\n- Prioritize suggestions that address the described issues\n- Look for patterns in the prompt that could cause the mentioned problem\n\n## Important\n\n- Don't suggest changes to variable references like \\`[VARIABLE_NAME]\\` - those are intentional\n- The user will review suggestions in Prompt Project Manager web UI`,\n },\n // User-facing slash command: /ppm:system:feedback\n {\n name: \"system:feedback\",\n description: \"Submit user feedback about a prompt to create an optimization suggestion\",\n inputSchema: {\n type: \"object\",\n properties: {},\n },\n isSlashCommand: true,\n promptContent: `# Feedback\n\nSubmit your feedback about a prompt to create an optimization suggestion.\n\n## Instructions\n\n1. **Identify the prompt** from the PREVIOUS tool call in this conversation:\n - Look at the last \\`/ppm:*\\` slash command or MCP tool you called\n - The format is \\`/ppm:workspace:prompt\\` (e.g., \\`/ppm:test:joke\\` means workspace=\\`test\\`, prompt=\\`joke\\`)\n - Extract the workspace slug (before the colon) and prompt slug (after the colon)\n - **CRITICAL**: Use the EXACT slugs from the tool name, do NOT guess or invent slugs\n\n2. **Read the user's feedback** from $ARGUMENTS below\n\n3. **Structure the feedback** into a proper optimization suggestion:\n - Determine the most appropriate category: clarity, specificity, structure, completeness, or efficiency\n - Assess priority: \"high\" (critical issue), \"medium\" (noticeable problem), \"low\" (minor improvement)\n - Identify the specific text to change (originalText) if applicable\n - Write a clear, actionable fix (suggestedText)\n\n4. **Call the submission tool** (\\`system:optimize-prompt\\`) with:\n - \\`promptSlug\\`: The prompt's slug\n - \\`workspaceSlug\\`: The workspace slug\n - \\`suggestions\\`: Array containing a SINGLE suggestion based on the user's feedback\n\n## User Feedback\n\n**$ARGUMENTS**\n\n## Scope of Analysis\n\nWhen analyzing the prompt to address the feedback:\n- **ONLY** focus on content BEFORE the \\`# Technical Reference\\` heading\n- **IGNORE** the Technical Reference Footer (Variable Definitions, Snippet Definitions, CRITICAL RULES, Runtime Variables)\n- Don't suggest changes to variable references like \\`[VARIABLE_NAME]\\` - those are intentional\n\n## Example\n\nIf the user runs:\n\\`/feedback the output format should specify JSON not markdown\\`\n\nYou should:\n1. Find the prompt that was used earlier in the session\n2. Create a suggestion like:\n \\`\\`\\`json\n {\n \"category\": \"specificity\",\n \"priority\": \"medium\",\n \"issue\": \"Output format should specify JSON instead of markdown\",\n \"originalText\": \"Output format: markdown\",\n \"suggestedText\": \"Output format: JSON with fields: { result: string, status: string }\"\n }\n \\`\\`\\`\n3. Submit via \\`system:optimize-prompt\\` tool\n\n## Important\n\n- Create exactly ONE suggestion from the user's feedback (not multiple)\n- The suggestion will be reviewed in Prompt Project Manager web UI\n- Use your understanding of the session to interpret vague feedback into actionable fixes`,\n },\n // Utility tool: /ppm:system:prompts\n {\n name: \"system:prompts\",\n description: \"List all available prompts from Prompt Project Manager\",\n inputSchema: {\n type: \"object\",\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 isSlashCommand: true,\n promptContent: `# List Prompts\n\nThis is a utility tool. Call it directly as a tool to list all available prompts.\n\nExample: Use the \\`system:prompts\\` tool with optional \\`search\\` parameter to filter results.`,\n },\n // Backend submission tool (called by system:optimize and system:feedback)\n // NOT a slash command - only available as a tool for AI to call\n {\n name: \"system:optimize-prompt\",\n description: \"Submit optimization suggestions to Prompt Project Manager (internal tool)\",\n inputSchema: {\n type: \"object\",\n properties: {\n promptSlug: {\n type: \"string\",\n description: \"The slug of the prompt to optimize (e.g., 'code-review')\",\n },\n workspaceSlug: {\n type: \"string\",\n description: \"The workspace containing the prompt (e.g., 'personal')\",\n },\n suggestions: {\n type: \"array\",\n description: \"Array of optimization suggestions\",\n items: {\n type: \"object\",\n properties: {\n category: {\n type: \"string\",\n description: \"Category: 'clarity', 'specificity', 'structure', 'completeness', 'efficiency'\",\n },\n priority: {\n type: \"string\",\n enum: [\"high\", \"medium\", \"low\"],\n description: \"Priority level\",\n },\n issue: {\n type: \"string\",\n description: \"Brief description of the problem\",\n },\n originalText: {\n type: \"string\",\n description: \"Optional: The specific text that should be changed\",\n },\n suggestedText: {\n type: \"string\",\n description: \"The improved text\",\n },\n },\n required: [\"category\", \"priority\", \"issue\", \"suggestedText\"],\n },\n },\n },\n required: [\"promptSlug\", \"workspaceSlug\", \"suggestions\"],\n },\n isSlashCommand: false, // Tool only - not a slash command\n promptContent: `# Submit Optimization Suggestions\n\nThis is an internal submission tool. Do not call it directly.\n\nUse \\`/ppm:system:optimize\\` for full prompt evaluation or \\`/ppm:system:feedback\\` to submit specific feedback.`,\n },\n];\n\n/**\n * Initialize and start the MCP server\n */\nexport async function startServer(\n config: ServerConfig,\n convexClient: ConvexHttpClient\n): Promise<void> {\n // Validate API key with Convex\n console.error(\"[MCP] Validating API key...\");\n const validation = await validateApiKey(convexClient, config.apiKey);\n if (!validation.valid) {\n throw new Error(`Invalid API key: ${validation.error}`);\n }\n console.error(`[MCP] API key validated for user: ${validation.userId}`);\n\n // Fetch prompts exposed to MCP (with optional workspace filter from CLI)\n console.error(\"[MCP] Fetching exposed prompts...\");\n const prompts = await fetchMcpPrompts(convexClient, config.apiKey, config.selectedWorkspaces);\n console.error(`[MCP] Found ${prompts.length} exposed prompts`);\n\n if (prompts.length === 0) {\n if (config.selectedWorkspaces.length > 0) {\n console.error(\n `[MCP] WARNING: No prompts found in workspaces: ${config.selectedWorkspaces.join(', ')}. Check that these workspace slugs exist and contain prompts.`\n );\n } else {\n console.error(\n \"[MCP] WARNING: No prompts found. Create some prompts in Prompt Project Manager to expose them via MCP.\"\n );\n }\n }\n\n // Filter out prompts with missing flattened content\n const validPrompts = prompts.filter((p) => {\n if (!p.flattenedPrompt) {\n console.error(\n `[MCP] WARNING: Skipping prompt \"${p.name}\" (${p.slug}) - missing flattened content. Re-save in ContextFS to regenerate.`\n );\n return false;\n }\n return true;\n });\n\n // Log workspace filtering info (Ticket 144)\n if (config.selectedWorkspaces.length > 0) {\n console.error(`[MCP] Workspace filter: ${config.selectedWorkspaces.join(', ')}`);\n } else {\n console.error(`[MCP] Workspace filter: ALL (no --workspaces specified)`);\n }\n console.error(`[MCP] Registering ${validPrompts.length} prompts...`);\n\n // Build dynamic ticket tools per workspace (Ticket 135, 149, 151, 153)\n // Each workspace gets :tickets:list, :tickets:work, :tickets:working, :tickets:close, :tickets:message, :tickets:get, and :tickets:create tools\n const workspaceSlugs = new Set(validPrompts.map((p) => p.workspaceSlug).filter((s): s is string => !!s));\n const dynamicTicketTools: { name: string; description: string; workspaceSlug: string; type: 'list' | 'work' | 'working' | 'close' | 'message' | 'get' | 'create' }[] = [];\n\n for (const workspaceSlug of workspaceSlugs) {\n dynamicTicketTools.push({\n name: `${workspaceSlug}:tickets:list`,\n description: `List pending tickets in the \"${workspaceSlug}\" workspace queue`,\n workspaceSlug,\n type: 'list',\n });\n // Ticket 153: Renamed tickets:next → tickets:work\n dynamicTicketTools.push({\n name: `${workspaceSlug}:tickets:work`,\n description: `Start work on a ticket with local session files in the \"${workspaceSlug}\" workspace. Optionally specify a ticket number or slug.`,\n workspaceSlug,\n type: 'work',\n });\n // Ticket 151: New working/close/message tools\n dynamicTicketTools.push({\n name: `${workspaceSlug}:tickets:working`,\n description: `List all tickets currently being worked on in the \"${workspaceSlug}\" workspace`,\n workspaceSlug,\n type: 'working',\n });\n dynamicTicketTools.push({\n name: `${workspaceSlug}:tickets:close`,\n description: `Mark a working ticket as completed in the \"${workspaceSlug}\" workspace`,\n workspaceSlug,\n type: 'close',\n });\n dynamicTicketTools.push({\n name: `${workspaceSlug}:tickets:message`,\n description: `Add a progress message to a working or closed ticket in the \"${workspaceSlug}\" workspace`,\n workspaceSlug,\n type: 'message',\n });\n dynamicTicketTools.push({\n name: `${workspaceSlug}:tickets:get`,\n description: `Get a specific ticket from the \"${workspaceSlug}\" workspace by number or slug`,\n workspaceSlug,\n type: 'get',\n });\n dynamicTicketTools.push({\n name: `${workspaceSlug}:tickets:create`,\n description: `Create a new ticket in the \"${workspaceSlug}\" workspace queue`,\n workspaceSlug,\n type: 'create',\n });\n }\n\n console.error(`[MCP] Registering ${dynamicTicketTools.length} ticket tools for ${workspaceSlugs.size} workspace(s)...`);\n\n // Check for duplicate prompt names\n const promptNames = new Set<string>();\n const duplicates: string[] = [];\n validPrompts.forEach((p) => {\n const name = buildPromptName(p);\n if (promptNames.has(name)) {\n duplicates.push(name);\n }\n promptNames.add(name);\n });\n\n if (duplicates.length > 0) {\n console.error(\n `[MCP] WARNING: Duplicate prompt names 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: \"2.8.1\",\n },\n {\n capabilities: {\n prompts: {},\n tools: {},\n },\n }\n );\n\n // Register list_prompts handler\n server.setRequestHandler(ListPromptsRequestSchema, async () => {\n const uniquePrompts = Array.from(\n new Map(validPrompts.map((p) => [buildPromptName(p), p])).values()\n );\n\n // Build system tool schemas (only those marked as slash commands)\n const systemPromptSchemas = SYSTEM_TOOLS\n .filter((st) => st.isSlashCommand) // Only include user-facing slash commands\n .map((st) => ({\n name: st.name,\n description: st.description,\n }));\n\n // Build ticket prompt schemas (Ticket 135, 149 - register as slash commands)\n // Note: tickets:get is NOT a slash command - it's only available as a follow-on tool\n const ticketPromptSchemas = dynamicTicketTools\n .filter((tt) => tt.type !== 'get')\n .map((tt) => ({\n name: tt.name,\n description: tt.description,\n }));\n\n return {\n prompts: [\n ...systemPromptSchemas,\n ...ticketPromptSchemas,\n ...uniquePrompts.map(buildPromptSchema),\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., \"workspace:tickets:work 102\")\n // Note: tickets:get is NOT a slash command - only available as a follow-on tool\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 workspaceSlug = ticketWorkMatch[1];\n const ticketArg = ticketWorkMatch[2].trim();\n description = `Start work on ticket \"${ticketArg}\" from \"${workspaceSlug}\"`;\n promptContent = `Start work on ticket \"${ticketArg}\" from the \"${workspaceSlug}\" workspace queue.\\n\\nCall the \\`${workspaceSlug}:tickets:work\\` tool with ticketSlug: \"${ticketArg}\" to start working on the specified ticket.`;\n } else if (ticketTool!.type === 'list') {\n description = ticketTool!.description;\n promptContent = `List all pending tickets in the \"${ticketTool!.workspaceSlug}\" workspace queue.\\n\\nCall the \\`${ticketTool!.name}\\` tool to get the list.`;\n } else if (ticketTool!.type === 'work') {\n // Ticket 153: Renamed from 'next' to 'work'\n description = ticketTool!.description;\n promptContent = `Start work on the next ticket from the \"${ticketTool!.workspaceSlug}\" workspace queue.\\n\\nCall the \\`${ticketTool!.name}\\` tool to get the next ticket and move it to working status. Local session files will be created if PPM directory is configured.\\n\\nYou can also specify a ticket: /ppm:${ticketTool!.workspaceSlug}:tickets:work <number-or-slug>`;\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!.workspaceSlug}\" workspace 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 {\n // type === 'get' - shouldn't reach here as it's not a slash command, but handle gracefully\n description = ticketTool!.description;\n promptContent = `This command is not available as a slash command. Use the \\`${ticketTool!.name}\\` tool directly to inspect a ticket.`;\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 user prompts\n const prompt = validPrompts.find((p) => buildPromptName(p) === promptName);\n\n if (!prompt) {\n throw new Error(`Unknown prompt: ${promptName}`);\n }\n\n const handler = buildPromptHandler(prompt, config, convexClient);\n return await handler();\n });\n\n // Register list_tools handler\n server.setRequestHandler(ListToolsRequestSchema, async () => {\n const uniquePrompts = Array.from(\n new Map(validPrompts.map((p) => [buildPromptName(p), p])).values()\n );\n\n // Build tools array: system tools + ticket tools + each user prompt as a tool\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 workspace (Ticket 135, 149, 151, 153)\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 === 'get' || tt.type === 'close') {\n // Both get and close require 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 // Ticket 153: Renamed from 'next' to '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 (e.g., '102-fix-auth'). If not provided, starts work on next ticket in 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 },\n required: [\"content\"],\n };\n } else if (tt.type === 'message') {\n // Ticket 151: message requires ticketSlug and message\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 message: {\n type: \"string\",\n description: \"Progress message to add to the ticket\",\n },\n },\n required: [\"ticketSlug\", \"message\"],\n };\n } else {\n // list, working: no required 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 // Each user prompt becomes a discoverable tool\n ...uniquePrompts.map((prompt) => ({\n name: buildPromptName(prompt),\n description: prompt.description || `Prompt: ${prompt.name}`,\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 system:prompts tool\n if (toolName === \"system:prompts\") {\n const searchTerm = request.params.arguments?.search as string | undefined;\n const uniquePrompts = Array.from(\n new Map(validPrompts.map((p) => [buildPromptName(p), p])).values()\n );\n\n // Filter system tools\n let filteredSystemTools = SYSTEM_TOOLS;\n if (searchTerm) {\n const lowerSearch = searchTerm.toLowerCase();\n filteredSystemTools = SYSTEM_TOOLS.filter(\n (st) =>\n st.name.toLowerCase().includes(lowerSearch) ||\n st.description.toLowerCase().includes(lowerSearch)\n );\n }\n\n // Filter user prompts\n let filteredPrompts = uniquePrompts;\n if (searchTerm) {\n const lowerSearch = searchTerm.toLowerCase();\n filteredPrompts = uniquePrompts.filter(\n (p) =>\n buildPromptName(p).toLowerCase().includes(lowerSearch) ||\n p.name.toLowerCase().includes(lowerSearch) ||\n p.description?.toLowerCase().includes(lowerSearch)\n );\n }\n\n // Sort user prompts by workspace then by prompt slug\n filteredPrompts.sort((a, b) => {\n const aName = buildPromptName(a);\n const bName = buildPromptName(b);\n return aName.localeCompare(bName);\n });\n\n // Build system tools list\n const systemToolList = filteredSystemTools\n .map((st) => `• ${st.name}\\n Type: System\\n Description: ${st.description}`)\n .join(\"\\n\\n\");\n\n // Build user prompts list\n const userPromptList = filteredPrompts\n .map((p) => {\n const name = buildPromptName(p);\n const workspace = p.workspaceSlug || \"unknown\";\n const desc = p.description || \"No description\";\n return `• ${name}\\n Workspace: ${workspace}\\n Description: ${desc}`;\n })\n .join(\"\\n\\n\");\n\n const totalCount = filteredSystemTools.length + filteredPrompts.length;\n const summary = searchTerm\n ? `Found ${totalCount} prompts matching \"${searchTerm}\":`\n : `Available prompts (${totalCount} total):`;\n\n const sections: string[] = [];\n if (systemToolList) {\n sections.push(`**System Tools:**\\n\\n${systemToolList}`);\n }\n if (userPromptList) {\n sections.push(`**User Prompts:**\\n\\n${userPromptList}`);\n }\n\n return {\n content: [\n {\n type: \"text\",\n text: `${summary}\\n\\n${sections.join(\"\\n\\n\") || \"No prompts found.\"}`,\n },\n ],\n };\n }\n\n // Handle system:optimize-prompt tool (consolidated optimization tool)\n if (toolName === \"system:optimize-prompt\") {\n const input = request.params.arguments as unknown as OptimizePromptInput;\n\n if (!input.promptSlug || !input.workspaceSlug || !input.suggestions) {\n return {\n content: [\n {\n type: \"text\",\n text: \"Error: Missing required fields. Please provide promptSlug, workspaceSlug, and suggestions.\",\n },\n ],\n isError: true,\n };\n }\n\n if (!Array.isArray(input.suggestions) || input.suggestions.length === 0) {\n return {\n content: [\n {\n type: \"text\",\n text: \"Error: suggestions must be a non-empty array.\",\n },\n ],\n isError: true,\n };\n }\n\n try {\n // Call Convex mutation to add suggestions (authenticated via API key)\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const result = await (convexClient as any).mutation(\n \"prompts:addOptimizationSuggestions\",\n {\n apiKey: config.apiKey,\n workspaceSlug: input.workspaceSlug,\n promptSlug: input.promptSlug,\n suggestions: input.suggestions,\n }\n );\n\n return {\n content: [\n {\n type: \"text\",\n text: `Submitted ${result.addedCount} optimization suggestion(s) for prompt \"${input.promptSlug}\" in workspace \"${input.workspaceSlug}\". Review and apply them in Prompt Project Manager.`,\n },\n ],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] system:optimize-prompt error:`, error);\n return {\n content: [\n {\n type: \"text\",\n text: `Error submitting optimization suggestions: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n }\n\n // Handle ticket tool invocations (Ticket 135)\n const ticketTool = dynamicTicketTools.find((tt) => tt.name === toolName);\n if (ticketTool) {\n if (ticketTool.type === 'list') {\n // Handle tickets:list\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const tickets = await (convexClient as any).query(\n \"mcp_tickets:getMcpTickets\",\n {\n apiKey: config.apiKey,\n workspaceSlug: ticketTool.workspaceSlug,\n }\n ) as TicketInfo[];\n\n if (tickets.length === 0) {\n return {\n content: [\n {\n type: \"text\",\n text: `No pending tickets in workspace \"${ticketTool.workspaceSlug}\".`,\n },\n ],\n };\n }\n\n const ticketList = tickets\n .map((t) => `${t.position}. [${t.slug}] ${t.preview}`)\n .join(\"\\n\");\n\n return {\n content: [\n {\n type: \"text\",\n text: `Pending tickets in \"${ticketTool.workspaceSlug}\" (${tickets.length} total):\\n\\n${ticketList}`,\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 === 'work') {\n // Ticket 153: Handle tickets:work (replaces tickets:next with local file support)\n const ticketSlug = request.params.arguments?.ticketSlug as string | undefined;\n\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const result = await (convexClient as any).mutation(\n \"mcp_tickets:workMcpTicket\",\n {\n apiKey: config.apiKey,\n workspaceSlug: ticketTool.workspaceSlug,\n ticketSlug: ticketSlug, // Optional: specific ticket to work on\n }\n ) as WorkTicketResult | null;\n\n if (!result || !result.content) {\n const message = ticketSlug\n ? `Ticket \"${ticketSlug}\" not found or not open in workspace \"${ticketTool.workspaceSlug}\".`\n : `No pending tickets in workspace \"${ticketTool.workspaceSlug}\".`;\n return {\n content: [\n {\n type: \"text\",\n text: message,\n },\n ],\n };\n }\n\n // Ticket 153: Attempt local file creation\n // Use effectivePpmDir from backend, or default to cwd/.ppm for out-of-box experience\n let filesCreated = false;\n let ticketFilePath: string | null = null;\n let sessionFilePath: string | null = null;\n\n const effectivePpmDir = result.effectivePpmDir || path.join(process.cwd(), '.ppm');\n const effectiveWorkspaceRoot = result.workspaceRoot || process.cwd();\n\n try {\n const workingDir = path.join(effectivePpmDir, 'working');\n const ticketFileName = `${result.ticketNumber}-${result.slug}.md`;\n const sessionFileName = `${result.ticketNumber}-${result.slug}.session.md`;\n\n ticketFilePath = path.join(workingDir, ticketFileName);\n sessionFilePath = path.join(workingDir, sessionFileName);\n\n // Create directory (recursive)\n fs.mkdirSync(workingDir, { recursive: true });\n\n // Write ticket file (flattened content)\n fs.writeFileSync(ticketFilePath, result.content, 'utf-8');\n\n // Write session file (template)\n const sessionTemplate = generateSessionTemplate(\n result.ticketNumber,\n result.slug,\n effectiveWorkspaceRoot,\n effectivePpmDir\n );\n fs.writeFileSync(sessionFilePath, sessionTemplate, 'utf-8');\n\n filesCreated = true;\n } catch (fsError) {\n console.error(\"[MCP] tickets:work file I/O error:\", fsError);\n // Continue without files - graceful degradation\n }\n\n // Build response\n let responseText = `# Ticket: ${result.slug} [WORKING]\\n\\n`;\n responseText += result.content;\n responseText += `\\n\\n---\\n`;\n\n if (filesCreated) {\n responseText += `_Session files created:_\\n`;\n responseText += `- Ticket: \\`${ticketFilePath}\\`\\n`;\n responseText += `- Session: \\`${sessionFilePath}\\`\\n\\n`;\n responseText += `_Use session file to log progress and resume points._\\n`;\n } else {\n responseText += `_Local session files could not be created (file system error)._\\n`;\n }\n\n responseText += `\\n${result.remainingTickets} ticket(s) remaining in queue.\\n\\n`;\n responseText += `**Next steps:**\\n`;\n responseText += `- Use \\`${ticketTool.workspaceSlug}:tickets:message\\` to log progress\\n`;\n responseText += `- Use \\`${ticketTool.workspaceSlug}:tickets:close\\` when done`;\n\n return {\n content: [\n {\n type: \"text\",\n text: responseText,\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 starting ticket work: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n } else if (ticketTool.type === 'create') {\n // Handle tickets:create (Ticket 149)\n const content = request.params.arguments?.content as string | undefined;\n\n if (!content) {\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 try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const result = await (convexClient as any).mutation(\n \"mcp_tickets:createMcpTicket\",\n {\n apiKey: config.apiKey,\n workspaceSlug: ticketTool.workspaceSlug,\n content,\n }\n ) as { slug: string; preview: string; position: number; totalOpen: number };\n\n return {\n content: [\n {\n type: \"text\",\n text: `✅ Created ticket [${result.slug}] in workspace \"${ticketTool.workspaceSlug}\".\\n\\nPosition: #${result.position} of ${result.totalOpen} open tickets\\nPreview: ${result.preview}`,\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 === 'working') {\n // Ticket 151: Handle tickets:working\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const tickets = await (convexClient as any).query(\n \"mcp_tickets:getMcpWorkingTickets\",\n {\n apiKey: config.apiKey,\n workspaceSlug: ticketTool.workspaceSlug,\n }\n ) as WorkingTicketInfo[];\n\n if (tickets.length === 0) {\n return {\n content: [\n {\n type: \"text\",\n text: `No tickets currently being worked on in workspace \"${ticketTool.workspaceSlug}\".`,\n },\n ],\n };\n }\n\n const ticketList = tickets\n .map((t) => {\n const started = t.startedAt ? `Started: ${new Date(t.startedAt).toISOString()}` : '';\n return `• [${t.slug}] ${t.preview}\\n ${started}`;\n })\n .join(\"\\n\");\n\n return {\n content: [\n {\n type: \"text\",\n text: `Working tickets in \"${ticketTool.workspaceSlug}\" (${tickets.length} in progress):\\n\\n${ticketList}`,\n },\n ],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] tickets:working error:`, error);\n return {\n content: [\n {\n type: \"text\",\n text: `Error listing working tickets: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n } else if (ticketTool.type === 'close') {\n // Ticket 151: Handle tickets:close\n const ticketSlug = request.params.arguments?.ticketSlug as string | undefined;\n\n if (!ticketSlug) {\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 try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const result = await (convexClient as any).mutation(\n \"mcp_tickets:closeMcpTicket\",\n {\n apiKey: config.apiKey,\n workspaceSlug: ticketTool.workspaceSlug,\n ticketSlug: ticketSlug,\n }\n ) as CloseTicketResult;\n\n return {\n content: [\n {\n type: \"text\",\n text: `✅ Ticket [${result.slug}] closed in workspace \"${ticketTool.workspaceSlug}\".\\n\\nClosed at: ${new Date(result.closedAt).toISOString()}`,\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 === 'message') {\n // Ticket 151: Handle tickets:message\n const ticketSlug = request.params.arguments?.ticketSlug as string | undefined;\n const message = request.params.arguments?.message as string | undefined;\n\n if (!ticketSlug || !message) {\n return {\n content: [\n {\n type: \"text\",\n text: `Error: Missing required parameters. Provide ticketSlug and message.`,\n },\n ],\n isError: true,\n };\n }\n\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const result = await (convexClient as any).mutation(\n \"mcp_tickets:addMcpTicketMessage\",\n {\n apiKey: config.apiKey,\n workspaceSlug: ticketTool.workspaceSlug,\n ticketSlug: ticketSlug,\n message: message,\n }\n ) as AddMessageResult;\n\n return {\n content: [\n {\n type: \"text\",\n text: `✅ Message added to ticket [${result.ticketSlug}].\\n\\nTotal messages: ${result.messageCount}`,\n },\n ],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] tickets:message error:`, error);\n return {\n content: [\n {\n type: \"text\",\n text: `Error adding message: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n } else if (ticketTool.type === 'get') {\n // Handle tickets:get\n const ticketSlug = request.params.arguments?.ticketSlug as string | undefined;\n\n if (!ticketSlug) {\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 try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const result = await (convexClient as any).query(\n \"mcp_tickets:getMcpTicket\",\n {\n apiKey: config.apiKey,\n workspaceSlug: ticketTool.workspaceSlug,\n ticketSlug: ticketSlug,\n }\n ) as { slug: string; content: string; status: string; createdAt: number; startedAt?: number; closedAt?: number; messages?: Array<{ content: string; createdAt: number }> } | null;\n\n if (!result) {\n return {\n content: [\n {\n type: \"text\",\n text: `Ticket \"${ticketSlug}\" not found in workspace \"${ticketTool.workspaceSlug}\".`,\n },\n ],\n };\n }\n\n // Ticket 151: Updated status labels\n let statusLabel: string;\n if (result.status === 'open') {\n statusLabel = '🟢 OPEN';\n } else if (result.status === 'working') {\n statusLabel = '🟡 WORKING';\n } else {\n statusLabel = '⚫ CLOSED';\n }\n\n const startedInfo = result.startedAt\n ? `\\nStarted: ${new Date(result.startedAt).toISOString()}`\n : '';\n const closedInfo = result.closedAt\n ? `\\nClosed: ${new Date(result.closedAt).toISOString()}`\n : '';\n\n // Ticket 151: Include messages if any\n let messagesSection = '';\n if (result.messages && result.messages.length > 0) {\n const messageList = result.messages\n .map((m) => `[${new Date(m.createdAt).toISOString()}] ${m.content}`)\n .join('\\n');\n messagesSection = `\\n\\n## Progress Messages\\n\\n${messageList}`;\n }\n\n return {\n content: [\n {\n type: \"text\",\n text: `# Ticket: ${result.slug}\\n\\nStatus: ${statusLabel}${startedInfo}${closedInfo}\\n\\n---\\n\\n${result.content}${messagesSection}`,\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 }\n }\n\n // Handle user prompt tool invocations\n const prompt = validPrompts.find((p) => buildPromptName(p) === toolName);\n\n if (!prompt) {\n throw new Error(`Unknown tool: ${toolName}`);\n }\n\n // Execute the prompt and return flattened content\n const handler = buildPromptHandler(prompt, config, convexClient);\n const result = await handler();\n\n // Convert prompt result to tool result format\n // Extract text from prompt messages\n const promptText = result.messages\n .map((msg) => (typeof msg.content === 'string' ? msg.content : msg.content.text))\n .join('\\n\\n');\n\n return {\n content: [\n {\n type: \"text\",\n text: promptText,\n },\n ],\n };\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 API key with Convex\n */\nasync function validateApiKey(\n client: ConvexHttpClient,\n apiKey: string\n): Promise<{ valid: boolean; userId?: string; error?: string }> {\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const result = await client.query(\"apiKeys:validateApiKey\" as any, { key: apiKey });\n if (result) {\n return { valid: true, userId: result.userId };\n }\n return { valid: false, error: \"Invalid API key\" };\n } catch (error) {\n return {\n valid: false,\n error: error instanceof Error ? error.message : \"Unknown error\",\n };\n }\n}\n\n/**\n * Fetch prompts exposed to MCP\n * Passes workspace filter from CLI --workspaces arg (Ticket 144)\n */\nasync function fetchMcpPrompts(\n client: ConvexHttpClient,\n apiKey: string,\n workspaces: string[]\n): Promise<McpPrompt[]> {\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const prompts = await client.query(\"mcp_prompts:getMcpPrompts\" as any, {\n apiKey,\n workspaces: workspaces.length > 0 ? workspaces : undefined, // Only pass if specified\n });\n return prompts;\n } catch (error) {\n throw new Error(\n `Failed to fetch prompts: ${error instanceof Error ? error.message : \"Unknown error\"}`\n );\n }\n}\n\n/**\n * Generate session template for local ticket session file (Ticket 153)\n */\nfunction generateSessionTemplate(\n ticketNumber: number,\n slug: string,\n workspaceRoot: string | null,\n effectivePpmDir: string | null\n): string {\n const timestamp = new Date().toISOString();\n\n return `# Session: ${ticketNumber}-${slug}\nStarted: ${timestamp}\n\n## Context\n- Ticket: ${ticketNumber}-${slug}\n- Working directory: ${workspaceRoot || 'Not configured'}\n- PPM directory: ${effectivePpmDir || 'Not configured'}\n\n## Progress\n\n<!-- Append progress entries with timestamps -->\n\n## Resume Point\n<!-- Note where to pick up if interrupted -->\n\n## Notes\n<!-- Observations, blockers, questions -->\n`;\n}\n","import type { ConvexHttpClient } from \"convex/browser\";\nimport type { McpPrompt, ServerConfig } from \"./types.js\";\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 (workspace 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/**\n * Build the MCP prompt name for a prompt with workspace scoping\n *\n * MCP tool names must match pattern: ^[a-zA-Z0-9_:-]{1,64}$\n * (letters, numbers, underscores, hyphens, and colons allowed)\n *\n * Format: workspace:prompt\n * - Folders are NOT included in slash command names\n * - Folders are only used for ZIP download organization\n *\n * Character mapping:\n * - Separator: : (colon) between workspace and prompt\n * - Hyphens: - used within multi-word slugs\n * - Example: personal:code-review, work:api-design\n */\nexport function buildPromptName(prompt: McpPrompt): string {\n const workspace = sanitizeForMcp(prompt.workspaceSlug || 'default') || 'default';\n const promptSlug = sanitizeForMcp(prompt.slug || 'unknown') || 'unknown';\n\n // Hierarchical workspace:prompt format\n return `${workspace}:${promptSlug}`.trim();\n}\n\n/**\n * Build the MCP prompt schema for a prompt\n */\nexport function buildPromptSchema(prompt: McpPrompt) {\n return {\n name: buildPromptName(prompt),\n description: prompt.description || `Prompt: ${prompt.name}`,\n arguments: [],\n };\n}\n\n/**\n * Build the prompt execution handler for a prompt\n */\nexport function buildPromptHandler(\n prompt: McpPrompt,\n config: ServerConfig,\n convexClient: ConvexHttpClient\n) {\n return async () => {\n let flattenedPrompt = prompt.flattenedPrompt;\n let promptName = prompt.name;\n\n // Always fetch fresh data from Convex (real-time mode)\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let freshPrompts = await convexClient.query(\"mcp_prompts:getMcpPrompts\" as any, {\n apiKey: config.apiKey,\n });\n\n // Apply workspace filter to fresh data if configured\n if (config.selectedWorkspaces.length > 0) {\n freshPrompts = freshPrompts.filter((p: McpPrompt) =>\n p.workspaceSlug && config.selectedWorkspaces.includes(p.workspaceSlug)\n );\n }\n\n const freshPrompt = freshPrompts.find((p: McpPrompt) => p.slug === prompt.slug && p.workspaceSlug === prompt.workspaceSlug);\n\n if (freshPrompt) {\n flattenedPrompt = freshPrompt.flattenedPrompt;\n promptName = freshPrompt.name;\n console.error(`[MCP] Fetched fresh data for: ${buildPromptName(prompt)}`);\n } else {\n console.error(\n `[MCP] WARNING: Prompt \"${prompt.slug}\" not found in fresh fetch, using cached version`\n );\n }\n } catch (error) {\n console.error(\n `[MCP] WARNING: Failed to fetch fresh data, using cached version: ${error instanceof Error ? error.message : \"Unknown error\"}`\n );\n }\n\n if (!flattenedPrompt) {\n throw new Error(\n `Prompt \"${promptName}\" has no flattened content. Please re-save the prompt in ContextFS to regenerate it.`\n );\n }\n\n console.error(`[MCP] Executed prompt: ${buildPromptName(prompt)}`);\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: flattenedPrompt,\n },\n },\n ],\n };\n };\n}\n"],"mappings":"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","names":["result"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/convex-client.ts","../src/server.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 // Parse --workspaces CLI argument (Ticket 144)\n // Can be: --workspaces ws1,ws2 OR --workspaces ws1 --workspaces ws2 OR -w ws1,ws2\n const workspacesArg = argv.workspaces || argv.w;\n let selectedWorkspaces: string[] = [];\n if (workspacesArg) {\n if (Array.isArray(workspacesArg)) {\n // Multiple --workspaces flags: [\"ws1\", \"ws2\"]\n selectedWorkspaces = workspacesArg.flatMap((w: string) => String(w).split(','));\n } else {\n // Single --workspaces flag with comma-separated: \"ws1,ws2\"\n selectedWorkspaces = String(workspacesArg).split(',');\n }\n // Clean up whitespace\n selectedWorkspaces = selectedWorkspaces.map((s) => s.trim()).filter(Boolean);\n }\n\n // Check for API key (PPM_API_KEY with backward compatibility for THEPROMPTEDITOR_API_KEY)\n const apiKey = process.env.PPM_API_KEY || process.env.THEPROMPTEDITOR_API_KEY;\n if (!apiKey) {\n console.error(\n \"[MCP] ERROR: Missing PPM_API_KEY environment variable\"\n );\n console.error(\n \"[MCP] Please set your API key from Prompt Project Manager Settings page:\"\n );\n console.error(\"[MCP] export PPM_API_KEY=your_api_key_here\");\n process.exit(1);\n }\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 apiKey,\n isDev,\n convexUrl,\n selectedWorkspaces, // Workspace slugs to filter (empty = all workspaces)\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 { McpPrompt, ServerConfig } from \"./types.js\";\n\n/**\n * Input for the system:optimize-prompt tool\n */\ninterface OptimizePromptInput {\n promptSlug: string;\n workspaceSlug: string;\n suggestions: Array<{\n category: string;\n priority: string;\n issue: string;\n originalText?: string;\n suggestedText: string;\n }>;\n}\n\n/**\n * Result from executing the next ticket (Ticket 151: now moves to working status)\n */\ninterface ExecuteTicketResult {\n content: string | null;\n slug: string;\n status: string; // Ticket 151: \"working\" (no longer \"closed\")\n remainingTickets: number;\n}\n\n/**\n * Close ticket result (Ticket 151)\n */\ninterface CloseTicketResult {\n slug: string;\n status: string;\n closedAt: number;\n}\n\n/**\n * Message result (Ticket 151)\n */\ninterface AddMessageResult {\n ticketSlug: string;\n messageCount: number;\n createdAt: number;\n}\n\nimport { buildPromptHandler, buildPromptName, buildPromptSchema } from \"./prompt-builder.js\";\n\n/**\n * Built-in system tools (with input schemas for submission)\n *\n * Architecture:\n * - system:optimize: User-facing slash command for full prompt evaluation\n * - system:feedback: User-facing slash command for directed feedback via $ARGUMENTS\n * - system:prompts: Utility tool (works as both slash command and tool)\n * - system:optimize-prompt: Backend submission tool ONLY (not a slash command)\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 // User-facing slash command: /ppm:system:optimize\n {\n name: \"system:optimize\",\n description: \"Evaluate a prompt and submit optimization suggestions to Prompt Project Manager\",\n inputSchema: {\n type: \"object\",\n properties: {},\n },\n isSlashCommand: true,\n promptContent: `# Optimize\n\nEvaluate the prompt you just used and submit optimization suggestions.\n\n## Instructions\n\n1. **Identify the prompt** from the PREVIOUS tool call in this conversation:\n - Look at the last \\`/ppm:*\\` slash command or MCP tool you called\n - The format is \\`/ppm:workspace:prompt\\` (e.g., \\`/ppm:test:joke\\` means workspace=\\`test\\`, prompt=\\`joke\\`)\n - Extract the workspace slug (before the colon) and prompt slug (after the colon)\n - **CRITICAL**: Use the EXACT slugs from the tool name, do NOT guess or invent slugs\n\n2. **Evaluate** for issues in these categories:\n - **clarity**: Is the language clear and unambiguous?\n - **specificity**: Are instructions specific enough?\n - **structure**: Is the organization logical?\n - **completeness**: Are there missing instructions?\n - **efficiency**: Is there redundancy to remove?\n\n3. **Call the submission tool** (\\`system:optimize-prompt\\`) with:\n - \\`promptSlug\\`: The prompt's slug\n - \\`workspaceSlug\\`: The workspace slug\n - \\`suggestions\\`: Array of issues found, each with category, priority, issue, originalText (optional), suggestedText\n\n4. The tool will submit and confirm the count\n\n## Scope of Analysis\n\n**CRITICAL - READ CAREFULLY**:\n\nThe flattened prompt contains TWO parts:\n1. **MAIN PROMPT CONTENT** (at the top) - This is what you should analyze\n2. **Technical Reference Footer** (at the bottom) - **COMPLETELY IGNORE THIS ENTIRE SECTION**\n\n**The Technical Reference Footer starts with \\`# Technical Reference\\` and includes:**\n- \\`## Variable Definitions:\\` - Contains variable names, types, and descriptions\n- \\`## Snippet Definitions:\\` - Contains snippet content\n- \\`## CRITICAL RULES:\\` - System rules\n- \\`## Runtime Variables:\\` - Runtime placeholders\n\n**IGNORE ALL CONTENT IN THE TECHNICAL REFERENCE FOOTER** - even if it contains typos, grammar issues, or formatting problems. These sections are auto-generated from user-defined resources and should NOT be optimized through this tool.\n\n**Only analyze and suggest changes for content BEFORE the \\`# Technical Reference\\` heading.**\n\n## User Feedback\n\nIf the user provided feedback about issues they experienced, it appears below:\n\n**Feedback**: $ARGUMENTS\n\nWhen feedback is provided:\n- Treat it as context about real problems encountered during use\n- Prioritize suggestions that address the described issues\n- Look for patterns in the prompt that could cause the mentioned problem\n\n## Important\n\n- Don't suggest changes to variable references like \\`[VARIABLE_NAME]\\` - those are intentional\n- The user will review suggestions in Prompt Project Manager web UI`,\n },\n // User-facing slash command: /ppm:system:feedback\n {\n name: \"system:feedback\",\n description: \"Submit user feedback about a prompt to create an optimization suggestion\",\n inputSchema: {\n type: \"object\",\n properties: {},\n },\n isSlashCommand: true,\n promptContent: `# Feedback\n\nSubmit your feedback about a prompt to create an optimization suggestion.\n\n## Instructions\n\n1. **Identify the prompt** from the PREVIOUS tool call in this conversation:\n - Look at the last \\`/ppm:*\\` slash command or MCP tool you called\n - The format is \\`/ppm:workspace:prompt\\` (e.g., \\`/ppm:test:joke\\` means workspace=\\`test\\`, prompt=\\`joke\\`)\n - Extract the workspace slug (before the colon) and prompt slug (after the colon)\n - **CRITICAL**: Use the EXACT slugs from the tool name, do NOT guess or invent slugs\n\n2. **Read the user's feedback** from $ARGUMENTS below\n\n3. **Structure the feedback** into a proper optimization suggestion:\n - Determine the most appropriate category: clarity, specificity, structure, completeness, or efficiency\n - Assess priority: \"high\" (critical issue), \"medium\" (noticeable problem), \"low\" (minor improvement)\n - Identify the specific text to change (originalText) if applicable\n - Write a clear, actionable fix (suggestedText)\n\n4. **Call the submission tool** (\\`system:optimize-prompt\\`) with:\n - \\`promptSlug\\`: The prompt's slug\n - \\`workspaceSlug\\`: The workspace slug\n - \\`suggestions\\`: Array containing a SINGLE suggestion based on the user's feedback\n\n## User Feedback\n\n**$ARGUMENTS**\n\n## Scope of Analysis\n\nWhen analyzing the prompt to address the feedback:\n- **ONLY** focus on content BEFORE the \\`# Technical Reference\\` heading\n- **IGNORE** the Technical Reference Footer (Variable Definitions, Snippet Definitions, CRITICAL RULES, Runtime Variables)\n- Don't suggest changes to variable references like \\`[VARIABLE_NAME]\\` - those are intentional\n\n## Example\n\nIf the user runs:\n\\`/feedback the output format should specify JSON not markdown\\`\n\nYou should:\n1. Find the prompt that was used earlier in the session\n2. Create a suggestion like:\n \\`\\`\\`json\n {\n \"category\": \"specificity\",\n \"priority\": \"medium\",\n \"issue\": \"Output format should specify JSON instead of markdown\",\n \"originalText\": \"Output format: markdown\",\n \"suggestedText\": \"Output format: JSON with fields: { result: string, status: string }\"\n }\n \\`\\`\\`\n3. Submit via \\`system:optimize-prompt\\` tool\n\n## Important\n\n- Create exactly ONE suggestion from the user's feedback (not multiple)\n- The suggestion will be reviewed in Prompt Project Manager web UI\n- Use your understanding of the session to interpret vague feedback into actionable fixes`,\n },\n // Utility tool: /ppm:system:prompts\n {\n name: \"system:prompts\",\n description: \"List all available prompts from Prompt Project Manager\",\n inputSchema: {\n type: \"object\",\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 isSlashCommand: true,\n promptContent: `# List Prompts\n\nThis is a utility tool. Call it directly as a tool to list all available prompts.\n\nExample: Use the \\`system:prompts\\` tool with optional \\`search\\` parameter to filter results.`,\n },\n // Backend submission tool (called by system:optimize and system:feedback)\n // NOT a slash command - only available as a tool for AI to call\n {\n name: \"system:optimize-prompt\",\n description: \"Submit optimization suggestions to Prompt Project Manager (internal tool)\",\n inputSchema: {\n type: \"object\",\n properties: {\n promptSlug: {\n type: \"string\",\n description: \"The slug of the prompt to optimize (e.g., 'code-review')\",\n },\n workspaceSlug: {\n type: \"string\",\n description: \"The workspace containing the prompt (e.g., 'personal')\",\n },\n suggestions: {\n type: \"array\",\n description: \"Array of optimization suggestions\",\n items: {\n type: \"object\",\n properties: {\n category: {\n type: \"string\",\n description: \"Category: 'clarity', 'specificity', 'structure', 'completeness', 'efficiency'\",\n },\n priority: {\n type: \"string\",\n enum: [\"high\", \"medium\", \"low\"],\n description: \"Priority level\",\n },\n issue: {\n type: \"string\",\n description: \"Brief description of the problem\",\n },\n originalText: {\n type: \"string\",\n description: \"Optional: The specific text that should be changed\",\n },\n suggestedText: {\n type: \"string\",\n description: \"The improved text\",\n },\n },\n required: [\"category\", \"priority\", \"issue\", \"suggestedText\"],\n },\n },\n },\n required: [\"promptSlug\", \"workspaceSlug\", \"suggestions\"],\n },\n isSlashCommand: false, // Tool only - not a slash command\n promptContent: `# Submit Optimization Suggestions\n\nThis is an internal submission tool. Do not call it directly.\n\nUse \\`/ppm:system:optimize\\` for full prompt evaluation or \\`/ppm:system:feedback\\` to submit specific feedback.`,\n },\n];\n\n/**\n * Initialize and start the MCP server\n */\nexport async function startServer(\n config: ServerConfig,\n convexClient: ConvexHttpClient\n): Promise<void> {\n // Validate API key with Convex\n console.error(\"[MCP] Validating API key...\");\n const validation = await validateApiKey(convexClient, config.apiKey);\n if (!validation.valid) {\n throw new Error(`Invalid API key: ${validation.error}`);\n }\n console.error(`[MCP] API key validated for user: ${validation.userId}`);\n\n // Fetch prompts exposed to MCP (with optional workspace filter from CLI)\n console.error(\"[MCP] Fetching exposed prompts...\");\n const prompts = await fetchMcpPrompts(convexClient, config.apiKey, config.selectedWorkspaces);\n console.error(`[MCP] Found ${prompts.length} exposed prompts`);\n\n if (prompts.length === 0) {\n if (config.selectedWorkspaces.length > 0) {\n console.error(\n `[MCP] WARNING: No prompts found in workspaces: ${config.selectedWorkspaces.join(', ')}. Check that these workspace slugs exist and contain prompts.`\n );\n } else {\n console.error(\n \"[MCP] WARNING: No prompts found. Create some prompts in Prompt Project Manager to expose them via MCP.\"\n );\n }\n }\n\n // Filter out prompts with missing flattened content\n const validPrompts = prompts.filter((p) => {\n if (!p.flattenedPrompt) {\n console.error(\n `[MCP] WARNING: Skipping prompt \"${p.name}\" (${p.slug}) - missing flattened content. Re-save in ContextFS to regenerate.`\n );\n return false;\n }\n return true;\n });\n\n // Log workspace filtering info (Ticket 144)\n if (config.selectedWorkspaces.length > 0) {\n console.error(`[MCP] Workspace filter: ${config.selectedWorkspaces.join(', ')}`);\n } else {\n console.error(`[MCP] Workspace filter: ALL (no --workspaces specified)`);\n }\n console.error(`[MCP] Registering ${validPrompts.length} prompts...`);\n\n // Build dynamic ticket tools per workspace (Ticket 135, 149, 151, 153)\n // Streamlined command set: open, work, close, message, create\n const workspaceSlugs = new Set(validPrompts.map((p) => p.workspaceSlug).filter((s): s is string => !!s));\n const dynamicTicketTools: { name: string; description: string; workspaceSlug: string; type: 'open' | 'work' | 'close' | 'message' | 'create' }[] = [];\n\n for (const workspaceSlug of workspaceSlugs) {\n // Ticket 153: tickets:open (renamed from tickets:next)\n dynamicTicketTools.push({\n name: `${workspaceSlug}:tickets:open`,\n description: `Open the next ticket from the \"${workspaceSlug}\" workspace queue and move it to working status. Optionally specify a ticket number or slug.`,\n workspaceSlug,\n type: 'open',\n });\n // Ticket 153: tickets:work (context recovery for working tickets)\n dynamicTicketTools.push({\n name: `${workspaceSlug}:tickets:work`,\n description: `Resume work on a ticket in the \"${workspaceSlug}\" workspace. Returns ticket content and all progress messages for context recovery.`,\n workspaceSlug,\n type: 'work',\n });\n dynamicTicketTools.push({\n name: `${workspaceSlug}:tickets:close`,\n description: `Mark a working ticket as completed in the \"${workspaceSlug}\" workspace`,\n workspaceSlug,\n type: 'close',\n });\n dynamicTicketTools.push({\n name: `${workspaceSlug}:tickets:message`,\n description: `Add a progress message to a working or closed ticket in the \"${workspaceSlug}\" workspace`,\n workspaceSlug,\n type: 'message',\n });\n dynamicTicketTools.push({\n name: `${workspaceSlug}:tickets:create`,\n description: `Create a new ticket in the \"${workspaceSlug}\" workspace queue`,\n workspaceSlug,\n type: 'create',\n });\n }\n\n console.error(`[MCP] Registering ${dynamicTicketTools.length} ticket tools for ${workspaceSlugs.size} workspace(s)...`);\n\n // Check for duplicate prompt names\n const promptNames = new Set<string>();\n const duplicates: string[] = [];\n validPrompts.forEach((p) => {\n const name = buildPromptName(p);\n if (promptNames.has(name)) {\n duplicates.push(name);\n }\n promptNames.add(name);\n });\n\n if (duplicates.length > 0) {\n console.error(\n `[MCP] WARNING: Duplicate prompt names 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: \"2.8.1\",\n },\n {\n capabilities: {\n prompts: {},\n tools: {},\n },\n }\n );\n\n // Register list_prompts handler\n server.setRequestHandler(ListPromptsRequestSchema, async () => {\n const uniquePrompts = Array.from(\n new Map(validPrompts.map((p) => [buildPromptName(p), p])).values()\n );\n\n // Build system tool schemas (only those marked as slash commands)\n const systemPromptSchemas = SYSTEM_TOOLS\n .filter((st) => st.isSlashCommand) // Only include user-facing slash commands\n .map((st) => ({\n name: st.name,\n description: st.description,\n }));\n\n // Build ticket prompt schemas (Ticket 135, 149, 153 - register as slash commands)\n // All ticket tools are available as slash commands\n const ticketPromptSchemas = dynamicTicketTools.map((tt) => ({\n name: tt.name,\n description: tt.description,\n }));\n\n return {\n prompts: [\n ...systemPromptSchemas,\n ...ticketPromptSchemas,\n ...uniquePrompts.map(buildPromptSchema),\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 :open with optional argument (e.g., \"workspace:tickets:open 102\")\n const ticketTool = dynamicTicketTools.find((tt) => tt.name === promptName);\n const ticketOpenMatch = promptName.match(/^(.+):tickets:open\\s+(.+)$/);\n\n if (ticketTool || ticketOpenMatch) {\n let promptContent: string;\n let description: string;\n\n if (ticketOpenMatch) {\n // Handle tickets:open with specific ticket argument\n const workspaceSlug = ticketOpenMatch[1];\n const ticketArg = ticketOpenMatch[2].trim();\n description = `Open ticket \"${ticketArg}\" from \"${workspaceSlug}\"`;\n promptContent = `Open ticket \"${ticketArg}\" from the \"${workspaceSlug}\" workspace queue and move it to working status.\\n\\nCall the \\`${workspaceSlug}:tickets:open\\` tool with ticketSlug: \"${ticketArg}\".`;\n } else if (ticketTool!.type === 'open') {\n // Ticket 153: tickets:open (renamed from tickets:next)\n description = ticketTool!.description;\n promptContent = `Open the next ticket from the \"${ticketTool!.workspaceSlug}\" workspace queue and move it to working status.\\n\\nCall the \\`${ticketTool!.name}\\` tool to get the next ticket.\\n\\nYou can also specify a ticket: /ppm:${ticketTool!.workspaceSlug}:tickets:open <number-or-slug>`;\n } else if (ticketTool!.type === 'work') {\n // Ticket 153: tickets:work (context recovery)\n description = ticketTool!.description;\n promptContent = `Resume work on a ticket in the \"${ticketTool!.workspaceSlug}\" workspace.\\n\\nCall the \\`${ticketTool!.name}\\` tool to get ticket content and all progress messages for context recovery.\\n\\nIf no ticket slug is provided, returns the first working ticket (by startedAt).\\n\\nYou can also specify a ticket: /ppm:${ticketTool!.workspaceSlug}:tickets:work <number-or-slug>`;\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!.workspaceSlug}\" workspace 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!.workspaceSlug}\" workspace.\\n\\nCall the \\`${ticketTool!.name}\\` tool with the ticket number or slug.`;\n } else if (ticketTool!.type === 'message') {\n description = ticketTool!.description;\n promptContent = `Add a progress message to a working or closed ticket in the \"${ticketTool!.workspaceSlug}\" workspace.\\n\\nCall the \\`${ticketTool!.name}\\` tool with the ticket number/slug and your message.`;\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 user prompts\n const prompt = validPrompts.find((p) => buildPromptName(p) === promptName);\n\n if (!prompt) {\n throw new Error(`Unknown prompt: ${promptName}`);\n }\n\n const handler = buildPromptHandler(prompt, config, convexClient);\n return await handler();\n });\n\n // Register list_tools handler\n server.setRequestHandler(ListToolsRequestSchema, async () => {\n const uniquePrompts = Array.from(\n new Map(validPrompts.map((p) => [buildPromptName(p), p])).values()\n );\n\n // Build tools array: system tools + ticket tools + each user prompt as a tool\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 workspace (Ticket 135, 149, 151, 153)\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 === 'open') {\n // Ticket 153: tickets:open (renamed from tickets:next)\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 (e.g., '102-fix-auth'). If not provided, opens the next ticket in queue.\",\n },\n },\n required: [],\n };\n } else if (tt.type === 'work') {\n // Ticket 153: tickets:work (context recovery)\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, returns the first working ticket by startedAt.\",\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 },\n required: [\"content\"],\n };\n } else if (tt.type === 'message') {\n // Ticket 151: message requires ticketSlug and message\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 message: {\n type: \"string\",\n description: \"Progress message to add to the ticket\",\n },\n },\n required: [\"ticketSlug\", \"message\"],\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 // Each user prompt becomes a discoverable tool\n ...uniquePrompts.map((prompt) => ({\n name: buildPromptName(prompt),\n description: prompt.description || `Prompt: ${prompt.name}`,\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 system:prompts tool\n if (toolName === \"system:prompts\") {\n const searchTerm = request.params.arguments?.search as string | undefined;\n const uniquePrompts = Array.from(\n new Map(validPrompts.map((p) => [buildPromptName(p), p])).values()\n );\n\n // Filter system tools\n let filteredSystemTools = SYSTEM_TOOLS;\n if (searchTerm) {\n const lowerSearch = searchTerm.toLowerCase();\n filteredSystemTools = SYSTEM_TOOLS.filter(\n (st) =>\n st.name.toLowerCase().includes(lowerSearch) ||\n st.description.toLowerCase().includes(lowerSearch)\n );\n }\n\n // Filter user prompts\n let filteredPrompts = uniquePrompts;\n if (searchTerm) {\n const lowerSearch = searchTerm.toLowerCase();\n filteredPrompts = uniquePrompts.filter(\n (p) =>\n buildPromptName(p).toLowerCase().includes(lowerSearch) ||\n p.name.toLowerCase().includes(lowerSearch) ||\n p.description?.toLowerCase().includes(lowerSearch)\n );\n }\n\n // Sort user prompts by workspace then by prompt slug\n filteredPrompts.sort((a, b) => {\n const aName = buildPromptName(a);\n const bName = buildPromptName(b);\n return aName.localeCompare(bName);\n });\n\n // Build system tools list\n const systemToolList = filteredSystemTools\n .map((st) => `• ${st.name}\\n Type: System\\n Description: ${st.description}`)\n .join(\"\\n\\n\");\n\n // Build user prompts list\n const userPromptList = filteredPrompts\n .map((p) => {\n const name = buildPromptName(p);\n const workspace = p.workspaceSlug || \"unknown\";\n const desc = p.description || \"No description\";\n return `• ${name}\\n Workspace: ${workspace}\\n Description: ${desc}`;\n })\n .join(\"\\n\\n\");\n\n const totalCount = filteredSystemTools.length + filteredPrompts.length;\n const summary = searchTerm\n ? `Found ${totalCount} prompts matching \"${searchTerm}\":`\n : `Available prompts (${totalCount} total):`;\n\n const sections: string[] = [];\n if (systemToolList) {\n sections.push(`**System Tools:**\\n\\n${systemToolList}`);\n }\n if (userPromptList) {\n sections.push(`**User Prompts:**\\n\\n${userPromptList}`);\n }\n\n return {\n content: [\n {\n type: \"text\",\n text: `${summary}\\n\\n${sections.join(\"\\n\\n\") || \"No prompts found.\"}`,\n },\n ],\n };\n }\n\n // Handle system:optimize-prompt tool (consolidated optimization tool)\n if (toolName === \"system:optimize-prompt\") {\n const input = request.params.arguments as unknown as OptimizePromptInput;\n\n if (!input.promptSlug || !input.workspaceSlug || !input.suggestions) {\n return {\n content: [\n {\n type: \"text\",\n text: \"Error: Missing required fields. Please provide promptSlug, workspaceSlug, and suggestions.\",\n },\n ],\n isError: true,\n };\n }\n\n if (!Array.isArray(input.suggestions) || input.suggestions.length === 0) {\n return {\n content: [\n {\n type: \"text\",\n text: \"Error: suggestions must be a non-empty array.\",\n },\n ],\n isError: true,\n };\n }\n\n try {\n // Call Convex mutation to add suggestions (authenticated via API key)\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const result = await (convexClient as any).mutation(\n \"prompts:addOptimizationSuggestions\",\n {\n apiKey: config.apiKey,\n workspaceSlug: input.workspaceSlug,\n promptSlug: input.promptSlug,\n suggestions: input.suggestions,\n }\n );\n\n return {\n content: [\n {\n type: \"text\",\n text: `Submitted ${result.addedCount} optimization suggestion(s) for prompt \"${input.promptSlug}\" in workspace \"${input.workspaceSlug}\". Review and apply them in Prompt Project Manager.`,\n },\n ],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] system:optimize-prompt error:`, error);\n return {\n content: [\n {\n type: \"text\",\n text: `Error submitting optimization suggestions: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n }\n\n // Handle ticket tool invocations (Ticket 135, 153)\n const ticketTool = dynamicTicketTools.find((tt) => tt.name === toolName);\n if (ticketTool) {\n if (ticketTool.type === 'open') {\n // Ticket 153: Handle tickets:open (renamed from tickets:next)\n const ticketSlug = request.params.arguments?.ticketSlug as string | undefined;\n\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const result = await (convexClient as any).mutation(\n \"mcp_tickets:executeMcpNextTicket\",\n {\n apiKey: config.apiKey,\n workspaceSlug: ticketTool.workspaceSlug,\n ticketSlug: ticketSlug, // Optional: specific ticket to open\n }\n ) as ExecuteTicketResult | null;\n\n if (!result) {\n const message = ticketSlug\n ? `Ticket \"${ticketSlug}\" not found or not open in workspace \"${ticketTool.workspaceSlug}\".`\n : `No pending tickets in workspace \"${ticketTool.workspaceSlug}\".`;\n return {\n content: [\n {\n type: \"text\",\n text: message,\n },\n ],\n };\n }\n\n return {\n content: [\n {\n type: \"text\",\n text: `# Ticket: ${result.slug} [WORKING]\\n\\n${result.content}\\n\\n---\\n_Ticket moved to working status. ${result.remainingTickets} ticket(s) remaining in queue._\\n\\n**Next steps:**\\n- Use \\`${ticketTool.workspaceSlug}:tickets:message\\` to log progress\\n- Use \\`${ticketTool.workspaceSlug}:tickets:close\\` when done`,\n },\n ],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] tickets:open error:`, error);\n return {\n content: [\n {\n type: \"text\",\n text: `Error opening ticket: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n } else if (ticketTool.type === 'work') {\n // Ticket 153: Handle tickets:work (context recovery)\n const ticketSlug = request.params.arguments?.ticketSlug as string | undefined;\n\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const result = await (convexClient as any).query(\n \"mcp_tickets:workMcpTicket\",\n {\n apiKey: config.apiKey,\n workspaceSlug: ticketTool.workspaceSlug,\n ticketSlug: ticketSlug, // Optional: specific ticket to resume\n }\n ) as { slug: string; ticketNumber?: number; content: string; startedAt?: number; messages: Array<{ content: string; createdAt: number }> } | null;\n\n if (!result) {\n const message = ticketSlug\n ? `Ticket \"${ticketSlug}\" not found or not in working status in workspace \"${ticketTool.workspaceSlug}\".`\n : `No tickets currently being worked on in workspace \"${ticketTool.workspaceSlug}\".`;\n return {\n content: [\n {\n type: \"text\",\n text: message,\n },\n ],\n };\n }\n\n // Build messages section\n let messagesSection = '';\n if (result.messages && result.messages.length > 0) {\n const messageList = result.messages\n .map((m) => `[${new Date(m.createdAt).toISOString()}] ${m.content}`)\n .join('\\n');\n messagesSection = `\\n\\n## Progress Messages\\n\\n${messageList}`;\n }\n\n const startedInfo = result.startedAt\n ? `\\nStarted: ${new Date(result.startedAt).toISOString()}`\n : '';\n\n return {\n content: [\n {\n type: \"text\",\n text: `# Ticket: ${result.slug} [WORKING]${startedInfo}\\n\\n${result.content}${messagesSection}\\n\\n---\\n_Resuming work on this ticket._\\n\\n**Next steps:**\\n- Use \\`${ticketTool.workspaceSlug}:tickets:message\\` to log progress\\n- Use \\`${ticketTool.workspaceSlug}:tickets:close\\` when done`,\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 resuming ticket: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n } else if (ticketTool.type === 'create') {\n // Handle tickets:create (Ticket 149)\n const content = request.params.arguments?.content as string | undefined;\n\n if (!content) {\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 try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const result = await (convexClient as any).mutation(\n \"mcp_tickets:createMcpTicket\",\n {\n apiKey: config.apiKey,\n workspaceSlug: ticketTool.workspaceSlug,\n content,\n }\n ) as { slug: string; preview: string; position: number; totalOpen: number };\n\n return {\n content: [\n {\n type: \"text\",\n text: `✅ Created ticket [${result.slug}] in workspace \"${ticketTool.workspaceSlug}\".\\n\\nPosition: #${result.position} of ${result.totalOpen} open tickets\\nPreview: ${result.preview}`,\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 ticketSlug = request.params.arguments?.ticketSlug as string | undefined;\n\n if (!ticketSlug) {\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 try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const result = await (convexClient as any).mutation(\n \"mcp_tickets:closeMcpTicket\",\n {\n apiKey: config.apiKey,\n workspaceSlug: ticketTool.workspaceSlug,\n ticketSlug: ticketSlug,\n }\n ) as CloseTicketResult;\n\n return {\n content: [\n {\n type: \"text\",\n text: `✅ Ticket [${result.slug}] closed in workspace \"${ticketTool.workspaceSlug}\".\\n\\nClosed at: ${new Date(result.closedAt).toISOString()}`,\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 === 'message') {\n // Ticket 151: Handle tickets:message\n const ticketSlug = request.params.arguments?.ticketSlug as string | undefined;\n const message = request.params.arguments?.message as string | undefined;\n\n if (!ticketSlug || !message) {\n return {\n content: [\n {\n type: \"text\",\n text: `Error: Missing required parameters. Provide ticketSlug and message.`,\n },\n ],\n isError: true,\n };\n }\n\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const result = await (convexClient as any).mutation(\n \"mcp_tickets:addMcpTicketMessage\",\n {\n apiKey: config.apiKey,\n workspaceSlug: ticketTool.workspaceSlug,\n ticketSlug: ticketSlug,\n message: message,\n }\n ) as AddMessageResult;\n\n return {\n content: [\n {\n type: \"text\",\n text: `✅ Message added to ticket [${result.ticketSlug}].\\n\\nTotal messages: ${result.messageCount}`,\n },\n ],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] tickets:message error:`, error);\n return {\n content: [\n {\n type: \"text\",\n text: `Error adding message: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n }\n }\n\n // Handle user prompt tool invocations\n const prompt = validPrompts.find((p) => buildPromptName(p) === toolName);\n\n if (!prompt) {\n throw new Error(`Unknown tool: ${toolName}`);\n }\n\n // Execute the prompt and return flattened content\n const handler = buildPromptHandler(prompt, config, convexClient);\n const result = await handler();\n\n // Convert prompt result to tool result format\n // Extract text from prompt messages\n const promptText = result.messages\n .map((msg) => (typeof msg.content === 'string' ? msg.content : msg.content.text))\n .join('\\n\\n');\n\n return {\n content: [\n {\n type: \"text\",\n text: promptText,\n },\n ],\n };\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 API key with Convex\n */\nasync function validateApiKey(\n client: ConvexHttpClient,\n apiKey: string\n): Promise<{ valid: boolean; userId?: string; error?: string }> {\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const result = await client.query(\"apiKeys:validateApiKey\" as any, { key: apiKey });\n if (result) {\n return { valid: true, userId: result.userId };\n }\n return { valid: false, error: \"Invalid API key\" };\n } catch (error) {\n return {\n valid: false,\n error: error instanceof Error ? error.message : \"Unknown error\",\n };\n }\n}\n\n/**\n * Fetch prompts exposed to MCP\n * Passes workspace filter from CLI --workspaces arg (Ticket 144)\n */\nasync function fetchMcpPrompts(\n client: ConvexHttpClient,\n apiKey: string,\n workspaces: string[]\n): Promise<McpPrompt[]> {\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const prompts = await client.query(\"mcp_prompts:getMcpPrompts\" as any, {\n apiKey,\n workspaces: workspaces.length > 0 ? workspaces : undefined, // Only pass if specified\n });\n return prompts;\n } catch (error) {\n throw new Error(\n `Failed to fetch prompts: ${error instanceof Error ? error.message : \"Unknown error\"}`\n );\n }\n}\n","import type { ConvexHttpClient } from \"convex/browser\";\nimport type { McpPrompt, ServerConfig } from \"./types.js\";\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 (workspace 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/**\n * Build the MCP prompt name for a prompt with workspace scoping\n *\n * MCP tool names must match pattern: ^[a-zA-Z0-9_:-]{1,64}$\n * (letters, numbers, underscores, hyphens, and colons allowed)\n *\n * Format: workspace:prompt\n * - Folders are NOT included in slash command names\n * - Folders are only used for ZIP download organization\n *\n * Character mapping:\n * - Separator: : (colon) between workspace and prompt\n * - Hyphens: - used within multi-word slugs\n * - Example: personal:code-review, work:api-design\n */\nexport function buildPromptName(prompt: McpPrompt): string {\n const workspace = sanitizeForMcp(prompt.workspaceSlug || 'default') || 'default';\n const promptSlug = sanitizeForMcp(prompt.slug || 'unknown') || 'unknown';\n\n // Hierarchical workspace:prompt format\n return `${workspace}:${promptSlug}`.trim();\n}\n\n/**\n * Build the MCP prompt schema for a prompt\n */\nexport function buildPromptSchema(prompt: McpPrompt) {\n return {\n name: buildPromptName(prompt),\n description: prompt.description || `Prompt: ${prompt.name}`,\n arguments: [],\n };\n}\n\n/**\n * Build the prompt execution handler for a prompt\n */\nexport function buildPromptHandler(\n prompt: McpPrompt,\n config: ServerConfig,\n convexClient: ConvexHttpClient\n) {\n return async () => {\n let flattenedPrompt = prompt.flattenedPrompt;\n let promptName = prompt.name;\n\n // Always fetch fresh data from Convex (real-time mode)\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let freshPrompts = await convexClient.query(\"mcp_prompts:getMcpPrompts\" as any, {\n apiKey: config.apiKey,\n });\n\n // Apply workspace filter to fresh data if configured\n if (config.selectedWorkspaces.length > 0) {\n freshPrompts = freshPrompts.filter((p: McpPrompt) =>\n p.workspaceSlug && config.selectedWorkspaces.includes(p.workspaceSlug)\n );\n }\n\n const freshPrompt = freshPrompts.find((p: McpPrompt) => p.slug === prompt.slug && p.workspaceSlug === prompt.workspaceSlug);\n\n if (freshPrompt) {\n flattenedPrompt = freshPrompt.flattenedPrompt;\n promptName = freshPrompt.name;\n console.error(`[MCP] Fetched fresh data for: ${buildPromptName(prompt)}`);\n } else {\n console.error(\n `[MCP] WARNING: Prompt \"${prompt.slug}\" not found in fresh fetch, using cached version`\n );\n }\n } catch (error) {\n console.error(\n `[MCP] WARNING: Failed to fetch fresh data, using cached version: ${error instanceof Error ? error.message : \"Unknown error\"}`\n );\n }\n\n if (!flattenedPrompt) {\n throw new Error(\n `Prompt \"${promptName}\" has no flattened content. Please re-save the prompt in ContextFS to regenerate it.`\n );\n }\n\n console.error(`[MCP] Executed prompt: ${buildPromptName(prompt)}`);\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: flattenedPrompt,\n },\n },\n ],\n };\n };\n}\n"],"mappings":"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","names":["result"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@promptprojectmanager/mcp-server",
3
- "version": "2.8.5",
3
+ "version": "2.8.7",
4
4
  "description": "MCP server that exposes Prompt Project Manager workspace prompts as slash commands",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",