@promptprojectmanager/mcp-server 3.3.4 → 3.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +125 -168
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -23,14 +23,6 @@ import {
|
|
|
23
23
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
24
24
|
|
|
25
25
|
// src/prompt-builder.ts
|
|
26
|
-
function sanitizeForMcp(str) {
|
|
27
|
-
return str.trim().toLowerCase().replace(/[^a-z0-9_-]/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "").trim();
|
|
28
|
-
}
|
|
29
|
-
function buildPromptNameFromMetadata(metadata) {
|
|
30
|
-
const workspace = sanitizeForMcp(metadata.workspaceSlug || "default") || "default";
|
|
31
|
-
const promptSlug = sanitizeForMcp(metadata.slug || "unknown") || "unknown";
|
|
32
|
-
return `${workspace}:${promptSlug}`.trim();
|
|
33
|
-
}
|
|
34
26
|
var AmbiguousPromptError = class extends Error {
|
|
35
27
|
suggestions;
|
|
36
28
|
constructor(promptSlug, suggestions) {
|
|
@@ -39,26 +31,17 @@ var AmbiguousPromptError = class extends Error {
|
|
|
39
31
|
this.suggestions = suggestions;
|
|
40
32
|
}
|
|
41
33
|
};
|
|
42
|
-
async function
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
result = await convexClient.query("mcp_prompts:getMcpPromptBySlug", {
|
|
46
|
-
apiKey: config.apiKey,
|
|
47
|
-
workspaceSlug,
|
|
48
|
-
promptSlug
|
|
49
|
-
});
|
|
50
|
-
} else if (config.workspaceToken) {
|
|
51
|
-
result = await convexClient.query("mcp_prompts:getMcpPromptBySlugWithToken", {
|
|
52
|
-
workspaceToken: config.workspaceToken,
|
|
53
|
-
workspaceSlug,
|
|
54
|
-
promptSlug
|
|
55
|
-
});
|
|
56
|
-
} else {
|
|
57
|
-
throw new Error("No authentication configured");
|
|
34
|
+
async function fetchAndExecuteAccountScopedPrompt(promptSlug, config, convexClient) {
|
|
35
|
+
if (!config.apiKey) {
|
|
36
|
+
throw new Error("Account-scoped prompts require API key authentication");
|
|
58
37
|
}
|
|
38
|
+
const result = await convexClient.query("mcp_prompts:getAccountScopedPromptBySlug", {
|
|
39
|
+
apiKey: config.apiKey,
|
|
40
|
+
promptSlug
|
|
41
|
+
});
|
|
59
42
|
if (!result) {
|
|
60
43
|
throw new Error(
|
|
61
|
-
`Prompt "${promptSlug}" not found
|
|
44
|
+
`Prompt "${promptSlug}" not found. Use system:prompts to list available prompts.`
|
|
62
45
|
);
|
|
63
46
|
}
|
|
64
47
|
if (result.matchType === "ambiguous") {
|
|
@@ -70,7 +53,7 @@ async function fetchAndExecutePrompt(workspaceSlug, promptSlug, config, convexCl
|
|
|
70
53
|
);
|
|
71
54
|
}
|
|
72
55
|
const matchNote = result.matchType === "exact" ? "exact match" : result.matchType === "prefix" ? `prefix match \u2192 ${result.slug}` : `contains match \u2192 ${result.slug}`;
|
|
73
|
-
console.error(`[MCP] Fetched prompt: ${
|
|
56
|
+
console.error(`[MCP] Fetched account-scoped prompt: ${promptSlug} (${matchNote})`);
|
|
74
57
|
return {
|
|
75
58
|
messages: [
|
|
76
59
|
{
|
|
@@ -121,81 +104,83 @@ async function startServer(config, convexClient) {
|
|
|
121
104
|
} else {
|
|
122
105
|
throw new Error("No authentication provided. Set PPM_API_KEY or PPM_WORKSPACE_TOKEN.");
|
|
123
106
|
}
|
|
124
|
-
let
|
|
125
|
-
|
|
107
|
+
let accountScopedPrompts = [];
|
|
108
|
+
let workspacesForTickets = [];
|
|
109
|
+
console.error("[MCP] Fetching prompt and workspace metadata...");
|
|
126
110
|
if (config.apiKey) {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
if (promptMetadata.length === 0) {
|
|
133
|
-
if (config.apiKey && config.selectedWorkspaces.length > 0) {
|
|
134
|
-
console.error(
|
|
135
|
-
`[MCP] WARNING: No prompts found in workspaces: ${config.selectedWorkspaces.join(", ")}. Check that these workspace slugs exist and contain prompts.`
|
|
136
|
-
);
|
|
111
|
+
accountScopedPrompts = await fetchAccountScopedPromptMetadata(convexClient, config.apiKey);
|
|
112
|
+
const allWorkspaces = await fetchMcpWorkspaces(convexClient, config.apiKey);
|
|
113
|
+
if (config.selectedWorkspaces.length > 0) {
|
|
114
|
+
const selectedSet = new Set(config.selectedWorkspaces);
|
|
115
|
+
workspacesForTickets = allWorkspaces.filter((w) => selectedSet.has(w.slug));
|
|
137
116
|
} else {
|
|
138
|
-
|
|
139
|
-
"[MCP] WARNING: No prompts found. Create some prompts in Prompt Project Manager to expose them via MCP."
|
|
140
|
-
);
|
|
117
|
+
workspacesForTickets = allWorkspaces;
|
|
141
118
|
}
|
|
119
|
+
} else if (config.workspaceToken && tokenWorkspaceSlug) {
|
|
120
|
+
console.error("[MCP] Workspace token mode: prompts not available (tickets only)");
|
|
121
|
+
workspacesForTickets = [{ slug: tokenWorkspaceSlug, name: tokenWorkspaceSlug }];
|
|
122
|
+
}
|
|
123
|
+
console.error(`[MCP] Found ${accountScopedPrompts.length} account-scoped prompts (global tools)`);
|
|
124
|
+
console.error(`[MCP] Found ${workspacesForTickets.length} workspace(s) for ticket tools`);
|
|
125
|
+
if (accountScopedPrompts.length === 0 && config.apiKey) {
|
|
126
|
+
console.error(
|
|
127
|
+
"[MCP] WARNING: No account-scoped prompts found. Create prompts in the 'prompts' section to expose them via MCP."
|
|
128
|
+
);
|
|
142
129
|
}
|
|
143
|
-
const validPrompts = promptMetadata;
|
|
144
130
|
if (tokenWorkspaceSlug) {
|
|
145
|
-
console.error(`[MCP]
|
|
131
|
+
console.error(`[MCP] Ticket workspace scope: ${tokenWorkspaceSlug} (token-scoped)`);
|
|
146
132
|
} else if (config.selectedWorkspaces.length > 0) {
|
|
147
|
-
console.error(`[MCP]
|
|
133
|
+
console.error(`[MCP] Ticket workspace filter: ${config.selectedWorkspaces.join(", ")}`);
|
|
148
134
|
} else {
|
|
149
|
-
console.error(`[MCP]
|
|
135
|
+
console.error(`[MCP] Ticket workspaces: ALL (${workspacesForTickets.map((w) => w.slug).join(", ")})`);
|
|
150
136
|
}
|
|
151
|
-
|
|
152
|
-
const workspaceSlugs = tokenWorkspaceSlug ? /* @__PURE__ */ new Set([tokenWorkspaceSlug]) : new Set(validPrompts.map((p) => p.workspaceSlug).filter((s) => !!s));
|
|
137
|
+
const workspaceSlugs = new Set(workspacesForTickets.map((w) => w.slug));
|
|
153
138
|
const dynamicTicketTools = [];
|
|
154
139
|
for (const workspaceSlug of workspaceSlugs) {
|
|
155
140
|
dynamicTicketTools.push({
|
|
156
|
-
name: `${workspaceSlug}
|
|
141
|
+
name: `${workspaceSlug}_tickets_work`,
|
|
157
142
|
description: `Get work from the "${workspaceSlug}" workspace. With no args: gets next ticket from open queue. With ticket slug/number: opens or resumes that specific ticket.`,
|
|
158
143
|
slashDescription: `Get next ticket from queue, or work on specific ticket by slug/number`,
|
|
159
144
|
workspaceSlug,
|
|
160
145
|
type: "work"
|
|
161
146
|
});
|
|
162
147
|
dynamicTicketTools.push({
|
|
163
|
-
name: `${workspaceSlug}
|
|
148
|
+
name: `${workspaceSlug}_tickets_close`,
|
|
164
149
|
description: `Mark a working ticket as completed in the "${workspaceSlug}" workspace`,
|
|
165
150
|
slashDescription: `Mark a working ticket as completed`,
|
|
166
151
|
workspaceSlug,
|
|
167
152
|
type: "close"
|
|
168
153
|
});
|
|
169
154
|
dynamicTicketTools.push({
|
|
170
|
-
name: `${workspaceSlug}
|
|
155
|
+
name: `${workspaceSlug}_tickets_create`,
|
|
171
156
|
description: `Create a new ticket in the "${workspaceSlug}" workspace queue`,
|
|
172
157
|
slashDescription: `Create a new ticket in the backlog queue`,
|
|
173
158
|
workspaceSlug,
|
|
174
159
|
type: "create"
|
|
175
160
|
});
|
|
176
161
|
dynamicTicketTools.push({
|
|
177
|
-
name: `${workspaceSlug}
|
|
162
|
+
name: `${workspaceSlug}_tickets_search`,
|
|
178
163
|
description: `Search for tickets by content in the "${workspaceSlug}" workspace`,
|
|
179
164
|
slashDescription: `Search for tickets by content`,
|
|
180
165
|
workspaceSlug,
|
|
181
166
|
type: "search"
|
|
182
167
|
});
|
|
183
168
|
dynamicTicketTools.push({
|
|
184
|
-
name: `${workspaceSlug}
|
|
169
|
+
name: `${workspaceSlug}_tickets_get`,
|
|
185
170
|
description: `Get a specific ticket by number or slug from "${workspaceSlug}" (read-only)`,
|
|
186
171
|
slashDescription: `Get a specific ticket by number or slug (read-only)`,
|
|
187
172
|
workspaceSlug,
|
|
188
173
|
type: "get"
|
|
189
174
|
});
|
|
190
175
|
dynamicTicketTools.push({
|
|
191
|
-
name: `${workspaceSlug}
|
|
176
|
+
name: `${workspaceSlug}_tickets_list`,
|
|
192
177
|
description: `List active tickets in the "${workspaceSlug}" workspace (backlog + open + working)`,
|
|
193
178
|
slashDescription: `List active tickets (backlog + open + working)`,
|
|
194
179
|
workspaceSlug,
|
|
195
180
|
type: "list"
|
|
196
181
|
});
|
|
197
182
|
dynamicTicketTools.push({
|
|
198
|
-
name: `${workspaceSlug}
|
|
183
|
+
name: `${workspaceSlug}_tickets_update`,
|
|
199
184
|
description: `Update a ticket in the "${workspaceSlug}" workspace by appending content with timestamp`,
|
|
200
185
|
slashDescription: `Update a ticket by appending content with timestamp`,
|
|
201
186
|
workspaceSlug,
|
|
@@ -203,49 +188,40 @@ async function startServer(config, convexClient) {
|
|
|
203
188
|
});
|
|
204
189
|
}
|
|
205
190
|
console.error(`[MCP] Registering ${dynamicTicketTools.length} ticket tools for ${workspaceSlugs.size} workspace(s)...`);
|
|
206
|
-
const
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
191
|
+
const globalSystemTools = [
|
|
192
|
+
{
|
|
193
|
+
name: "system_run_prompt",
|
|
194
|
+
description: "Execute a prompt by slug. Use system_prompts to list available prompts.",
|
|
195
|
+
slashDescription: "Execute a prompt by slug. Use system_prompts to list available prompts."
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
name: "system_prompts",
|
|
199
|
+
description: "List all available prompts",
|
|
200
|
+
slashDescription: "List all available prompts"
|
|
201
|
+
}
|
|
202
|
+
];
|
|
203
|
+
console.error(`[MCP] Registering ${globalSystemTools.length} global system tools...`);
|
|
216
204
|
const dynamicPromptTools = [];
|
|
217
|
-
for (const prompt of
|
|
218
|
-
if (!prompt.workspaceSlug) continue;
|
|
205
|
+
for (const prompt of accountScopedPrompts) {
|
|
219
206
|
dynamicPromptTools.push({
|
|
220
|
-
name:
|
|
207
|
+
name: prompt.slug,
|
|
208
|
+
// Global tool name, no workspace prefix
|
|
221
209
|
description: prompt.description || `Execute the "${prompt.slug}" prompt`,
|
|
222
|
-
workspaceSlug: prompt.workspaceSlug,
|
|
223
210
|
promptSlug: prompt.slug
|
|
224
211
|
});
|
|
225
212
|
}
|
|
226
|
-
console.error(`[MCP] Registering ${dynamicPromptTools.length} per-prompt tools...`);
|
|
227
|
-
const dynamicSystemPromptsTools = [];
|
|
228
|
-
for (const workspaceSlug of workspaceSlugs) {
|
|
229
|
-
dynamicSystemPromptsTools.push({
|
|
230
|
-
name: `${workspaceSlug}:system:prompts`,
|
|
231
|
-
description: `List all available prompts from the "${workspaceSlug}" workspace`,
|
|
232
|
-
slashDescription: `List all available prompts from the workspace`,
|
|
233
|
-
workspaceSlug
|
|
234
|
-
});
|
|
235
|
-
}
|
|
236
|
-
console.error(`[MCP] Registering ${dynamicSystemPromptsTools.length} system:prompts tools for ${workspaceSlugs.size} workspace(s)...`);
|
|
213
|
+
console.error(`[MCP] Registering ${dynamicPromptTools.length} per-prompt tools (global)...`);
|
|
237
214
|
const promptNames = /* @__PURE__ */ new Set();
|
|
238
215
|
const duplicates = [];
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
duplicates.push(name);
|
|
216
|
+
accountScopedPrompts.forEach((p) => {
|
|
217
|
+
if (promptNames.has(p.slug)) {
|
|
218
|
+
duplicates.push(p.slug);
|
|
243
219
|
}
|
|
244
|
-
promptNames.add(
|
|
220
|
+
promptNames.add(p.slug);
|
|
245
221
|
});
|
|
246
222
|
if (duplicates.length > 0) {
|
|
247
223
|
console.error(
|
|
248
|
-
`[MCP] WARNING: Duplicate prompt
|
|
224
|
+
`[MCP] WARNING: Duplicate prompt slugs detected: ${duplicates.join(", ")}. Only the first occurrence will be registered.`
|
|
249
225
|
);
|
|
250
226
|
}
|
|
251
227
|
const server = new Server(
|
|
@@ -265,20 +241,16 @@ async function startServer(config, convexClient) {
|
|
|
265
241
|
name: tt.name,
|
|
266
242
|
description: tt.slashDescription
|
|
267
243
|
}));
|
|
268
|
-
const systemPromptsSchemas =
|
|
269
|
-
name:
|
|
270
|
-
description:
|
|
271
|
-
}));
|
|
272
|
-
const runPromptSchemas = dynamicRunPromptTools.map((rp) => ({
|
|
273
|
-
name: rp.name,
|
|
274
|
-
description: rp.slashDescription
|
|
244
|
+
const systemPromptsSchemas = globalSystemTools.map((st) => ({
|
|
245
|
+
name: st.name,
|
|
246
|
+
description: st.slashDescription
|
|
275
247
|
}));
|
|
276
248
|
return {
|
|
277
249
|
prompts: [
|
|
278
250
|
...systemPromptsSchemas,
|
|
279
|
-
//
|
|
280
|
-
...ticketPromptSchemas
|
|
281
|
-
|
|
251
|
+
// system_prompts, system_run_prompt (global)
|
|
252
|
+
...ticketPromptSchemas
|
|
253
|
+
// workspace_tickets_* (workspace-scoped)
|
|
282
254
|
]
|
|
283
255
|
};
|
|
284
256
|
});
|
|
@@ -300,7 +272,7 @@ async function startServer(config, convexClient) {
|
|
|
300
272
|
};
|
|
301
273
|
}
|
|
302
274
|
const ticketTool = dynamicTicketTools.find((tt) => tt.name === promptName);
|
|
303
|
-
const ticketWorkMatch = promptName.match(/^(.+)
|
|
275
|
+
const ticketWorkMatch = promptName.match(/^(.+)_tickets_work\s+(.+)$/);
|
|
304
276
|
if (ticketTool || ticketWorkMatch) {
|
|
305
277
|
let promptContent;
|
|
306
278
|
let description;
|
|
@@ -310,7 +282,7 @@ async function startServer(config, convexClient) {
|
|
|
310
282
|
description = `Work on ticket "${ticketArg}" from "${workspaceSlug}"`;
|
|
311
283
|
promptContent = `Get work on ticket "${ticketArg}" from the "${workspaceSlug}" workspace.
|
|
312
284
|
|
|
313
|
-
Call the \`${workspaceSlug}
|
|
285
|
+
Call the \`${workspaceSlug}_tickets_work\` tool with ticketSlug: "${ticketArg}".
|
|
314
286
|
|
|
315
287
|
This will open the ticket if it's in the open queue, or resume it if already working.`;
|
|
316
288
|
} else if (ticketTool.type === "work") {
|
|
@@ -319,7 +291,7 @@ This will open the ticket if it's in the open queue, or resume it if already wor
|
|
|
319
291
|
|
|
320
292
|
Call the \`${ticketTool.name}\` tool to get the next ticket from the open queue.
|
|
321
293
|
|
|
322
|
-
You can also specify a ticket: /ppm:${ticketTool.workspaceSlug}
|
|
294
|
+
You can also specify a ticket: /ppm:${ticketTool.workspaceSlug}_tickets_work <number-or-slug>
|
|
323
295
|
|
|
324
296
|
This unified command handles both opening new tickets and resuming in-progress work.`;
|
|
325
297
|
} else if (ticketTool.type === "create") {
|
|
@@ -371,47 +343,44 @@ Call the \`${ticketTool.name}\` tool with the ticket number or slug.`;
|
|
|
371
343
|
]
|
|
372
344
|
};
|
|
373
345
|
}
|
|
374
|
-
|
|
375
|
-
if (systemPromptsTool) {
|
|
346
|
+
if (promptName === "system_prompts") {
|
|
376
347
|
return {
|
|
377
|
-
description:
|
|
348
|
+
description: "List all available prompts",
|
|
378
349
|
messages: [
|
|
379
350
|
{
|
|
380
351
|
role: "user",
|
|
381
352
|
content: {
|
|
382
353
|
type: "text",
|
|
383
|
-
text: `List all available prompts
|
|
354
|
+
text: `List all available prompts.
|
|
384
355
|
|
|
385
|
-
Call the
|
|
356
|
+
Call the \`system_prompts\` tool with optional \`search\` parameter to filter results.`
|
|
386
357
|
}
|
|
387
358
|
}
|
|
388
359
|
]
|
|
389
360
|
};
|
|
390
361
|
}
|
|
391
|
-
const
|
|
392
|
-
|
|
393
|
-
if (runPromptTool || runPromptMatch) {
|
|
362
|
+
const runPromptMatch = promptName.match(/^system_run_prompt\s+(.+)$/);
|
|
363
|
+
if (promptName === "system_run_prompt" || runPromptMatch) {
|
|
394
364
|
let promptContent;
|
|
395
365
|
let description;
|
|
396
366
|
if (runPromptMatch) {
|
|
397
|
-
const
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
promptContent = `Execute the "${promptSlug}" prompt from the "${workspaceSlug}" workspace.
|
|
367
|
+
const promptSlug = runPromptMatch[1].trim();
|
|
368
|
+
description = `Execute prompt "${promptSlug}"`;
|
|
369
|
+
promptContent = `Execute the "${promptSlug}" prompt.
|
|
401
370
|
|
|
402
|
-
Call the
|
|
371
|
+
Call the \`system_run_prompt\` tool with slug: "${promptSlug}".`;
|
|
403
372
|
} else {
|
|
404
|
-
description =
|
|
405
|
-
promptContent = `Execute a prompt
|
|
373
|
+
description = "Execute a prompt by slug";
|
|
374
|
+
promptContent = `Execute a prompt by slug.
|
|
406
375
|
|
|
407
376
|
## Usage
|
|
408
|
-
Call the
|
|
377
|
+
Call the \`system_run_prompt\` tool with the prompt slug.
|
|
409
378
|
|
|
410
379
|
## Available Prompts
|
|
411
|
-
Use
|
|
380
|
+
Use \`system_prompts\` to list all available prompts.
|
|
412
381
|
|
|
413
382
|
## Example
|
|
414
|
-
/ppm
|
|
383
|
+
/ppm:system_run_prompt code-review
|
|
415
384
|
|
|
416
385
|
This will execute the "code-review" prompt.`;
|
|
417
386
|
}
|
|
@@ -428,7 +397,7 @@ This will execute the "code-review" prompt.`;
|
|
|
428
397
|
]
|
|
429
398
|
};
|
|
430
399
|
}
|
|
431
|
-
throw new Error(`Unknown prompt: ${promptName}. Use
|
|
400
|
+
throw new Error(`Unknown prompt: ${promptName}. Use system_run_prompt to execute prompts.`);
|
|
432
401
|
});
|
|
433
402
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
434
403
|
const tools = [
|
|
@@ -530,10 +499,10 @@ This will execute the "code-review" prompt.`;
|
|
|
530
499
|
inputSchema
|
|
531
500
|
};
|
|
532
501
|
}),
|
|
533
|
-
//
|
|
534
|
-
|
|
535
|
-
name:
|
|
536
|
-
description:
|
|
502
|
+
// Global system_run_prompt tool
|
|
503
|
+
{
|
|
504
|
+
name: "system_run_prompt",
|
|
505
|
+
description: "Execute a prompt by slug. Use system_prompts to list available prompts.",
|
|
537
506
|
inputSchema: {
|
|
538
507
|
type: "object",
|
|
539
508
|
properties: {
|
|
@@ -544,11 +513,11 @@ This will execute the "code-review" prompt.`;
|
|
|
544
513
|
},
|
|
545
514
|
required: ["slug"]
|
|
546
515
|
}
|
|
547
|
-
}
|
|
548
|
-
//
|
|
549
|
-
|
|
550
|
-
name:
|
|
551
|
-
description:
|
|
516
|
+
},
|
|
517
|
+
// Global system_prompts tool
|
|
518
|
+
{
|
|
519
|
+
name: "system_prompts",
|
|
520
|
+
description: "List all available prompts",
|
|
552
521
|
inputSchema: {
|
|
553
522
|
type: "object",
|
|
554
523
|
properties: {
|
|
@@ -558,8 +527,8 @@ This will execute the "code-review" prompt.`;
|
|
|
558
527
|
}
|
|
559
528
|
}
|
|
560
529
|
}
|
|
561
|
-
}
|
|
562
|
-
// Dynamic per-prompt tools (each prompt as its own tool)
|
|
530
|
+
},
|
|
531
|
+
// Dynamic per-prompt tools (each prompt as its own global tool)
|
|
563
532
|
...dynamicPromptTools.map((pt) => ({
|
|
564
533
|
name: pt.name,
|
|
565
534
|
description: pt.description,
|
|
@@ -574,8 +543,7 @@ This will execute the "code-review" prompt.`;
|
|
|
574
543
|
});
|
|
575
544
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
576
545
|
const toolName = request.params.name;
|
|
577
|
-
|
|
578
|
-
if (runPromptTool) {
|
|
546
|
+
if (toolName === "system_run_prompt") {
|
|
579
547
|
const promptSlug = request.params.arguments?.slug;
|
|
580
548
|
if (!promptSlug) {
|
|
581
549
|
return {
|
|
@@ -584,15 +552,14 @@ This will execute the "code-review" prompt.`;
|
|
|
584
552
|
type: "text",
|
|
585
553
|
text: `Error: Missing 'slug' parameter. Provide prompt slug (e.g., 'code-review', 'plan').
|
|
586
554
|
|
|
587
|
-
Use
|
|
555
|
+
Use \`system_prompts\` to list available prompts.`
|
|
588
556
|
}
|
|
589
557
|
],
|
|
590
558
|
isError: true
|
|
591
559
|
};
|
|
592
560
|
}
|
|
593
561
|
try {
|
|
594
|
-
const result = await
|
|
595
|
-
runPromptTool.workspaceSlug,
|
|
562
|
+
const result = await fetchAndExecuteAccountScopedPrompt(
|
|
596
563
|
promptSlug,
|
|
597
564
|
config,
|
|
598
565
|
convexClient
|
|
@@ -618,7 +585,7 @@ Use \`${runPromptTool.workspaceSlug}:system:prompts\` to list available prompts.
|
|
|
618
585
|
|
|
619
586
|
${suggestionsList}
|
|
620
587
|
|
|
621
|
-
Example:
|
|
588
|
+
Example: \`system_run_prompt ${error.suggestions[0]}\``
|
|
622
589
|
}
|
|
623
590
|
],
|
|
624
591
|
isError: true
|
|
@@ -637,18 +604,12 @@ Example: \`${runPromptTool.workspaceSlug}:system:run_prompt ${error.suggestions[
|
|
|
637
604
|
};
|
|
638
605
|
}
|
|
639
606
|
}
|
|
640
|
-
|
|
641
|
-
if (systemPromptsTool) {
|
|
607
|
+
if (toolName === "system_prompts") {
|
|
642
608
|
const searchTerm = request.params.arguments?.search;
|
|
643
|
-
|
|
644
|
-
const workspacePrompts = validPrompts.filter((p) => p.workspaceSlug === workspaceSlug);
|
|
645
|
-
const uniquePrompts = Array.from(
|
|
646
|
-
new Map(workspacePrompts.map((p) => [buildPromptNameFromMetadata(p), p])).values()
|
|
647
|
-
);
|
|
648
|
-
let filteredPrompts = uniquePrompts;
|
|
609
|
+
let filteredPrompts = [...accountScopedPrompts];
|
|
649
610
|
if (searchTerm) {
|
|
650
611
|
const lowerSearch = searchTerm.toLowerCase();
|
|
651
|
-
filteredPrompts =
|
|
612
|
+
filteredPrompts = filteredPrompts.filter(
|
|
652
613
|
(p) => p.slug.toLowerCase().includes(lowerSearch) || p.description?.toLowerCase().includes(lowerSearch)
|
|
653
614
|
);
|
|
654
615
|
}
|
|
@@ -658,7 +619,7 @@ Example: \`${runPromptTool.workspaceSlug}:system:run_prompt ${error.suggestions[
|
|
|
658
619
|
return `\u2022 ${p.slug}
|
|
659
620
|
Description: ${desc}`;
|
|
660
621
|
}).join("\n\n");
|
|
661
|
-
const summary = searchTerm ? `Found ${filteredPrompts.length} prompt(s) matching "${searchTerm}"
|
|
622
|
+
const summary = searchTerm ? `Found ${filteredPrompts.length} prompt(s) matching "${searchTerm}":` : `Available prompts (${filteredPrompts.length} total):`;
|
|
662
623
|
return {
|
|
663
624
|
content: [
|
|
664
625
|
{
|
|
@@ -726,7 +687,7 @@ ${statusNote}`
|
|
|
726
687
|
};
|
|
727
688
|
} catch (error) {
|
|
728
689
|
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
729
|
-
console.error(`[MCP]
|
|
690
|
+
console.error(`[MCP] tickets_work error:`, error);
|
|
730
691
|
return {
|
|
731
692
|
content: [
|
|
732
693
|
{
|
|
@@ -768,13 +729,13 @@ ${statusNote}`
|
|
|
768
729
|
Position: #${result.position} of ${result.totalBacklog} backlog tickets
|
|
769
730
|
Preview: ${result.preview}
|
|
770
731
|
|
|
771
|
-
_Ticket created in backlog. Use \`
|
|
732
|
+
_Ticket created in backlog. Use \`tickets_work ${result.slug}\` to move it to working status._`
|
|
772
733
|
}
|
|
773
734
|
]
|
|
774
735
|
};
|
|
775
736
|
} catch (error) {
|
|
776
737
|
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
777
|
-
console.error(`[MCP]
|
|
738
|
+
console.error(`[MCP] tickets_create error:`, error);
|
|
778
739
|
return {
|
|
779
740
|
content: [
|
|
780
741
|
{
|
|
@@ -821,7 +782,7 @@ _Reminder: Ensure all embedded \`[RUN_PROMPT ...]\` directives were executed bef
|
|
|
821
782
|
};
|
|
822
783
|
} catch (error) {
|
|
823
784
|
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
824
|
-
console.error(`[MCP]
|
|
785
|
+
console.error(`[MCP] tickets_close error:`, error);
|
|
825
786
|
return {
|
|
826
787
|
content: [
|
|
827
788
|
{
|
|
@@ -882,7 +843,7 @@ ${formattedList}`
|
|
|
882
843
|
};
|
|
883
844
|
} catch (error) {
|
|
884
845
|
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
885
|
-
console.error(`[MCP]
|
|
846
|
+
console.error(`[MCP] tickets_search error:`, error);
|
|
886
847
|
return {
|
|
887
848
|
content: [
|
|
888
849
|
{
|
|
@@ -952,13 +913,13 @@ Assigned to: ${result.assignedTo}` : "\nUnassigned";
|
|
|
952
913
|
${result.content}
|
|
953
914
|
|
|
954
915
|
---
|
|
955
|
-
_Read-only inspection. Use
|
|
916
|
+
_Read-only inspection. Use tickets_work to start working on this ticket._`
|
|
956
917
|
}
|
|
957
918
|
]
|
|
958
919
|
};
|
|
959
920
|
} catch (error) {
|
|
960
921
|
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
961
|
-
console.error(`[MCP]
|
|
922
|
+
console.error(`[MCP] tickets_get error:`, error);
|
|
962
923
|
return {
|
|
963
924
|
content: [
|
|
964
925
|
{
|
|
@@ -1023,7 +984,7 @@ ${sections.join("\n\n")}`
|
|
|
1023
984
|
};
|
|
1024
985
|
} catch (error) {
|
|
1025
986
|
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
1026
|
-
console.error(`[MCP]
|
|
987
|
+
console.error(`[MCP] tickets_list error:`, error);
|
|
1027
988
|
return {
|
|
1028
989
|
content: [
|
|
1029
990
|
{
|
|
@@ -1081,7 +1042,7 @@ _Ticket content has been appended with your update._`
|
|
|
1081
1042
|
};
|
|
1082
1043
|
} catch (error) {
|
|
1083
1044
|
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
1084
|
-
console.error(`[MCP]
|
|
1045
|
+
console.error(`[MCP] tickets_update error:`, error);
|
|
1085
1046
|
return {
|
|
1086
1047
|
content: [
|
|
1087
1048
|
{
|
|
@@ -1097,8 +1058,7 @@ _Ticket content has been appended with your update._`
|
|
|
1097
1058
|
const promptTool = dynamicPromptTools.find((pt) => pt.name === toolName);
|
|
1098
1059
|
if (promptTool) {
|
|
1099
1060
|
try {
|
|
1100
|
-
const result = await
|
|
1101
|
-
promptTool.workspaceSlug,
|
|
1061
|
+
const result = await fetchAndExecuteAccountScopedPrompt(
|
|
1102
1062
|
promptTool.promptSlug,
|
|
1103
1063
|
config,
|
|
1104
1064
|
convexClient
|
|
@@ -1116,7 +1076,7 @@ _Ticket content has been appended with your update._`
|
|
|
1116
1076
|
};
|
|
1117
1077
|
}
|
|
1118
1078
|
}
|
|
1119
|
-
throw new Error(`Unknown tool: ${toolName}. Use
|
|
1079
|
+
throw new Error(`Unknown tool: ${toolName}. Use system_run_prompt to execute prompts by name, or check available tools.`);
|
|
1120
1080
|
});
|
|
1121
1081
|
const transport = new StdioServerTransport();
|
|
1122
1082
|
await server.connect(transport);
|
|
@@ -1167,30 +1127,27 @@ async function validateWorkspaceToken(client, token) {
|
|
|
1167
1127
|
};
|
|
1168
1128
|
}
|
|
1169
1129
|
}
|
|
1170
|
-
async function
|
|
1130
|
+
async function fetchAccountScopedPromptMetadata(client, apiKey) {
|
|
1171
1131
|
try {
|
|
1172
|
-
const metadata = await client.query("mcp_prompts:
|
|
1173
|
-
apiKey
|
|
1174
|
-
workspaces: workspaces.length > 0 ? workspaces : void 0
|
|
1175
|
-
// Only pass if specified
|
|
1132
|
+
const metadata = await client.query("mcp_prompts:getAccountScopedPromptMetadata", {
|
|
1133
|
+
apiKey
|
|
1176
1134
|
});
|
|
1177
1135
|
return metadata;
|
|
1178
1136
|
} catch (error) {
|
|
1179
1137
|
throw new Error(
|
|
1180
|
-
`Failed to fetch prompt metadata: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
1138
|
+
`Failed to fetch account-scoped prompt metadata: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
1181
1139
|
);
|
|
1182
1140
|
}
|
|
1183
1141
|
}
|
|
1184
|
-
async function
|
|
1142
|
+
async function fetchMcpWorkspaces(client, apiKey) {
|
|
1185
1143
|
try {
|
|
1186
|
-
const
|
|
1187
|
-
|
|
1188
|
-
workspaceSlug
|
|
1144
|
+
const workspaces = await client.query("mcp_prompts:getMcpWorkspaces", {
|
|
1145
|
+
apiKey
|
|
1189
1146
|
});
|
|
1190
|
-
return
|
|
1147
|
+
return workspaces;
|
|
1191
1148
|
} catch (error) {
|
|
1192
1149
|
throw new Error(
|
|
1193
|
-
`Failed to fetch
|
|
1150
|
+
`Failed to fetch workspaces: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
1194
1151
|
);
|
|
1195
1152
|
}
|
|
1196
1153
|
}
|
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 authentication (API key or workspace token)\n // API key takes precedence if both are provided\n const apiKey = process.env.PPM_API_KEY || process.env.THEPROMPTEDITOR_API_KEY;\n const workspaceToken = process.env.PPM_WORKSPACE_TOKEN;\n\n // PPM_AGENT: Optional agent identity selection (only with API key auth)\n // When set, uses the named workspace token as identity while API key provides access\n const ppmAgent = process.env.PPM_AGENT;\n\n if (!apiKey && !workspaceToken) {\n console.error(\n \"[MCP] ERROR: Missing authentication. Provide either PPM_API_KEY or PPM_WORKSPACE_TOKEN\"\n );\n console.error(\n \"[MCP] Options:\"\n );\n console.error(\"[MCP] export PPM_API_KEY=your_api_key_here # Full access to all workspaces\");\n console.error(\"[MCP] export PPM_WORKSPACE_TOKEN=wst_... # Scoped access to one workspace (tickets only)\");\n process.exit(1);\n }\n\n // Log which auth mode is being used\n if (apiKey) {\n console.error(\"[MCP] Auth mode: API Key (full access)\");\n if (workspaceToken) {\n console.error(\"[MCP] Note: PPM_WORKSPACE_TOKEN ignored when PPM_API_KEY is set\");\n }\n if (ppmAgent) {\n console.error(`[MCP] Agent identity: ${ppmAgent}`);\n }\n } else {\n console.error(\"[MCP] Auth mode: Workspace Token (scoped access, tickets only)\");\n if (ppmAgent) {\n console.error(\"[MCP] Note: PPM_AGENT ignored when using PPM_WORKSPACE_TOKEN directly\");\n }\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: apiKey || undefined, // Use undefined if not set (not empty string)\n workspaceToken: apiKey ? undefined : workspaceToken, // Only use token if no API key\n isDev,\n convexUrl,\n selectedWorkspaces, // Workspace slugs to filter (empty = all workspaces)\n agentName: apiKey && ppmAgent ? ppmAgent : undefined, // Only use agent name with API key auth\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 { McpPromptMetadata, ServerConfig, WorkspaceTokenValidation } from \"./types.js\";\n\n/**\n * Close ticket result (Ticket 151)\n */\ninterface CloseTicketResult {\n slug: string;\n status: string;\n closedAt: number;\n}\n\n/**\n * Update ticket result (Ticket 293)\n */\ninterface UpdateTicketResult {\n slug: string;\n ticketNumber?: number;\n status: string;\n updatedAt: number;\n}\n\nimport { buildPromptNameFromMetadata, fetchAndExecutePrompt, AmbiguousPromptError } from \"./prompt-builder.js\";\n// Note: McpCommandMetadata removed - commands resource eliminated\n\n/**\n * Build authentication arguments for Convex mutations/queries.\n * Passes either apiKey OR workspaceToken (never both) based on config.\n *\n * When using API key with agentName, passes agentName for identity resolution.\n * The backend will look up the workspace token by name to use as the actor identity.\n */\nfunction buildAuthArgs(config: ServerConfig): { apiKey?: string; workspaceToken?: string; agentName?: string } {\n if (config.apiKey) {\n return {\n apiKey: config.apiKey,\n agentName: config.agentName, // Pass agent name for identity resolution\n };\n }\n if (config.workspaceToken) {\n return { workspaceToken: config.workspaceToken };\n }\n return {};\n}\n\n/**\n * Built-in system tools (no global tools - all are workspace-scoped now)\n */\nconst SYSTEM_TOOLS: {\n name: string;\n description: string;\n inputSchema: object;\n promptContent: string;\n isSlashCommand: boolean; // true = appears as /ppm:name, false = tool only\n}[] = [\n // All tools are now workspace-scoped (see dynamic*Tools arrays below)\n];\n\n/**\n * Initialize and start the MCP server\n */\nexport async function startServer(\n config: ServerConfig,\n convexClient: ConvexHttpClient\n): Promise<void> {\n // Track auth mode and workspace scope\n let tokenWorkspaceSlug: string | undefined;\n\n // Validate authentication - API key or workspace token\n if (config.apiKey) {\n // API Key mode - full access\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 } else if (config.workspaceToken) {\n // Workspace Token mode - scoped access\n console.error(\"[MCP] Validating workspace token...\");\n const validation = await validateWorkspaceToken(convexClient, config.workspaceToken);\n if (!validation.valid) {\n throw new Error(`Invalid workspace token: ${validation.error}`);\n }\n tokenWorkspaceSlug = validation.workspaceSlug;\n console.error(`[MCP] Workspace token validated: \"${validation.tokenName}\" for workspace \"${tokenWorkspaceSlug}\"`);\n // Note: In token mode, prompts are NOT accessible - only tickets\n } else {\n throw new Error(\"No authentication provided. Set PPM_API_KEY or PPM_WORKSPACE_TOKEN.\");\n }\n\n // Fetch prompt metadata for either auth mode\n // API key: fetch from all/selected workspaces\n // Workspace token: fetch from token's scoped workspace only\n let promptMetadata: McpPromptMetadata[] = [];\n console.error(\"[MCP] Fetching prompt metadata (lightweight startup)...\");\n\n if (config.apiKey) {\n // API key mode - fetch prompts from all/selected workspaces\n promptMetadata = await fetchMcpPromptMetadata(convexClient, config.apiKey, config.selectedWorkspaces);\n } else if (config.workspaceToken && tokenWorkspaceSlug) {\n // Workspace token mode - fetch prompts from token's workspace only\n promptMetadata = await fetchMcpPromptMetadataByToken(convexClient, config.workspaceToken, tokenWorkspaceSlug);\n }\n\n console.error(`[MCP] Found ${promptMetadata.length} prompts (metadata only)`);\n\n if (promptMetadata.length === 0) {\n // Warn about missing prompts\n if (config.apiKey && 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 // Metadata-only fetch means we don't validate flattenedPrompt at startup\n // Validation happens at invocation time when we fetch the single prompt\n const validPrompts = promptMetadata;\n\n // Log workspace filtering info (Ticket 144)\n if (tokenWorkspaceSlug) {\n console.error(`[MCP] Workspace scope: ${tokenWorkspaceSlug} (token-scoped)`);\n } else 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, 155)\n // Streamlined command set: work, close, create, search, get, list\n // Note: 'open' command deprecated - unified into 'work'\n // Note: 'message' command removed - ticket messages system deprecated\n // In token mode, only the token's workspace is exposed\n const workspaceSlugs = tokenWorkspaceSlug\n ? new Set([tokenWorkspaceSlug])\n : new Set(validPrompts.map((p) => p.workspaceSlug).filter((s): s is string => !!s));\n const dynamicTicketTools: { name: string; description: string; slashDescription: string; workspaceSlug: string; type: 'work' | 'close' | 'create' | 'search' | 'get' | 'list' | 'update' }[] = [];\n\n for (const workspaceSlug of workspaceSlugs) {\n // Unified work command (replaces both tickets:open and tickets:work)\n dynamicTicketTools.push({\n name: `${workspaceSlug}:tickets:work`,\n description: `Get work from the \"${workspaceSlug}\" workspace. With no args: gets next ticket from open queue. With ticket slug/number: opens or resumes that specific ticket.`,\n slashDescription: `Get next ticket from queue, or work on specific ticket by slug/number`,\n 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 slashDescription: `Mark a working ticket as completed`,\n workspaceSlug,\n type: 'close',\n });\n dynamicTicketTools.push({\n name: `${workspaceSlug}:tickets:create`,\n description: `Create a new ticket in the \"${workspaceSlug}\" workspace queue`,\n slashDescription: `Create a new ticket in the backlog queue`,\n workspaceSlug,\n type: 'create',\n });\n dynamicTicketTools.push({\n name: `${workspaceSlug}:tickets:search`,\n description: `Search for tickets by content in the \"${workspaceSlug}\" workspace`,\n slashDescription: `Search for tickets by content`,\n workspaceSlug,\n type: 'search',\n });\n dynamicTicketTools.push({\n name: `${workspaceSlug}:tickets:get`,\n description: `Get a specific ticket by number or slug from \"${workspaceSlug}\" (read-only)`,\n slashDescription: `Get a specific ticket by number or slug (read-only)`,\n workspaceSlug,\n type: 'get',\n });\n dynamicTicketTools.push({\n name: `${workspaceSlug}:tickets:list`,\n description: `List active tickets in the \"${workspaceSlug}\" workspace (backlog + open + working)`,\n slashDescription: `List active tickets (backlog + open + working)`,\n workspaceSlug,\n type: 'list',\n });\n dynamicTicketTools.push({\n name: `${workspaceSlug}:tickets:update`,\n description: `Update a ticket in the \"${workspaceSlug}\" workspace by appending content with timestamp`,\n slashDescription: `Update a ticket by appending content with timestamp`,\n workspaceSlug,\n type: 'update',\n });\n }\n\n console.error(`[MCP] Registering ${dynamicTicketTools.length} ticket tools for ${workspaceSlugs.size} workspace(s)...`);\n\n // Build dynamic run_prompt tools per workspace (scoped like tickets)\n const dynamicRunPromptTools: { name: string; description: string; slashDescription: string; workspaceSlug: string }[] = [];\n\n for (const workspaceSlug of workspaceSlugs) {\n dynamicRunPromptTools.push({\n name: `${workspaceSlug}:system:run_prompt`,\n description: `Execute a prompt from the \"${workspaceSlug}\" workspace by slug. Use ${workspaceSlug}:system:prompts to list available prompts.`,\n slashDescription: `Execute a prompt by slug. Use system:prompts to list available prompts.`,\n workspaceSlug,\n });\n }\n\n console.error(`[MCP] Registering ${dynamicRunPromptTools.length} system:run_prompt tools for ${workspaceSlugs.size} workspace(s)...`);\n\n // Build dynamic per-prompt tools (each prompt as its own tool)\n // Enables direct tool invocation like `projects:code-review` instead of `system:run_prompt` with args\n const dynamicPromptTools: {\n name: string; // \"projects:code-review\"\n description: string;\n workspaceSlug: string;\n promptSlug: string;\n }[] = [];\n\n for (const prompt of validPrompts) {\n if (!prompt.workspaceSlug) continue;\n\n dynamicPromptTools.push({\n name: `${prompt.workspaceSlug}:${prompt.slug}`,\n description: prompt.description || `Execute the \"${prompt.slug}\" prompt`,\n workspaceSlug: prompt.workspaceSlug,\n promptSlug: prompt.slug,\n });\n }\n\n console.error(`[MCP] Registering ${dynamicPromptTools.length} per-prompt tools...`);\n\n // Build dynamic system:prompts tools per workspace (workspace-scoped prompt listing)\n const dynamicSystemPromptsTools: { name: string; description: string; slashDescription: string; workspaceSlug: string }[] = [];\n\n for (const workspaceSlug of workspaceSlugs) {\n dynamicSystemPromptsTools.push({\n name: `${workspaceSlug}:system:prompts`,\n description: `List all available prompts from the \"${workspaceSlug}\" workspace`,\n slashDescription: `List all available prompts from the workspace`,\n workspaceSlug,\n });\n }\n\n console.error(`[MCP] Registering ${dynamicSystemPromptsTools.length} system:prompts tools for ${workspaceSlugs.size} workspace(s)...`);\n\n // Note: Commands resource removed - users create slash commands directly from flattened prompts\n\n // Check for duplicate prompt names\n const promptNames = new Set<string>();\n const duplicates: string[] = [];\n validPrompts.forEach((p) => {\n const name = buildPromptNameFromMetadata(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: \"3.1.0\",\n },\n {\n capabilities: {\n prompts: {},\n tools: {},\n },\n }\n );\n\n // Register list_prompts handler\n // Note: Individual prompts no longer listed - use workspace:system:run_prompt instead\n // Uses slashDescription for concise slash command listings (tools use verbose description)\n server.setRequestHandler(ListPromptsRequestSchema, async () => {\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.slashDescription,\n }));\n\n // Build system:prompts schemas per workspace\n const systemPromptsSchemas = dynamicSystemPromptsTools.map((sp) => ({\n name: sp.name,\n description: sp.slashDescription,\n }));\n\n // Build run_prompt schemas per workspace (replaces individual prompt listings)\n const runPromptSchemas = dynamicRunPromptTools.map((rp) => ({\n name: rp.name,\n description: rp.slashDescription,\n }));\n\n // Note: commandSchemas removed - commands resource eliminated\n\n return {\n prompts: [\n ...systemPromptsSchemas, // {workspace}:system:prompts\n ...ticketPromptSchemas,\n ...runPromptSchemas,\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 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 = `Work on ticket \"${ticketArg}\" from \"${workspaceSlug}\"`;\n promptContent = `Get work on ticket \"${ticketArg}\" from the \"${workspaceSlug}\" workspace.\\n\\nCall the \\`${workspaceSlug}:tickets:work\\` tool with ticketSlug: \"${ticketArg}\".\\n\\nThis will open the ticket if it's in the open queue, or resume it if already working.`;\n } else if (ticketTool!.type === 'work') {\n // Unified work command (replaces open + work)\n description = ticketTool!.description;\n promptContent = `Get work from the \"${ticketTool!.workspaceSlug}\" workspace.\\n\\nCall the \\`${ticketTool!.name}\\` tool to get the next ticket from the open queue.\\n\\nYou can also specify a ticket: /ppm:${ticketTool!.workspaceSlug}:tickets:work <number-or-slug>\\n\\nThis unified command handles both opening new tickets and resuming in-progress work.`;\n } else if (ticketTool!.type === 'create') {\n // Ticket 149: tickets:create slash command\n description = ticketTool!.description;\n promptContent = `Create a new ticket in the \"${ticketTool!.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 {\n // Fallback for unknown type\n description = ticketTool!.description;\n promptContent = `Use the \\`${ticketTool!.name}\\` tool.`;\n }\n\n return {\n description,\n messages: [\n {\n role: \"user\" as const,\n content: {\n type: \"text\" as const,\n text: promptContent,\n },\n },\n ],\n };\n }\n\n // Check for workspace:system:prompts commands (workspace-scoped prompt listing)\n const systemPromptsTool = dynamicSystemPromptsTools.find((sp) => sp.name === promptName);\n if (systemPromptsTool) {\n return {\n description: systemPromptsTool.description,\n messages: [\n {\n role: \"user\" as const,\n content: {\n type: \"text\" as const,\n text: `List all available prompts from the \"${systemPromptsTool.workspaceSlug}\" workspace.\n\nCall the \\`${systemPromptsTool.name}\\` tool with optional \\`search\\` parameter to filter results.`,\n },\n },\n ],\n };\n }\n\n // Check for workspace:system:run_prompt commands (with optional prompt slug argument)\n const runPromptTool = dynamicRunPromptTools.find((rp) => rp.name === promptName);\n const runPromptMatch = promptName.match(/^(.+):system:run_prompt\\s+(.+)$/);\n\n if (runPromptTool || runPromptMatch) {\n let promptContent: string;\n let description: string;\n\n if (runPromptMatch) {\n // Handle run_prompt with specific prompt slug argument\n const workspaceSlug = runPromptMatch[1];\n const promptSlug = runPromptMatch[2].trim();\n description = `Execute prompt \"${promptSlug}\" from \"${workspaceSlug}\"`;\n promptContent = `Execute the \"${promptSlug}\" prompt from the \"${workspaceSlug}\" workspace.\\n\\nCall the \\`${workspaceSlug}:system:run_prompt\\` tool with slug: \"${promptSlug}\".`;\n } else {\n // Base run_prompt without argument - show help\n description = runPromptTool!.description;\n promptContent = `Execute a prompt from the \"${runPromptTool!.workspaceSlug}\" workspace.\n\n## Usage\nCall the \\`${runPromptTool!.name}\\` tool with the prompt slug.\n\n## Available Prompts\nUse \\`${runPromptTool!.workspaceSlug}:system:prompts\\` to list all available prompts in this workspace.\n\n## Example\n/ppm:${runPromptTool!.workspaceSlug}:system:run_prompt code-review\n\nThis will execute the \"code-review\" prompt.`;\n }\n\n return {\n description,\n messages: [\n {\n role: \"user\" as const,\n content: {\n type: \"text\" as const,\n text: promptContent,\n },\n },\n ],\n };\n }\n\n // Note: Command handling removed - commands resource eliminated\n\n // Unknown prompt\n throw new Error(`Unknown prompt: ${promptName}. Use workspace:system:run_prompt to execute prompts.`);\n });\n\n // Register list_tools handler\n // Note: Per-prompt tool registration removed in favor of workspace-scoped run_prompt tools\n // This reduces token overhead and allows dynamic prompt discovery without server restart\n server.setRequestHandler(ListToolsRequestSchema, async () => {\n // Build tools array: system tools + ticket tools + workspace:run_prompt tools\n // Individual prompts no longer registered as separate tools\n const tools = [\n // System tools with full input schemas\n ...SYSTEM_TOOLS.map((st) => ({\n name: st.name,\n description: st.description,\n inputSchema: st.inputSchema,\n })),\n // Dynamic ticket tools per workspace (Ticket 135, 149, 151, 153, unified work)\n ...dynamicTicketTools.map((tt) => {\n // Build inputSchema based on tool type\n let inputSchema: { type: \"object\"; properties: Record<string, object>; required: string[] };\n\n if (tt.type === 'close') {\n // close requires a ticketSlug\n inputSchema = {\n type: \"object\" as const,\n properties: {\n ticketSlug: {\n type: \"string\",\n description: \"Ticket number (e.g., '102') or full slug (e.g., '102-fix-auth')\",\n },\n },\n required: [\"ticketSlug\"],\n };\n } else if (tt.type === 'work') {\n // Unified work command (replaces open + work)\n inputSchema = {\n type: \"object\" as const,\n properties: {\n ticketSlug: {\n type: \"string\",\n description: \"Optional: Ticket number (e.g., '102') or full slug. If not provided, gets next ticket from open queue.\",\n },\n },\n required: [],\n };\n } else if (tt.type === 'create') {\n inputSchema = {\n type: \"object\" as const,\n properties: {\n content: {\n type: \"string\",\n description: \"The generated ticket content. First line should be a clear descriptive title (becomes the slug). Body contains tasks, description, context as needed.\",\n },\n },\n required: [\"content\"],\n };\n } else if (tt.type === 'search') {\n // Ticket 155: tickets:search\n inputSchema = {\n type: \"object\" as const,\n properties: {\n query: {\n type: \"string\",\n description: \"Search query (min 3 characters)\",\n },\n },\n required: [\"query\"],\n };\n } else if (tt.type === 'get') {\n // Ticket 155: tickets:get (read-only inspection)\n inputSchema = {\n type: \"object\" as const,\n properties: {\n ticketSlug: {\n type: \"string\",\n description: \"Ticket number (e.g., '102') or full slug\",\n },\n },\n required: [\"ticketSlug\"],\n };\n } else if (tt.type === 'list') {\n // Ticket 155: tickets:list (no parameters - shows active queue)\n inputSchema = {\n type: \"object\" as const,\n properties: {},\n required: [],\n };\n } else if (tt.type === 'update') {\n // Ticket 293: tickets:update (append content to ticket)\n inputSchema = {\n type: \"object\" as const,\n properties: {\n ticketSlug: {\n type: \"string\",\n description: \"Ticket number (e.g., '102') or full slug (e.g., '102-fix-auth')\",\n },\n content: {\n type: \"string\",\n description: \"Update content to append to the ticket (markdown supported)\",\n },\n },\n required: [\"ticketSlug\", \"content\"],\n };\n } else {\n // Fallback: no params\n inputSchema = {\n type: \"object\" as const,\n properties: {},\n required: [],\n };\n }\n\n return {\n name: tt.name,\n description: tt.description,\n inputSchema,\n };\n }),\n // Dynamic run_prompt tools per workspace\n ...dynamicRunPromptTools.map((rp) => ({\n name: rp.name,\n description: rp.description,\n inputSchema: {\n type: \"object\" as const,\n properties: {\n slug: {\n type: \"string\",\n description: \"Prompt slug to execute (e.g., 'code-review', 'plan')\",\n },\n },\n required: [\"slug\"],\n },\n })),\n // Dynamic system:prompts tools per workspace (workspace-scoped prompt listing)\n ...dynamicSystemPromptsTools.map((sp) => ({\n name: sp.name,\n description: sp.description,\n inputSchema: {\n type: \"object\" as const,\n properties: {\n search: {\n type: \"string\",\n description: \"Optional search term to filter prompts by name or description (case-insensitive)\",\n },\n },\n },\n })),\n // Dynamic per-prompt tools (each prompt as its own tool)\n ...dynamicPromptTools.map((pt) => ({\n name: pt.name,\n description: pt.description,\n inputSchema: {\n type: \"object\" as const,\n properties: {},\n required: [],\n },\n })),\n ];\n\n return { tools };\n });\n\n // Register call_tool handler\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const toolName = request.params.name;\n\n // Handle workspace:run_prompt tools - dynamic prompt execution per workspace\n // Performance optimization: Uses O(1) index lookup instead of O(n) fetch all + filter\n const runPromptTool = dynamicRunPromptTools.find((rp) => rp.name === toolName);\n if (runPromptTool) {\n const promptSlug = request.params.arguments?.slug as string | undefined;\n\n if (!promptSlug) {\n return {\n content: [\n {\n type: \"text\",\n text: `Error: Missing 'slug' parameter. Provide prompt slug (e.g., 'code-review', 'plan').\n\nUse \\`${runPromptTool.workspaceSlug}:system:prompts\\` to list available prompts.`,\n },\n ],\n isError: true,\n };\n }\n\n // Execute the prompt using optimized single-prompt fetch (O(1) index lookup)\n try {\n const result = await fetchAndExecutePrompt(\n runPromptTool.workspaceSlug,\n promptSlug,\n config,\n convexClient\n );\n\n // Convert prompt result to tool result format\n const promptText = result.messages\n .map((msg) => msg.content.text)\n .join('\\n\\n');\n\n return {\n content: [\n {\n type: \"text\",\n text: promptText,\n },\n ],\n };\n } catch (error) {\n // Handle ambiguous prompt matches with helpful suggestions\n if (error instanceof AmbiguousPromptError) {\n const suggestionsList = error.suggestions.map((s) => ` • ${s}`).join(\"\\n\");\n console.error(`[MCP] ${toolName} ambiguous match:`, error.suggestions);\n return {\n content: [\n {\n type: \"text\",\n text: `Multiple prompts match \"${promptSlug}\". Please specify one of:\\n\\n${suggestionsList}\\n\\nExample: \\`${runPromptTool.workspaceSlug}:system:run_prompt ${error.suggestions[0]}\\``,\n },\n ],\n isError: true,\n };\n }\n\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] ${toolName} error:`, error);\n return {\n content: [\n {\n type: \"text\",\n text: `Error executing prompt \"${promptSlug}\": ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n }\n\n // Handle workspace:system:prompts tool (workspace-scoped prompt listing)\n // Uses cached metadata (lightweight) - no need to fetch full content for listing\n const systemPromptsTool = dynamicSystemPromptsTools.find((sp) => sp.name === toolName);\n if (systemPromptsTool) {\n const searchTerm = request.params.arguments?.search as string | undefined;\n const workspaceSlug = systemPromptsTool.workspaceSlug;\n\n // Filter prompts to only those in this workspace\n const workspacePrompts = validPrompts.filter((p) => p.workspaceSlug === workspaceSlug);\n const uniquePrompts = Array.from(\n new Map(workspacePrompts.map((p) => [buildPromptNameFromMetadata(p), p])).values()\n );\n\n // Filter user prompts by search term (metadata only - slug and description available)\n let filteredPrompts = uniquePrompts;\n if (searchTerm) {\n const lowerSearch = searchTerm.toLowerCase();\n filteredPrompts = uniquePrompts.filter(\n (p) =>\n p.slug.toLowerCase().includes(lowerSearch) ||\n p.description?.toLowerCase().includes(lowerSearch)\n );\n }\n\n // Sort prompts by slug\n filteredPrompts.sort((a, b) => a.slug.localeCompare(b.slug));\n\n // Build user prompts list\n const userPromptList = filteredPrompts\n .map((p) => {\n const desc = p.description || \"No description\";\n return `• ${p.slug}\\n Description: ${desc}`;\n })\n .join(\"\\n\\n\");\n\n const summary = searchTerm\n ? `Found ${filteredPrompts.length} prompt(s) matching \"${searchTerm}\" in workspace \"${workspaceSlug}\":`\n : `Available prompts in workspace \"${workspaceSlug}\" (${filteredPrompts.length} total):`;\n\n return {\n content: [\n {\n type: \"text\",\n text: userPromptList\n ? `${summary}\\n\\n${userPromptList}`\n : `${summary}\\n\\nNo prompts found.`,\n },\n ],\n };\n }\n\n // Handle ticket tool invocations (Ticket 135, 153, unified work)\n const ticketTool = dynamicTicketTools.find((tt) => tt.name === toolName);\n if (ticketTool) {\n if (ticketTool.type === 'work') {\n // Unified work command (replaces both open and work)\n const ticketSlug = request.params.arguments?.ticketSlug as string | undefined;\n\n try {\n // Call mutation (not query) - this can move ticket from open to working\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const result = await (convexClient as any).mutation(\n \"mcp_tickets:workMcpTicket\",\n {\n ...buildAuthArgs(config),\n workspaceSlug: ticketTool.workspaceSlug,\n ticketSlug: ticketSlug, // Optional: specific ticket to work on\n }\n ) as {\n slug: string;\n ticketNumber?: number;\n status: string;\n content: string;\n startedAt?: number;\n remainingTickets: number;\n wasOpened: boolean;\n } | { error: string; accessDenied: true } | null;\n\n if (!result) {\n const message = ticketSlug\n ? `Ticket \"${ticketSlug}\" not found or not in open/working status in workspace \"${ticketTool.workspaceSlug}\".`\n : `No open tickets in workspace \"${ticketTool.workspaceSlug}\".`;\n return {\n content: [\n {\n type: \"text\",\n text: message,\n },\n ],\n };\n }\n\n // Handle access denied response\n if ('accessDenied' in result && result.accessDenied) {\n return {\n content: [\n {\n type: \"text\",\n text: `Access denied: ${result.error}`,\n },\n ],\n isError: true,\n };\n }\n\n const startedInfo = result.startedAt\n ? `\\nStarted: ${new Date(result.startedAt).toISOString()}`\n : '';\n\n // Different footer based on whether this was a fresh open or resumption\n const statusNote = result.wasOpened\n ? `_Ticket moved to working status. ${result.remainingTickets} ticket(s) remaining in queue._`\n : `_Resuming work on this ticket. ${result.remainingTickets} ticket(s) in queue._`;\n\n return {\n content: [\n {\n type: \"text\",\n text: `# Ticket: ${result.slug} [WORKING]${startedInfo}\n\n${result.content}\n\n---\n${statusNote}`,\n },\n ],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] tickets:work error:`, error);\n return {\n content: [\n {\n type: \"text\",\n text: `Error getting work: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n } else if (ticketTool.type === 'create') {\n // Handle tickets:create (Ticket 149)\n const 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 ...buildAuthArgs(config),\n workspaceSlug: ticketTool.workspaceSlug,\n content,\n }\n ) as { slug: string; preview: string; position: number; totalBacklog: number };\n\n return {\n content: [\n {\n type: \"text\",\n text: `✅ Created ticket [${result.slug}] in backlog for workspace \"${ticketTool.workspaceSlug}\".\n\nPosition: #${result.position} of ${result.totalBacklog} backlog tickets\nPreview: ${result.preview}\n\n_Ticket created in backlog. Use \\`tickets:work ${result.slug}\\` to move it to working status._`,\n },\n ],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] tickets:create error:`, error);\n return {\n content: [\n {\n type: \"text\",\n text: `Error creating ticket: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n } else if (ticketTool.type === 'close') {\n // Ticket 151: Handle tickets:close\n const 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 ...buildAuthArgs(config),\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_Reminder: Ensure all embedded \\`[RUN_PROMPT ...]\\` directives were executed before closing._`,\n },\n ],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] tickets:close error:`, error);\n return {\n content: [\n {\n type: \"text\",\n text: `Error closing ticket: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n } else if (ticketTool.type === 'search') {\n // Ticket 155: Handle tickets:search\n const query = request.params.arguments?.query as string | undefined;\n\n if (!query || query.length < 3) {\n return {\n content: [\n {\n type: \"text\",\n text: `Error: Search query must be at least 3 characters`,\n },\n ],\n isError: true,\n };\n }\n\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const result = await (convexClient as any).query(\n \"mcp_tickets:searchMcpTickets\",\n {\n ...buildAuthArgs(config),\n workspaceSlug: ticketTool.workspaceSlug,\n query,\n }\n ) as Array<{ slug: string; ticketNumber?: number; status: string; matchSnippet: string; createdAt: number }>;\n\n if (result.length === 0) {\n return {\n content: [\n {\n type: \"text\",\n text: `No tickets found matching \"${query}\" in workspace \"${ticketTool.workspaceSlug}\".`,\n },\n ],\n };\n }\n\n // Format as list with status badges\n const formattedList = result\n .map((t) => {\n const statusBadge = t.status === 'open' ? '🟢' : t.status === 'working' ? '🟡' : t.status === 'closed' ? '⚫' : '⚪';\n const num = t.ticketNumber ? `#${t.ticketNumber}` : '';\n return `${statusBadge} ${num} ${t.slug}\\n ${t.matchSnippet}`;\n })\n .join('\\n\\n');\n\n return {\n content: [\n {\n type: \"text\",\n text: `Found ${result.length} ticket(s) matching \"${query}\":\\n\\n${formattedList}`,\n },\n ],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] tickets:search error:`, error);\n return {\n content: [\n {\n type: \"text\",\n text: `Error searching tickets: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n } else if (ticketTool.type === 'get') {\n // Ticket 155: Handle tickets:get (read-only inspection)\n const 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. Provide a ticket number (e.g., \"102\") or full slug.`,\n },\n ],\n isError: true,\n };\n }\n\n try {\n // Uses existing getMcpTicket query - read-only inspection\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const result = await (convexClient as any).query(\n \"mcp_tickets:getMcpTicket\",\n {\n ...buildAuthArgs(config),\n workspaceSlug: ticketTool.workspaceSlug,\n ticketSlug,\n }\n ) as { slug: string; ticketNumber?: number; content: string; status: string; createdAt: number; startedAt?: number; closedAt?: number; assignedTo?: string } | { error: string; accessDenied: true } | 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 // Handle access denied response\n if ('accessDenied' in result && result.accessDenied) {\n return {\n content: [\n {\n type: \"text\",\n text: `Access denied: ${result.error}`,\n },\n ],\n isError: true,\n };\n }\n\n // Build status line\n const statusBadge = result.status.toUpperCase();\n const startedInfo = result.startedAt ? `\\nStarted: ${new Date(result.startedAt).toISOString()}` : '';\n const closedInfo = result.closedAt ? `\\nClosed: ${new Date(result.closedAt).toISOString()}` : '';\n const assignedInfo = result.assignedTo ? `\\nAssigned to: ${result.assignedTo}` : '\\nUnassigned';\n\n return {\n content: [\n {\n type: \"text\",\n text: `# Ticket: ${result.slug} [${statusBadge}]${startedInfo}${closedInfo}${assignedInfo}\n\n${result.content}\n\n---\n_Read-only inspection. Use tickets:work to start working on this ticket._`,\n },\n ],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] tickets:get error:`, error);\n return {\n content: [\n {\n type: \"text\",\n text: `Error getting ticket: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n } else if (ticketTool.type === 'list') {\n // Ticket 155: Handle tickets:list (active queue)\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const result = await (convexClient as any).query(\n \"mcp_tickets:listMcpTickets\",\n {\n ...buildAuthArgs(config),\n workspaceSlug: ticketTool.workspaceSlug,\n }\n ) as Array<{ position: number; slug: string; ticketNumber?: number; status: string; preview: string; createdAt: number; startedAt?: number }>;\n\n if (result.length === 0) {\n return {\n content: [\n {\n type: \"text\",\n text: `No active tickets in workspace \"${ticketTool.workspaceSlug}\". All tickets are closed or archived.`,\n },\n ],\n };\n }\n\n // Group by status for clear display\n const openTickets = result.filter(t => t.status === 'open');\n const workingTickets = result.filter(t => t.status === 'working');\n const backlogTickets = result.filter(t => t.status === 'backlog');\n\n const formatTicketLine = (t: typeof result[0]) => {\n const num = t.ticketNumber ? `#${t.ticketNumber}` : '';\n return ` ${t.position}. ${num} ${t.slug}\\n ${t.preview}`;\n };\n\n const sections: string[] = [];\n\n if (openTickets.length > 0) {\n sections.push(`**🟢 Open (${openTickets.length})**\\n${openTickets.map(formatTicketLine).join('\\n')}`);\n }\n if (workingTickets.length > 0) {\n sections.push(`**🟡 Working (${workingTickets.length})**\\n${workingTickets.map(formatTicketLine).join('\\n')}`);\n }\n if (backlogTickets.length > 0) {\n sections.push(`**⚪ Backlog (${backlogTickets.length})**\\n${backlogTickets.map(formatTicketLine).join('\\n')}`);\n }\n\n return {\n content: [\n {\n type: \"text\",\n text: `# Active Queue: ${ticketTool.workspaceSlug}\\n\\n${result.length} ticket(s) in queue\\n\\n${sections.join('\\n\\n')}`,\n },\n ],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] tickets:list error:`, error);\n return {\n content: [\n {\n type: \"text\",\n text: `Error listing tickets: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n } else if (ticketTool.type === 'update') {\n // Ticket 293: Handle tickets:update (append content to ticket)\n const ticketSlug = request.params.arguments?.ticketSlug as string | undefined;\n const content = request.params.arguments?.content as string | undefined;\n\n if (!ticketSlug) {\n return {\n content: [\n {\n type: \"text\",\n text: `Error: Missing ticketSlug parameter. Provide a ticket number (e.g., \"102\") or full slug.`,\n },\n ],\n isError: true,\n };\n }\n\n if (!content) {\n return {\n content: [\n {\n type: \"text\",\n text: `Error: Missing content parameter. Provide the update content to append to the ticket.`,\n },\n ],\n isError: true,\n };\n }\n\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const result = await (convexClient as any).mutation(\n \"mcp_tickets:updateMcpTicket\",\n {\n ...buildAuthArgs(config),\n workspaceSlug: ticketTool.workspaceSlug,\n ticketSlug,\n content,\n }\n ) as UpdateTicketResult;\n\n return {\n content: [\n {\n type: \"text\",\n text: `✅ Ticket [${result.slug}] updated in workspace \"${ticketTool.workspaceSlug}\".\nUpdated at: ${new Date(result.updatedAt).toISOString()}\n_Ticket content has been appended with your update._`,\n },\n ],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] tickets:update error:`, error);\n return {\n content: [\n {\n type: \"text\",\n text: `Error updating ticket: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n }\n }\n\n // Handle per-prompt tool invocations (e.g., \"projects:code-review\")\n const promptTool = dynamicPromptTools.find((pt) => pt.name === toolName);\n if (promptTool) {\n try {\n const result = await fetchAndExecutePrompt(\n promptTool.workspaceSlug,\n promptTool.promptSlug,\n config,\n convexClient\n );\n\n const promptText = result.messages\n .map((msg) => msg.content.text)\n .join('\\n\\n');\n\n return {\n content: [{ type: \"text\", text: promptText }],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] ${toolName} error:`, error);\n return {\n content: [{ type: \"text\", text: `Error executing prompt: ${errorMessage}` }],\n isError: true,\n };\n }\n }\n\n // Unknown tool\n throw new Error(`Unknown tool: ${toolName}. Use workspace:system:run_prompt to execute prompts by name, or check available tools.`);\n });\n\n // Start server with stdio transport\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n console.error(\"[MCP] Server started successfully\");\n console.error(`[MCP] Deployment: ${config.isDev ? \"DEVELOPMENT\" : \"PRODUCTION\"}`);\n console.error(`[MCP] Convex URL: ${config.convexUrl}`);\n console.error(`[MCP] Data mode: REAL-TIME (fetches fresh data on each invocation)`);\n\n // List all prompts\n const allPromptNames = [...Array.from(promptNames)].sort();\n console.error(`[MCP] Prompts available: ${allPromptNames.join(\", \")}`);\n console.error(`[MCP] - Total prompts: ${promptNames.size}`);\n\n // Keep the event loop alive with a heartbeat\n // This prevents Node from exiting when there are no active handles\n setInterval(() => {\n // Heartbeat every 60 seconds to keep process alive\n }, 60000);\n\n // Return a promise that never resolves to keep the server running\n return new Promise<void>(() => {\n // The transport handles stdin/stdout communication\n // The interval above keeps the event loop active\n });\n}\n\n/**\n * Validate 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 * Validate workspace token with Convex\n *\n * Workspace tokens provide scoped access to a single workspace for ticket operations.\n * Returns workspace details if valid, error if invalid/revoked.\n */\nasync function validateWorkspaceToken(\n client: ConvexHttpClient,\n token: string\n): Promise<WorkspaceTokenValidation> {\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const result = await client.query(\"workspace_tokens:validateWorkspaceToken\" as any, { token });\n if (result && result.valid) {\n return {\n valid: true,\n workspaceId: result.workspaceId,\n workspaceSlug: result.workspaceSlug,\n ownerId: result.ownerId,\n tokenId: result.tokenId,\n tokenName: result.tokenName,\n };\n }\n return { valid: false, error: \"Invalid or revoked workspace token\" };\n } catch (error) {\n return {\n valid: false,\n error: error instanceof Error ? error.message : \"Unknown error\",\n };\n }\n}\n\n/**\n * Fetch lightweight metadata for prompts exposed to MCP (startup optimization)\n * Returns only slug, description, workspaceSlug - NO flattenedPrompt\n *\n * Performance improvement: ~50-100KB reduced to ~2-5KB at startup\n */\nasync function fetchMcpPromptMetadata(\n client: ConvexHttpClient,\n apiKey: string,\n workspaces: string[]\n): Promise<McpPromptMetadata[]> {\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const metadata = await client.query(\"mcp_prompts:getMcpPromptMetadata\" as any, {\n apiKey,\n workspaces: workspaces.length > 0 ? workspaces : undefined, // Only pass if specified\n });\n return metadata;\n } catch (error) {\n throw new Error(\n `Failed to fetch prompt metadata: ${error instanceof Error ? error.message : \"Unknown error\"}`\n );\n }\n}\n\n/**\n * Fetch lightweight metadata for prompts using workspace token auth\n * Scoped to the token's workspace only\n *\n * Similar to fetchMcpPromptMetadata but uses workspace token authentication\n */\nasync function fetchMcpPromptMetadataByToken(\n client: ConvexHttpClient,\n workspaceToken: string,\n workspaceSlug: string\n): Promise<McpPromptMetadata[]> {\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const metadata = await client.query(\"mcp_prompts:getMcpPromptMetadataByToken\" as any, {\n workspaceToken,\n workspaceSlug,\n });\n return metadata;\n } catch (error) {\n throw new Error(\n `Failed to fetch prompt metadata: ${error instanceof Error ? error.message : \"Unknown error\"}`\n );\n }\n}\n\n// Note: fetchMcpCommands and buildCommandName removed - commands resource eliminated\n","import type { ConvexHttpClient } from \"convex/browser\";\nimport type { McpPrompt, McpPromptMetadata, PromptMatchResult, 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 name from lightweight metadata (no flattenedPrompt)\n * Used for startup/tool registration when only metadata is available\n */\nexport function buildPromptNameFromMetadata(metadata: McpPromptMetadata): string {\n const workspace = sanitizeForMcp(metadata.workspaceSlug || 'default') || 'default';\n const promptSlug = sanitizeForMcp(metadata.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 * @deprecated Use buildPromptHandlerOptimized for better performance\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\n/**\n * Custom error class for ambiguous prompt matches\n * Contains suggestions for the user to choose from\n */\nexport class AmbiguousPromptError extends Error {\n suggestions: string[];\n\n constructor(promptSlug: string, suggestions: string[]) {\n super(`Multiple prompts match \"${promptSlug}\". Please be more specific.`);\n this.name = \"AmbiguousPromptError\";\n this.suggestions = suggestions;\n }\n}\n\n/**\n * Fetch a single prompt and execute it - O(1) index lookup with fuzzy fallback\n *\n * Supports both API key and workspace token authentication:\n * - API key: Full access to all user workspaces\n * - Workspace token: Scoped access to token's workspace only\n *\n * Matching algorithm:\n * 1. Exact match via index (O(1)) - fast path\n * 2. Prefix match fallback if exact fails\n * 3. Contains match fallback if prefix fails\n * 4. Ambiguous error if multiple matches found\n *\n * Performance optimization: Uses getMcpPromptBySlug for direct index lookup\n * with fallback to fuzzy matching only when exact match fails.\n */\nexport async function fetchAndExecutePrompt(\n workspaceSlug: string,\n promptSlug: string,\n config: ServerConfig,\n convexClient: ConvexHttpClient\n): Promise<{ messages: Array<{ role: string; content: { type: \"text\"; text: string } }> }> {\n let result: PromptMatchResult;\n\n // Use appropriate query based on auth type\n if (config.apiKey) {\n // API key auth - use standard query\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n result = await convexClient.query(\"mcp_prompts:getMcpPromptBySlug\" as any, {\n apiKey: config.apiKey,\n workspaceSlug,\n promptSlug,\n }) as PromptMatchResult;\n } else if (config.workspaceToken) {\n // Workspace token auth - use token-specific query\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n result = await convexClient.query(\"mcp_prompts:getMcpPromptBySlugWithToken\" as any, {\n workspaceToken: config.workspaceToken,\n workspaceSlug,\n promptSlug,\n }) as PromptMatchResult;\n } else {\n throw new Error(\"No authentication configured\");\n }\n\n // Handle null - no matches found\n if (!result) {\n throw new Error(\n `Prompt \"${promptSlug}\" not found in workspace \"${workspaceSlug}\".`\n );\n }\n\n // Handle ambiguous matches - throw custom error with suggestions\n if (result.matchType === \"ambiguous\") {\n throw new AmbiguousPromptError(promptSlug, result.suggestions);\n }\n\n // Handle successful match (exact, prefix, or contains)\n if (!result.flattenedPrompt) {\n throw new Error(\n `Prompt \"${result.slug}\" has no flattened content. Please re-save the prompt to regenerate it.`\n );\n }\n\n // Log match type for debugging\n const matchNote = result.matchType === \"exact\"\n ? \"exact match\"\n : result.matchType === \"prefix\"\n ? `prefix match → ${result.slug}`\n : `contains match → ${result.slug}`;\n console.error(`[MCP] Fetched prompt: ${workspaceSlug}:${promptSlug} (${matchNote})`);\n\n // Return raw flattened prompt (client will parse YAML front matter)\n return {\n messages: [\n {\n role: \"user\",\n content: {\n type: \"text\" as const,\n text: result.flattenedPrompt,\n },\n },\n ],\n };\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/convex-client.ts","../src/server.ts","../src/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 authentication (API key or workspace token)\n // API key takes precedence if both are provided\n const apiKey = process.env.PPM_API_KEY || process.env.THEPROMPTEDITOR_API_KEY;\n const workspaceToken = process.env.PPM_WORKSPACE_TOKEN;\n\n // PPM_AGENT: Optional agent identity selection (only with API key auth)\n // When set, uses the named workspace token as identity while API key provides access\n const ppmAgent = process.env.PPM_AGENT;\n\n if (!apiKey && !workspaceToken) {\n console.error(\n \"[MCP] ERROR: Missing authentication. Provide either PPM_API_KEY or PPM_WORKSPACE_TOKEN\"\n );\n console.error(\n \"[MCP] Options:\"\n );\n console.error(\"[MCP] export PPM_API_KEY=your_api_key_here # Full access to all workspaces\");\n console.error(\"[MCP] export PPM_WORKSPACE_TOKEN=wst_... # Scoped access to one workspace (tickets only)\");\n process.exit(1);\n }\n\n // Log which auth mode is being used\n if (apiKey) {\n console.error(\"[MCP] Auth mode: API Key (full access)\");\n if (workspaceToken) {\n console.error(\"[MCP] Note: PPM_WORKSPACE_TOKEN ignored when PPM_API_KEY is set\");\n }\n if (ppmAgent) {\n console.error(`[MCP] Agent identity: ${ppmAgent}`);\n }\n } else {\n console.error(\"[MCP] Auth mode: Workspace Token (scoped access, tickets only)\");\n if (ppmAgent) {\n console.error(\"[MCP] Note: PPM_AGENT ignored when using PPM_WORKSPACE_TOKEN directly\");\n }\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: apiKey || undefined, // Use undefined if not set (not empty string)\n workspaceToken: apiKey ? undefined : workspaceToken, // Only use token if no API key\n isDev,\n convexUrl,\n selectedWorkspaces, // Workspace slugs to filter (empty = all workspaces)\n agentName: apiKey && ppmAgent ? ppmAgent : undefined, // Only use agent name with API key auth\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 { ServerConfig, WorkspaceTokenValidation } from \"./types.js\";\n\n/**\n * Close ticket result (Ticket 151)\n */\ninterface CloseTicketResult {\n slug: string;\n status: string;\n closedAt: number;\n}\n\n/**\n * Update ticket result (Ticket 293)\n */\ninterface UpdateTicketResult {\n slug: string;\n ticketNumber?: number;\n status: string;\n updatedAt: number;\n}\n\nimport { fetchAndExecuteAccountScopedPrompt, AmbiguousPromptError } from \"./prompt-builder.js\";\n// Note: McpCommandMetadata removed - commands resource eliminated\n// Note: buildPromptNameFromMetadata, fetchAndExecutePrompt removed - prompts now account-scoped\n\n/**\n * Build authentication arguments for Convex mutations/queries.\n * Passes either apiKey OR workspaceToken (never both) based on config.\n *\n * When using API key with agentName, passes agentName for identity resolution.\n * The backend will look up the workspace token by name to use as the actor identity.\n */\nfunction buildAuthArgs(config: ServerConfig): { apiKey?: string; workspaceToken?: string; agentName?: string } {\n if (config.apiKey) {\n return {\n apiKey: config.apiKey,\n agentName: config.agentName, // Pass agent name for identity resolution\n };\n }\n if (config.workspaceToken) {\n return { workspaceToken: config.workspaceToken };\n }\n return {};\n}\n\n/**\n * Built-in system tools (no global tools - all are workspace-scoped now)\n */\nconst SYSTEM_TOOLS: {\n name: string;\n description: string;\n inputSchema: object;\n promptContent: string;\n isSlashCommand: boolean; // true = appears as /ppm:name, false = tool only\n}[] = [\n // All tools are now workspace-scoped (see dynamic*Tools arrays below)\n];\n\n/**\n * Initialize and start the MCP server\n */\nexport async function startServer(\n config: ServerConfig,\n convexClient: ConvexHttpClient\n): Promise<void> {\n // Track auth mode and workspace scope\n let tokenWorkspaceSlug: string | undefined;\n\n // Validate authentication - API key or workspace token\n if (config.apiKey) {\n // API Key mode - full access\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 } else if (config.workspaceToken) {\n // Workspace Token mode - scoped access\n console.error(\"[MCP] Validating workspace token...\");\n const validation = await validateWorkspaceToken(convexClient, config.workspaceToken);\n if (!validation.valid) {\n throw new Error(`Invalid workspace token: ${validation.error}`);\n }\n tokenWorkspaceSlug = validation.workspaceSlug;\n console.error(`[MCP] Workspace token validated: \"${validation.tokenName}\" for workspace \"${tokenWorkspaceSlug}\"`);\n // Note: In token mode, prompts are NOT accessible - only tickets\n } else {\n throw new Error(\"No authentication provided. Set PPM_API_KEY or PPM_WORKSPACE_TOKEN.\");\n }\n\n // Fetch account-scoped prompts (global MCP tools) and workspaces (for ticket tools)\n // Account-scoped prompts: Available as global tools (e.g., \"code-review\")\n // Workspaces: Needed for ticket tool registration (e.g., \"projects_tickets_work\")\n let accountScopedPrompts: Array<{ slug: string; description?: string }> = [];\n let workspacesForTickets: Array<{ slug: string; description?: string }> = [];\n\n console.error(\"[MCP] Fetching prompt and workspace metadata...\");\n\n if (config.apiKey) {\n // API key mode - fetch account-scoped prompts and workspaces\n accountScopedPrompts = await fetchAccountScopedPromptMetadata(convexClient, config.apiKey);\n\n // Fetch workspaces for ticket tools\n // If --workspaces specified, only register ticket tools for those workspaces\n const allWorkspaces = await fetchMcpWorkspaces(convexClient, config.apiKey);\n if (config.selectedWorkspaces.length > 0) {\n const selectedSet = new Set(config.selectedWorkspaces);\n workspacesForTickets = allWorkspaces.filter((w) => selectedSet.has(w.slug));\n } else {\n workspacesForTickets = allWorkspaces;\n }\n } else if (config.workspaceToken && tokenWorkspaceSlug) {\n // Workspace token mode - no prompts (token mode is tickets-only)\n // Note: Workspace tokens only have access to tickets, not account-scoped prompts\n console.error(\"[MCP] Workspace token mode: prompts not available (tickets only)\");\n workspacesForTickets = [{ slug: tokenWorkspaceSlug, name: tokenWorkspaceSlug }];\n }\n\n console.error(`[MCP] Found ${accountScopedPrompts.length} account-scoped prompts (global tools)`);\n console.error(`[MCP] Found ${workspacesForTickets.length} workspace(s) for ticket tools`);\n\n if (accountScopedPrompts.length === 0 && config.apiKey) {\n console.error(\n \"[MCP] WARNING: No account-scoped prompts found. Create prompts in the 'prompts' section to expose them via MCP.\"\n );\n }\n\n // Log workspace info for ticket tools\n if (tokenWorkspaceSlug) {\n console.error(`[MCP] Ticket workspace scope: ${tokenWorkspaceSlug} (token-scoped)`);\n } else if (config.selectedWorkspaces.length > 0) {\n console.error(`[MCP] Ticket workspace filter: ${config.selectedWorkspaces.join(', ')}`);\n } else {\n console.error(`[MCP] Ticket workspaces: ALL (${workspacesForTickets.map(w => w.slug).join(', ')})`);\n }\n\n // Build dynamic ticket tools per workspace\n // Ticket tools remain workspace-scoped (e.g., \"projects_tickets_work\")\n const workspaceSlugs = new Set(workspacesForTickets.map((w) => w.slug));\n const dynamicTicketTools: { name: string; description: string; slashDescription: string; workspaceSlug: string; type: 'work' | 'close' | 'create' | 'search' | 'get' | 'list' | 'update' }[] = [];\n\n for (const workspaceSlug of workspaceSlugs) {\n // Unified work command (replaces both tickets_open and tickets_work)\n dynamicTicketTools.push({\n name: `${workspaceSlug}_tickets_work`,\n description: `Get work from the \"${workspaceSlug}\" workspace. With no args: gets next ticket from open queue. With ticket slug/number: opens or resumes that specific ticket.`,\n slashDescription: `Get next ticket from queue, or work on specific ticket by slug/number`,\n 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 slashDescription: `Mark a working ticket as completed`,\n workspaceSlug,\n type: 'close',\n });\n dynamicTicketTools.push({\n name: `${workspaceSlug}_tickets_create`,\n description: `Create a new ticket in the \"${workspaceSlug}\" workspace queue`,\n slashDescription: `Create a new ticket in the backlog queue`,\n workspaceSlug,\n type: 'create',\n });\n dynamicTicketTools.push({\n name: `${workspaceSlug}_tickets_search`,\n description: `Search for tickets by content in the \"${workspaceSlug}\" workspace`,\n slashDescription: `Search for tickets by content`,\n workspaceSlug,\n type: 'search',\n });\n dynamicTicketTools.push({\n name: `${workspaceSlug}_tickets_get`,\n description: `Get a specific ticket by number or slug from \"${workspaceSlug}\" (read-only)`,\n slashDescription: `Get a specific ticket by number or slug (read-only)`,\n workspaceSlug,\n type: 'get',\n });\n dynamicTicketTools.push({\n name: `${workspaceSlug}_tickets_list`,\n description: `List active tickets in the \"${workspaceSlug}\" workspace (backlog + open + working)`,\n slashDescription: `List active tickets (backlog + open + working)`,\n workspaceSlug,\n type: 'list',\n });\n dynamicTicketTools.push({\n name: `${workspaceSlug}_tickets_update`,\n description: `Update a ticket in the \"${workspaceSlug}\" workspace by appending content with timestamp`,\n slashDescription: `Update a ticket by appending content with timestamp`,\n workspaceSlug,\n type: 'update',\n });\n }\n\n console.error(`[MCP] Registering ${dynamicTicketTools.length} ticket tools for ${workspaceSlugs.size} workspace(s)...`);\n\n // Build global system tools (not workspace-scoped)\n // run_prompt and system_prompts are now global since prompts are account-scoped\n const globalSystemTools: { name: string; description: string; slashDescription: string }[] = [\n {\n name: \"system_run_prompt\",\n description: \"Execute a prompt by slug. Use system_prompts to list available prompts.\",\n slashDescription: \"Execute a prompt by slug. Use system_prompts to list available prompts.\",\n },\n {\n name: \"system_prompts\",\n description: \"List all available prompts\",\n slashDescription: \"List all available prompts\",\n },\n ];\n\n console.error(`[MCP] Registering ${globalSystemTools.length} global system tools...`);\n\n // Build dynamic per-prompt tools (each prompt as its own tool)\n // Now global: Direct invocation like `code-review` instead of `projects:code-review`\n const dynamicPromptTools: {\n name: string; // \"code-review\" (global, no workspace prefix)\n description: string;\n promptSlug: string;\n }[] = [];\n\n for (const prompt of accountScopedPrompts) {\n dynamicPromptTools.push({\n name: prompt.slug, // Global tool name, no workspace prefix\n description: prompt.description || `Execute the \"${prompt.slug}\" prompt`,\n promptSlug: prompt.slug,\n });\n }\n\n console.error(`[MCP] Registering ${dynamicPromptTools.length} per-prompt tools (global)...`);\n\n // Note: Commands resource removed - users create slash commands directly from flattened prompts\n\n // Check for duplicate prompt names (now global, no workspace prefix)\n const promptNames = new Set<string>();\n const duplicates: string[] = [];\n accountScopedPrompts.forEach((p) => {\n if (promptNames.has(p.slug)) {\n duplicates.push(p.slug);\n }\n promptNames.add(p.slug);\n });\n\n if (duplicates.length > 0) {\n console.error(\n `[MCP] WARNING: Duplicate prompt slugs detected: ${duplicates.join(\", \")}. Only the first occurrence will be registered.`\n );\n }\n\n // Create MCP server\n const server = new Server(\n {\n name: \"ppm-mcp-server\",\n version: \"3.1.0\",\n },\n {\n capabilities: {\n prompts: {},\n tools: {},\n },\n }\n );\n\n // Register list_prompts handler\n // Prompts are now global (account-scoped), tickets remain workspace-scoped\n server.setRequestHandler(ListPromptsRequestSchema, async () => {\n // Build ticket prompt schemas (workspace-scoped)\n const ticketPromptSchemas = dynamicTicketTools.map((tt) => ({\n name: tt.name,\n description: tt.slashDescription,\n }));\n\n // Build global system prompt schemas\n const systemPromptsSchemas = globalSystemTools.map((st) => ({\n name: st.name,\n description: st.slashDescription,\n }));\n\n return {\n prompts: [\n ...systemPromptsSchemas, // system_prompts, system_run_prompt (global)\n ...ticketPromptSchemas, // workspace_tickets_* (workspace-scoped)\n ],\n };\n });\n\n // Register get_prompt handler\n server.setRequestHandler(GetPromptRequestSchema, async (request) => {\n const promptName = request.params.name;\n\n // Check for system tools first (as prompts for slash command access)\n const systemTool = SYSTEM_TOOLS.find((st) => st.name === promptName);\n if (systemTool) {\n return {\n description: systemTool.description,\n messages: [\n {\n role: \"user\" as const,\n content: {\n type: \"text\" as const,\n text: systemTool.promptContent,\n },\n },\n ],\n };\n }\n\n // Check for ticket commands (Ticket 135, 149, 153 - handle as slash commands)\n // Handle exact matches and _work with optional argument (e.g., \"workspace_tickets_work 102\")\n const ticketTool = dynamicTicketTools.find((tt) => tt.name === promptName);\n const ticketWorkMatch = promptName.match(/^(.+)_tickets_work\\s+(.+)$/);\n\n if (ticketTool || ticketWorkMatch) {\n let promptContent: string;\n let description: string;\n\n if (ticketWorkMatch) {\n // Handle tickets_work with specific ticket argument\n const workspaceSlug = ticketWorkMatch[1];\n const ticketArg = ticketWorkMatch[2].trim();\n description = `Work on ticket \"${ticketArg}\" from \"${workspaceSlug}\"`;\n promptContent = `Get work on ticket \"${ticketArg}\" from the \"${workspaceSlug}\" workspace.\\n\\nCall the \\`${workspaceSlug}_tickets_work\\` tool with ticketSlug: \"${ticketArg}\".\\n\\nThis will open the ticket if it's in the open queue, or resume it if already working.`;\n } else if (ticketTool!.type === 'work') {\n // Unified work command (replaces open + work)\n description = ticketTool!.description;\n promptContent = `Get work from the \"${ticketTool!.workspaceSlug}\" workspace.\\n\\nCall the \\`${ticketTool!.name}\\` tool to get the next ticket from the open queue.\\n\\nYou can also specify a ticket: /ppm:${ticketTool!.workspaceSlug}_tickets_work <number-or-slug>\\n\\nThis unified command handles both opening new tickets and resuming in-progress work.`;\n } else if (ticketTool!.type === 'create') {\n // Ticket 149: tickets:create slash command\n description = ticketTool!.description;\n promptContent = `Create a new ticket in the \"${ticketTool!.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 {\n // Fallback for unknown type\n description = ticketTool!.description;\n promptContent = `Use the \\`${ticketTool!.name}\\` tool.`;\n }\n\n return {\n description,\n messages: [\n {\n role: \"user\" as const,\n content: {\n type: \"text\" as const,\n text: promptContent,\n },\n },\n ],\n };\n }\n\n // Check for global system_prompts command\n if (promptName === \"system_prompts\") {\n return {\n description: \"List all available prompts\",\n messages: [\n {\n role: \"user\" as const,\n content: {\n type: \"text\" as const,\n text: `List all available prompts.\n\nCall the \\`system_prompts\\` tool with optional \\`search\\` parameter to filter results.`,\n },\n },\n ],\n };\n }\n\n // Check for global system_run_prompt command (with optional prompt slug argument)\n const runPromptMatch = promptName.match(/^system_run_prompt\\s+(.+)$/);\n\n if (promptName === \"system_run_prompt\" || runPromptMatch) {\n let promptContent: string;\n let description: string;\n\n if (runPromptMatch) {\n // Handle run_prompt with specific prompt slug argument\n const promptSlug = runPromptMatch[1].trim();\n description = `Execute prompt \"${promptSlug}\"`;\n promptContent = `Execute the \"${promptSlug}\" prompt.\\n\\nCall the \\`system_run_prompt\\` tool with slug: \"${promptSlug}\".`;\n } else {\n // Base run_prompt without argument - show help\n description = \"Execute a prompt by slug\";\n promptContent = `Execute a prompt by slug.\n\n## Usage\nCall the \\`system_run_prompt\\` tool with the prompt slug.\n\n## Available Prompts\nUse \\`system_prompts\\` to list all available prompts.\n\n## Example\n/ppm:system_run_prompt code-review\n\nThis will execute the \"code-review\" prompt.`;\n }\n\n return {\n description,\n messages: [\n {\n role: \"user\" as const,\n content: {\n type: \"text\" as const,\n text: promptContent,\n },\n },\n ],\n };\n }\n\n // Note: Command handling removed - commands resource eliminated\n\n // Unknown prompt\n throw new Error(`Unknown prompt: ${promptName}. Use system_run_prompt to execute prompts.`);\n });\n\n // Register list_tools handler\n // Note: Per-prompt tool registration removed in favor of workspace-scoped run_prompt tools\n // This reduces token overhead and allows dynamic prompt discovery without server restart\n server.setRequestHandler(ListToolsRequestSchema, async () => {\n // Build tools array: system tools + ticket tools + workspace:run_prompt tools\n // Individual prompts no longer registered as separate tools\n const tools = [\n // System tools with full input schemas\n ...SYSTEM_TOOLS.map((st) => ({\n name: st.name,\n description: st.description,\n inputSchema: st.inputSchema,\n })),\n // Dynamic ticket tools per workspace (Ticket 135, 149, 151, 153, unified work)\n ...dynamicTicketTools.map((tt) => {\n // Build inputSchema based on tool type\n let inputSchema: { type: \"object\"; properties: Record<string, object>; required: string[] };\n\n if (tt.type === 'close') {\n // close requires a ticketSlug\n inputSchema = {\n type: \"object\" as const,\n properties: {\n ticketSlug: {\n type: \"string\",\n description: \"Ticket number (e.g., '102') or full slug (e.g., '102-fix-auth')\",\n },\n },\n required: [\"ticketSlug\"],\n };\n } else if (tt.type === 'work') {\n // Unified work command (replaces open + work)\n inputSchema = {\n type: \"object\" as const,\n properties: {\n ticketSlug: {\n type: \"string\",\n description: \"Optional: Ticket number (e.g., '102') or full slug. If not provided, gets next ticket from open queue.\",\n },\n },\n required: [],\n };\n } else if (tt.type === 'create') {\n inputSchema = {\n type: \"object\" as const,\n properties: {\n content: {\n type: \"string\",\n description: \"The generated ticket content. First line should be a clear descriptive title (becomes the slug). Body contains tasks, description, context as needed.\",\n },\n },\n required: [\"content\"],\n };\n } else if (tt.type === 'search') {\n // Ticket 155: tickets:search\n inputSchema = {\n type: \"object\" as const,\n properties: {\n query: {\n type: \"string\",\n description: \"Search query (min 3 characters)\",\n },\n },\n required: [\"query\"],\n };\n } else if (tt.type === 'get') {\n // Ticket 155: tickets:get (read-only inspection)\n inputSchema = {\n type: \"object\" as const,\n properties: {\n ticketSlug: {\n type: \"string\",\n description: \"Ticket number (e.g., '102') or full slug\",\n },\n },\n required: [\"ticketSlug\"],\n };\n } else if (tt.type === 'list') {\n // Ticket 155: tickets:list (no parameters - shows active queue)\n inputSchema = {\n type: \"object\" as const,\n properties: {},\n required: [],\n };\n } else if (tt.type === 'update') {\n // Ticket 293: tickets:update (append content to ticket)\n inputSchema = {\n type: \"object\" as const,\n properties: {\n ticketSlug: {\n type: \"string\",\n description: \"Ticket number (e.g., '102') or full slug (e.g., '102-fix-auth')\",\n },\n content: {\n type: \"string\",\n description: \"Update content to append to the ticket (markdown supported)\",\n },\n },\n required: [\"ticketSlug\", \"content\"],\n };\n } else {\n // Fallback: no params\n inputSchema = {\n type: \"object\" as const,\n properties: {},\n required: [],\n };\n }\n\n return {\n name: tt.name,\n description: tt.description,\n inputSchema,\n };\n }),\n // Global system_run_prompt tool\n {\n name: \"system_run_prompt\",\n description: \"Execute a prompt by slug. Use system_prompts to list available prompts.\",\n inputSchema: {\n type: \"object\" as const,\n properties: {\n slug: {\n type: \"string\",\n description: \"Prompt slug to execute (e.g., 'code-review', 'plan')\",\n },\n },\n required: [\"slug\"],\n },\n },\n // Global system_prompts tool\n {\n name: \"system_prompts\",\n description: \"List all available prompts\",\n inputSchema: {\n type: \"object\" as const,\n properties: {\n search: {\n type: \"string\",\n description: \"Optional search term to filter prompts by name or description (case-insensitive)\",\n },\n },\n },\n },\n // Dynamic per-prompt tools (each prompt as its own global tool)\n ...dynamicPromptTools.map((pt) => ({\n name: pt.name,\n description: pt.description,\n inputSchema: {\n type: \"object\" as const,\n properties: {},\n required: [],\n },\n })),\n ];\n\n return { tools };\n });\n\n // Register call_tool handler\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const toolName = request.params.name;\n\n // Handle global system_run_prompt tool\n if (toolName === \"system_run_prompt\") {\n const promptSlug = request.params.arguments?.slug as string | undefined;\n\n if (!promptSlug) {\n return {\n content: [\n {\n type: \"text\",\n text: `Error: Missing 'slug' parameter. Provide prompt slug (e.g., 'code-review', 'plan').\n\nUse \\`system_prompts\\` to list available prompts.`,\n },\n ],\n isError: true,\n };\n }\n\n // Execute the prompt using account-scoped lookup\n try {\n const result = await fetchAndExecuteAccountScopedPrompt(\n promptSlug,\n config,\n convexClient\n );\n\n // Convert prompt result to tool result format\n const promptText = result.messages\n .map((msg) => msg.content.text)\n .join('\\n\\n');\n\n return {\n content: [\n {\n type: \"text\",\n text: promptText,\n },\n ],\n };\n } catch (error) {\n // Handle ambiguous prompt matches with helpful suggestions\n if (error instanceof AmbiguousPromptError) {\n const suggestionsList = error.suggestions.map((s) => ` • ${s}`).join(\"\\n\");\n console.error(`[MCP] ${toolName} ambiguous match:`, error.suggestions);\n return {\n content: [\n {\n type: \"text\",\n text: `Multiple prompts match \"${promptSlug}\". Please specify one of:\\n\\n${suggestionsList}\\n\\nExample: \\`system_run_prompt ${error.suggestions[0]}\\``,\n },\n ],\n isError: true,\n };\n }\n\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] ${toolName} error:`, error);\n return {\n content: [\n {\n type: \"text\",\n text: `Error executing prompt \"${promptSlug}\": ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n }\n\n // Handle global system_prompts tool (account-scoped prompt listing)\n // Uses cached metadata (lightweight) - no need to fetch full content for listing\n if (toolName === \"system_prompts\") {\n const searchTerm = request.params.arguments?.search as string | undefined;\n\n // Use account-scoped prompts (global, no workspace filter)\n let filteredPrompts = [...accountScopedPrompts];\n\n // Filter by search term if provided\n if (searchTerm) {\n const lowerSearch = searchTerm.toLowerCase();\n filteredPrompts = filteredPrompts.filter(\n (p) =>\n p.slug.toLowerCase().includes(lowerSearch) ||\n p.description?.toLowerCase().includes(lowerSearch)\n );\n }\n\n // Sort prompts by slug\n filteredPrompts.sort((a, b) => a.slug.localeCompare(b.slug));\n\n // Build user prompts list\n const userPromptList = filteredPrompts\n .map((p) => {\n const desc = p.description || \"No description\";\n return `• ${p.slug}\\n Description: ${desc}`;\n })\n .join(\"\\n\\n\");\n\n const summary = searchTerm\n ? `Found ${filteredPrompts.length} prompt(s) matching \"${searchTerm}\":`\n : `Available prompts (${filteredPrompts.length} total):`;\n\n return {\n content: [\n {\n type: \"text\",\n text: userPromptList\n ? `${summary}\\n\\n${userPromptList}`\n : `${summary}\\n\\nNo prompts found.`,\n },\n ],\n };\n }\n\n // Handle ticket tool invocations (Ticket 135, 153, unified work)\n const ticketTool = dynamicTicketTools.find((tt) => tt.name === toolName);\n if (ticketTool) {\n if (ticketTool.type === 'work') {\n // Unified work command (replaces both open and work)\n const ticketSlug = request.params.arguments?.ticketSlug as string | undefined;\n\n try {\n // Call mutation (not query) - this can move ticket from open to working\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const result = await (convexClient as any).mutation(\n \"mcp_tickets:workMcpTicket\",\n {\n ...buildAuthArgs(config),\n workspaceSlug: ticketTool.workspaceSlug,\n ticketSlug: ticketSlug, // Optional: specific ticket to work on\n }\n ) as {\n slug: string;\n ticketNumber?: number;\n status: string;\n content: string;\n startedAt?: number;\n remainingTickets: number;\n wasOpened: boolean;\n } | { error: string; accessDenied: true } | null;\n\n if (!result) {\n const message = ticketSlug\n ? `Ticket \"${ticketSlug}\" not found or not in open/working status in workspace \"${ticketTool.workspaceSlug}\".`\n : `No open tickets in workspace \"${ticketTool.workspaceSlug}\".`;\n return {\n content: [\n {\n type: \"text\",\n text: message,\n },\n ],\n };\n }\n\n // Handle access denied response\n if ('accessDenied' in result && result.accessDenied) {\n return {\n content: [\n {\n type: \"text\",\n text: `Access denied: ${result.error}`,\n },\n ],\n isError: true,\n };\n }\n\n const startedInfo = result.startedAt\n ? `\\nStarted: ${new Date(result.startedAt).toISOString()}`\n : '';\n\n // Different footer based on whether this was a fresh open or resumption\n const statusNote = result.wasOpened\n ? `_Ticket moved to working status. ${result.remainingTickets} ticket(s) remaining in queue._`\n : `_Resuming work on this ticket. ${result.remainingTickets} ticket(s) in queue._`;\n\n return {\n content: [\n {\n type: \"text\",\n text: `# Ticket: ${result.slug} [WORKING]${startedInfo}\n\n${result.content}\n\n---\n${statusNote}`,\n },\n ],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] tickets_work error:`, error);\n return {\n content: [\n {\n type: \"text\",\n text: `Error getting work: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n } else if (ticketTool.type === 'create') {\n // Handle tickets:create (Ticket 149)\n const 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 ...buildAuthArgs(config),\n workspaceSlug: ticketTool.workspaceSlug,\n content,\n }\n ) as { slug: string; preview: string; position: number; totalBacklog: number };\n\n return {\n content: [\n {\n type: \"text\",\n text: `✅ Created ticket [${result.slug}] in backlog for workspace \"${ticketTool.workspaceSlug}\".\n\nPosition: #${result.position} of ${result.totalBacklog} backlog tickets\nPreview: ${result.preview}\n\n_Ticket created in backlog. Use \\`tickets_work ${result.slug}\\` to move it to working status._`,\n },\n ],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] tickets_create error:`, error);\n return {\n content: [\n {\n type: \"text\",\n text: `Error creating ticket: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n } else if (ticketTool.type === 'close') {\n // Ticket 151: Handle tickets:close\n const 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 ...buildAuthArgs(config),\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_Reminder: Ensure all embedded \\`[RUN_PROMPT ...]\\` directives were executed before closing._`,\n },\n ],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] tickets_close error:`, error);\n return {\n content: [\n {\n type: \"text\",\n text: `Error closing ticket: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n } else if (ticketTool.type === 'search') {\n // Ticket 155: Handle tickets:search\n const query = request.params.arguments?.query as string | undefined;\n\n if (!query || query.length < 3) {\n return {\n content: [\n {\n type: \"text\",\n text: `Error: Search query must be at least 3 characters`,\n },\n ],\n isError: true,\n };\n }\n\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const result = await (convexClient as any).query(\n \"mcp_tickets:searchMcpTickets\",\n {\n ...buildAuthArgs(config),\n workspaceSlug: ticketTool.workspaceSlug,\n query,\n }\n ) as Array<{ slug: string; ticketNumber?: number; status: string; matchSnippet: string; createdAt: number }>;\n\n if (result.length === 0) {\n return {\n content: [\n {\n type: \"text\",\n text: `No tickets found matching \"${query}\" in workspace \"${ticketTool.workspaceSlug}\".`,\n },\n ],\n };\n }\n\n // Format as list with status badges\n const formattedList = result\n .map((t) => {\n const statusBadge = t.status === 'open' ? '🟢' : t.status === 'working' ? '🟡' : t.status === 'closed' ? '⚫' : '⚪';\n const num = t.ticketNumber ? `#${t.ticketNumber}` : '';\n return `${statusBadge} ${num} ${t.slug}\\n ${t.matchSnippet}`;\n })\n .join('\\n\\n');\n\n return {\n content: [\n {\n type: \"text\",\n text: `Found ${result.length} ticket(s) matching \"${query}\":\\n\\n${formattedList}`,\n },\n ],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] tickets_search error:`, error);\n return {\n content: [\n {\n type: \"text\",\n text: `Error searching tickets: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n } else if (ticketTool.type === 'get') {\n // Ticket 155: Handle tickets:get (read-only inspection)\n const 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. Provide a ticket number (e.g., \"102\") or full slug.`,\n },\n ],\n isError: true,\n };\n }\n\n try {\n // Uses existing getMcpTicket query - read-only inspection\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const result = await (convexClient as any).query(\n \"mcp_tickets:getMcpTicket\",\n {\n ...buildAuthArgs(config),\n workspaceSlug: ticketTool.workspaceSlug,\n ticketSlug,\n }\n ) as { slug: string; ticketNumber?: number; content: string; status: string; createdAt: number; startedAt?: number; closedAt?: number; assignedTo?: string } | { error: string; accessDenied: true } | 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 // Handle access denied response\n if ('accessDenied' in result && result.accessDenied) {\n return {\n content: [\n {\n type: \"text\",\n text: `Access denied: ${result.error}`,\n },\n ],\n isError: true,\n };\n }\n\n // Build status line\n const statusBadge = result.status.toUpperCase();\n const startedInfo = result.startedAt ? `\\nStarted: ${new Date(result.startedAt).toISOString()}` : '';\n const closedInfo = result.closedAt ? `\\nClosed: ${new Date(result.closedAt).toISOString()}` : '';\n const assignedInfo = result.assignedTo ? `\\nAssigned to: ${result.assignedTo}` : '\\nUnassigned';\n\n return {\n content: [\n {\n type: \"text\",\n text: `# Ticket: ${result.slug} [${statusBadge}]${startedInfo}${closedInfo}${assignedInfo}\n\n${result.content}\n\n---\n_Read-only inspection. Use tickets_work to start working on this ticket._`,\n },\n ],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] tickets_get error:`, error);\n return {\n content: [\n {\n type: \"text\",\n text: `Error getting ticket: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n } else if (ticketTool.type === 'list') {\n // Ticket 155: Handle tickets:list (active queue)\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const result = await (convexClient as any).query(\n \"mcp_tickets:listMcpTickets\",\n {\n ...buildAuthArgs(config),\n workspaceSlug: ticketTool.workspaceSlug,\n }\n ) as Array<{ position: number; slug: string; ticketNumber?: number; status: string; preview: string; createdAt: number; startedAt?: number }>;\n\n if (result.length === 0) {\n return {\n content: [\n {\n type: \"text\",\n text: `No active tickets in workspace \"${ticketTool.workspaceSlug}\". All tickets are closed or archived.`,\n },\n ],\n };\n }\n\n // Group by status for clear display\n const openTickets = result.filter(t => t.status === 'open');\n const workingTickets = result.filter(t => t.status === 'working');\n const backlogTickets = result.filter(t => t.status === 'backlog');\n\n const formatTicketLine = (t: typeof result[0]) => {\n const num = t.ticketNumber ? `#${t.ticketNumber}` : '';\n return ` ${t.position}. ${num} ${t.slug}\\n ${t.preview}`;\n };\n\n const sections: string[] = [];\n\n if (openTickets.length > 0) {\n sections.push(`**🟢 Open (${openTickets.length})**\\n${openTickets.map(formatTicketLine).join('\\n')}`);\n }\n if (workingTickets.length > 0) {\n sections.push(`**🟡 Working (${workingTickets.length})**\\n${workingTickets.map(formatTicketLine).join('\\n')}`);\n }\n if (backlogTickets.length > 0) {\n sections.push(`**⚪ Backlog (${backlogTickets.length})**\\n${backlogTickets.map(formatTicketLine).join('\\n')}`);\n }\n\n return {\n content: [\n {\n type: \"text\",\n text: `# Active Queue: ${ticketTool.workspaceSlug}\\n\\n${result.length} ticket(s) in queue\\n\\n${sections.join('\\n\\n')}`,\n },\n ],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] tickets_list error:`, error);\n return {\n content: [\n {\n type: \"text\",\n text: `Error listing tickets: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n } else if (ticketTool.type === 'update') {\n // Ticket 293: Handle tickets:update (append content to ticket)\n const ticketSlug = request.params.arguments?.ticketSlug as string | undefined;\n const content = request.params.arguments?.content as string | undefined;\n\n if (!ticketSlug) {\n return {\n content: [\n {\n type: \"text\",\n text: `Error: Missing ticketSlug parameter. Provide a ticket number (e.g., \"102\") or full slug.`,\n },\n ],\n isError: true,\n };\n }\n\n if (!content) {\n return {\n content: [\n {\n type: \"text\",\n text: `Error: Missing content parameter. Provide the update content to append to the ticket.`,\n },\n ],\n isError: true,\n };\n }\n\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const result = await (convexClient as any).mutation(\n \"mcp_tickets:updateMcpTicket\",\n {\n ...buildAuthArgs(config),\n workspaceSlug: ticketTool.workspaceSlug,\n ticketSlug,\n content,\n }\n ) as UpdateTicketResult;\n\n return {\n content: [\n {\n type: \"text\",\n text: `✅ Ticket [${result.slug}] updated in workspace \"${ticketTool.workspaceSlug}\".\nUpdated at: ${new Date(result.updatedAt).toISOString()}\n_Ticket content has been appended with your update._`,\n },\n ],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] tickets_update error:`, error);\n return {\n content: [\n {\n type: \"text\",\n text: `Error updating ticket: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n }\n }\n\n // Handle per-prompt tool invocations (e.g., \"code-review\" - now global)\n const promptTool = dynamicPromptTools.find((pt) => pt.name === toolName);\n if (promptTool) {\n try {\n const result = await fetchAndExecuteAccountScopedPrompt(\n promptTool.promptSlug,\n config,\n convexClient\n );\n\n const promptText = result.messages\n .map((msg) => msg.content.text)\n .join('\\n\\n');\n\n return {\n content: [{ type: \"text\", text: promptText }],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] ${toolName} error:`, error);\n return {\n content: [{ type: \"text\", text: `Error executing prompt: ${errorMessage}` }],\n isError: true,\n };\n }\n }\n\n // Unknown tool\n throw new Error(`Unknown tool: ${toolName}. Use system_run_prompt to execute prompts by name, or check available tools.`);\n });\n\n // Start server with stdio transport\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n console.error(\"[MCP] Server started successfully\");\n console.error(`[MCP] Deployment: ${config.isDev ? \"DEVELOPMENT\" : \"PRODUCTION\"}`);\n console.error(`[MCP] Convex URL: ${config.convexUrl}`);\n console.error(`[MCP] Data mode: REAL-TIME (fetches fresh data on each invocation)`);\n\n // List all prompts\n const allPromptNames = [...Array.from(promptNames)].sort();\n console.error(`[MCP] Prompts available: ${allPromptNames.join(\", \")}`);\n console.error(`[MCP] - Total prompts: ${promptNames.size}`);\n\n // Keep the event loop alive with a heartbeat\n // This prevents Node from exiting when there are no active handles\n setInterval(() => {\n // Heartbeat every 60 seconds to keep process alive\n }, 60000);\n\n // Return a promise that never resolves to keep the server running\n return new Promise<void>(() => {\n // The transport handles stdin/stdout communication\n // The interval above keeps the event loop active\n });\n}\n\n/**\n * Validate 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 * Validate workspace token with Convex\n *\n * Workspace tokens provide scoped access to a single workspace for ticket operations.\n * Returns workspace details if valid, error if invalid/revoked.\n */\nasync function validateWorkspaceToken(\n client: ConvexHttpClient,\n token: string\n): Promise<WorkspaceTokenValidation> {\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const result = await client.query(\"workspace_tokens:validateWorkspaceToken\" as any, { token });\n if (result && result.valid) {\n return {\n valid: true,\n workspaceId: result.workspaceId,\n workspaceSlug: result.workspaceSlug,\n ownerId: result.ownerId,\n tokenId: result.tokenId,\n tokenName: result.tokenName,\n };\n }\n return { valid: false, error: \"Invalid or revoked workspace token\" };\n } catch (error) {\n return {\n valid: false,\n error: error instanceof Error ? error.message : \"Unknown error\",\n };\n }\n}\n\n// Note: fetchMcpPromptMetadata and fetchMcpPromptMetadataByToken removed - prompts now account-scoped\n// Note: fetchMcpCommands and buildCommandName removed - commands resource eliminated\n\n/**\n * Fetch account-scoped prompt metadata for global MCP tools\n * Returns prompts WITHOUT workspaceId - globally available\n */\nasync function fetchAccountScopedPromptMetadata(\n client: ConvexHttpClient,\n apiKey: string\n): Promise<Array<{ slug: string; description?: string }>> {\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const metadata = await client.query(\"mcp_prompts:getAccountScopedPromptMetadata\" as any, {\n apiKey,\n });\n return metadata;\n } catch (error) {\n throw new Error(\n `Failed to fetch account-scoped prompt metadata: ${error instanceof Error ? error.message : \"Unknown error\"}`\n );\n }\n}\n\n/**\n * Fetch user's workspaces for ticket tool registration\n * Separate from prompts since prompts are now global\n */\nasync function fetchMcpWorkspaces(\n client: ConvexHttpClient,\n apiKey: string\n): Promise<Array<{ slug: string; description?: string }>> {\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const workspaces = await client.query(\"mcp_prompts:getMcpWorkspaces\" as any, {\n apiKey,\n });\n return workspaces;\n } catch (error) {\n throw new Error(\n `Failed to fetch workspaces: ${error instanceof Error ? error.message : \"Unknown error\"}`\n );\n }\n}\n","import type { ConvexHttpClient } from \"convex/browser\";\nimport type { McpPrompt, McpPromptMetadata, PromptMatchResult, 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 name from lightweight metadata (no flattenedPrompt)\n * Used for startup/tool registration when only metadata is available\n */\nexport function buildPromptNameFromMetadata(metadata: McpPromptMetadata): string {\n const workspace = sanitizeForMcp(metadata.workspaceSlug || 'default') || 'default';\n const promptSlug = sanitizeForMcp(metadata.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 * @deprecated Use buildPromptHandlerOptimized for better performance\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\n/**\n * Custom error class for ambiguous prompt matches\n * Contains suggestions for the user to choose from\n */\nexport class AmbiguousPromptError extends Error {\n suggestions: string[];\n\n constructor(promptSlug: string, suggestions: string[]) {\n super(`Multiple prompts match \"${promptSlug}\". Please be more specific.`);\n this.name = \"AmbiguousPromptError\";\n this.suggestions = suggestions;\n }\n}\n\n/**\n * Fetch a single prompt and execute it - O(1) index lookup with fuzzy fallback\n *\n * Supports both API key and workspace token authentication:\n * - API key: Full access to all user workspaces\n * - Workspace token: Scoped access to token's workspace only\n *\n * Matching algorithm:\n * 1. Exact match via index (O(1)) - fast path\n * 2. Prefix match fallback if exact fails\n * 3. Contains match fallback if prefix fails\n * 4. Ambiguous error if multiple matches found\n *\n * Performance optimization: Uses getMcpPromptBySlug for direct index lookup\n * with fallback to fuzzy matching only when exact match fails.\n */\nexport async function fetchAndExecutePrompt(\n workspaceSlug: string,\n promptSlug: string,\n config: ServerConfig,\n convexClient: ConvexHttpClient\n): Promise<{ messages: Array<{ role: string; content: { type: \"text\"; text: string } }> }> {\n let result: PromptMatchResult;\n\n // Use appropriate query based on auth type\n if (config.apiKey) {\n // API key auth - use standard query\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n result = await convexClient.query(\"mcp_prompts:getMcpPromptBySlug\" as any, {\n apiKey: config.apiKey,\n workspaceSlug,\n promptSlug,\n }) as PromptMatchResult;\n } else if (config.workspaceToken) {\n // Workspace token auth - use token-specific query\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n result = await convexClient.query(\"mcp_prompts:getMcpPromptBySlugWithToken\" as any, {\n workspaceToken: config.workspaceToken,\n workspaceSlug,\n promptSlug,\n }) as PromptMatchResult;\n } else {\n throw new Error(\"No authentication configured\");\n }\n\n // Handle null - no matches found\n if (!result) {\n throw new Error(\n `Prompt \"${promptSlug}\" not found in workspace \"${workspaceSlug}\".`\n );\n }\n\n // Handle ambiguous matches - throw custom error with suggestions\n if (result.matchType === \"ambiguous\") {\n throw new AmbiguousPromptError(promptSlug, result.suggestions);\n }\n\n // Handle successful match (exact, prefix, or contains)\n if (!result.flattenedPrompt) {\n throw new Error(\n `Prompt \"${result.slug}\" has no flattened content. Please re-save the prompt to regenerate it.`\n );\n }\n\n // Log match type for debugging\n const matchNote = result.matchType === \"exact\"\n ? \"exact match\"\n : result.matchType === \"prefix\"\n ? `prefix match → ${result.slug}`\n : `contains match → ${result.slug}`;\n console.error(`[MCP] Fetched prompt: ${workspaceSlug}:${promptSlug} (${matchNote})`);\n\n // Return raw flattened prompt (client will parse YAML front matter)\n return {\n messages: [\n {\n role: \"user\",\n content: {\n type: \"text\" as const,\n text: result.flattenedPrompt,\n },\n },\n ],\n };\n}\n\n/**\n * Type for account-scoped prompt match result\n */\ntype AccountScopedPromptResult =\n | { matchType: \"exact\" | \"prefix\" | \"contains\"; slug: string; description?: string; flattenedPrompt?: string }\n | { matchType: \"ambiguous\"; suggestions: string[] }\n | null;\n\n/**\n * Fetch a single account-scoped prompt and execute it\n *\n * Account-scoped prompts are global (no workspace prefix required).\n * This is for prompts that don't have a workspaceId.\n *\n * Matching algorithm same as fetchAndExecutePrompt but without workspace context.\n */\nexport async function fetchAndExecuteAccountScopedPrompt(\n promptSlug: string,\n config: ServerConfig,\n convexClient: ConvexHttpClient\n): Promise<{ messages: Array<{ role: string; content: { type: \"text\"; text: string } }> }> {\n if (!config.apiKey) {\n throw new Error(\"Account-scoped prompts require API key authentication\");\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const result = await convexClient.query(\"mcp_prompts:getAccountScopedPromptBySlug\" as any, {\n apiKey: config.apiKey,\n promptSlug,\n }) as AccountScopedPromptResult;\n\n // Handle null - no matches found\n if (!result) {\n throw new Error(\n `Prompt \"${promptSlug}\" not found. Use system:prompts to list available prompts.`\n );\n }\n\n // Handle ambiguous matches - throw custom error with suggestions\n if (result.matchType === \"ambiguous\") {\n throw new AmbiguousPromptError(promptSlug, result.suggestions);\n }\n\n // Handle successful match (exact, prefix, or contains)\n if (!result.flattenedPrompt) {\n throw new Error(\n `Prompt \"${result.slug}\" has no flattened content. Please re-save the prompt to regenerate it.`\n );\n }\n\n // Log match type for debugging\n const matchNote = result.matchType === \"exact\"\n ? \"exact match\"\n : result.matchType === \"prefix\"\n ? `prefix match → ${result.slug}`\n : `contains match → ${result.slug}`;\n console.error(`[MCP] Fetched account-scoped prompt: ${promptSlug} (${matchNote})`);\n\n // Return raw flattened prompt (client will parse YAML front matter)\n return {\n messages: [\n {\n role: \"user\",\n content: {\n type: \"text\" as const,\n text: result.flattenedPrompt,\n },\n },\n ],\n };\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED