@promptprojectmanager/mcp-server 4.0.2 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,511 +1,317 @@
1
1
  # @promptprojectmanager/mcp-server
2
2
 
3
- MCP server that exposes Prompt Project Manager workspace prompts through dual interfaces: **Prompts** (slash commands) and **Tools** (AI-discoverable).
3
+ MCP server that exposes Prompt Project Manager prompts and tickets through dual interfaces: **Prompts** (slash commands) and **Tools** (AI-discoverable).
4
4
 
5
5
  ## Overview
6
6
 
7
- `@promptprojectmanager/mcp-server` is a standalone npm package that integrates your Prompt Project Manager workspace with Claude Code via the Model Context Protocol (MCP). It exposes your prompts through **two complementary interfaces**:
7
+ `@promptprojectmanager/mcp-server` is a standalone npm package that integrates your Prompt Project Manager workspace with Claude Code, Codex CLI, and Gemini CLI via the Model Context Protocol (MCP).
8
8
 
9
- 1. **Prompts Interface** (Slash Commands) - Explicitly invoke prompts via `/ppm:workspace:prompt` commands
10
- 2. **Tools Interface** (AI Discovery) - AI assistants can discover and invoke prompts naturally through conversation
9
+ ### Two Authentication Modes
11
10
 
12
- Both interfaces use the same underlying prompts and return identical flattened content. Choose explicit commands when you know exactly which prompt to use, or let the AI discover the right prompt for your task.
11
+ | Mode | Env Variable | Scope | Use Case |
12
+ |------|--------------|-------|----------|
13
+ | **API Key** | `PPM_API_KEY` | Full account access | Multi-project workflows, all prompts |
14
+ | **Project Token** | `PPM_PROJECT_TOKEN` | Single project, tickets only | CI/CD, agents, scoped access |
13
15
 
14
- ## Features
15
-
16
- - 🔐 **Secure Authentication** - Uses API keys from your Prompt Project Manager Settings page
17
- - 🎯 **Selective Exposure** - Only prompts with "Expose to MCP" enabled appear
18
- - 🏢 **Workspace Scoping** - Commands namespaced by workspace (e.g., `personal-prompt`, `work-prompt`)
19
- - 🤖 **Dual Interface** - Both slash commands AND AI-discoverable tools
20
- - ⚡ **Zero Configuration** - Works out of the box with `npx`
21
- - 📝 **Flattened Content** - Returns pre-computed prompts with all resources embedded
22
- - 🔄 **Flexible Caching** - Choose between cached (fast) or lazy loading (always fresh) modes
23
-
24
- ## Installation
25
-
26
- No installation required! Use `npx` to run the server directly:
27
-
28
- ```bash
29
- npx @promptprojectmanager/mcp-server
30
- ```
31
-
32
- ## Configuration
33
-
34
- ### Quick Start (Recommended)
16
+ ### Two Interfaces
35
17
 
36
- **The easiest way to configure the MCP server is through Prompt Project Manager Settings page**:
18
+ 1. **Prompts Interface** (Slash Commands) - Explicitly invoke prompts via `/ppm:project:prompt` commands
19
+ 2. **Tools Interface** (AI Discovery) - AI assistants can discover and invoke prompts/tickets naturally
37
20
 
38
- 1. Visit https://promptprojectmanager.com/settings
39
- 2. Scroll to "MCP Server Configuration" section
40
- 3. Select your CLI tool (Claude Code, Codex CLI, or Gemini CLI)
41
- 4. Toggle "Lazy Loading" ON or OFF based on your preference
42
- 5. Click "Copy Configuration" button
43
- 6. Paste the configuration into your CLI's settings file
44
-
45
- The settings page automatically generates the correct format (JSON or TOML) with your API key pre-filled and `@latest` package versioning. See [Multi-CLI Support](#multi-cli-support) below for details on each CLI tool.
46
-
47
- ### Manual Configuration
48
-
49
- If you prefer to configure manually:
50
-
51
- **Step 1: Get Your API Key**
21
+ ## Features
52
22
 
53
- 1. Visit your Prompt Project Manager Settings page: https://promptprojectmanager.com/settings
54
- 2. Your API key is auto-generated and displayed in the "API Keys" section
55
- 3. Copy your API key (starts with `api_key_`)
23
+ - **Dual Authentication** - API keys for full access, project tokens for scoped access
24
+ - **Project Filtering** - Use `--projects` flag to limit scope (API key mode only)
25
+ - **Ticket Management** - Create, update, close tickets via MCP tools
26
+ - **Agent Identity** - Use `PPM_AGENT` to identify which agent is making requests
27
+ - **Selective Exposure** - Only prompts with "Expose to MCP" enabled appear
28
+ - **Zero Configuration** - Works out of the box with `npx`
29
+ - **Flexible Caching** - Choose between cached (fast) or lazy loading (always fresh) modes
56
30
 
57
- **Step 2: Configure Your CLI Tool**
31
+ ## Quick Start
58
32
 
59
- #### Claude Code (JSON)
33
+ ### Option 1: Project Token (Recommended for Agents)
60
34
 
61
- Add to your Claude Code MCP settings:
35
+ Project tokens are scoped to a single project and provide ticket access. **No `--projects` flag needed** - the token already knows which project it belongs to.
62
36
 
63
37
  ```json
64
38
  {
65
39
  "mcpServers": {
66
- "tpe": {
40
+ "ppm": {
67
41
  "command": "npx",
68
42
  "args": ["@promptprojectmanager/mcp-server@latest"],
69
43
  "env": {
70
- "PPM_API_KEY": "your_api_key_here"
44
+ "PPM_PROJECT_TOKEN": "wst_your_token_here"
71
45
  }
72
46
  }
73
47
  }
74
48
  }
75
49
  ```
76
50
 
77
- #### Codex CLI (TOML)
51
+ Get your project token from: Project → Agents → Create/View Agent
78
52
 
79
- Add to `~/.codex/config.toml` in the `[mcp_servers]` section:
53
+ ### Option 2: API Key (Full Account Access)
80
54
 
81
- ```toml
82
- [mcp_servers.tpe]
83
- command = "npx"
84
- args = ["@promptprojectmanager/mcp-server@latest"]
85
-
86
- [mcp_servers.tpe.env]
87
- PPM_API_KEY = "your_api_key_here"
88
- ```
89
-
90
- #### Gemini CLI (JSON)
91
-
92
- Add to `~/.gemini/settings.json` in the `mcpServers` object:
55
+ API keys provide access to all projects and prompts. Use `--projects` to filter.
93
56
 
94
57
  ```json
95
58
  {
96
59
  "mcpServers": {
97
- "tpe": {
60
+ "ppm": {
98
61
  "command": "npx",
99
- "args": ["@promptprojectmanager/mcp-server@latest"],
62
+ "args": ["@promptprojectmanager/mcp-server@latest", "--projects", "my-project"],
100
63
  "env": {
101
- "PPM_API_KEY": "your_api_key_here"
64
+ "PPM_API_KEY": "api_key_your_key_here"
102
65
  }
103
66
  }
104
67
  }
105
68
  }
106
69
  ```
107
70
 
108
- **With Lazy Loading:**
109
-
110
- Add `--lazy-loading` to the args array for any CLI:
111
-
112
- ```json
113
- "args": ["@promptprojectmanager/mcp-server@latest", "--lazy-loading"]
114
- ```
115
-
116
- See [Caching Modes](#caching-modes) below to choose which configuration is right for you.
117
-
118
- ### Step 3: Enable Prompts for MCP
71
+ Get your API key from: Settings → API Keys
119
72
 
120
- 1. Open any prompt in your ContextFS workspace
121
- 2. Check the "Expose to MCP" checkbox in the prompt editor
122
- 3. Save the prompt
123
- 4. Restart Claude Code to refresh available tools
73
+ ## Authentication
124
74
 
125
- ## Two Ways to Use Prompts
75
+ ### Project Token Mode (`PPM_PROJECT_TOKEN`)
126
76
 
127
- ### 1. Prompts Interface (Slash Commands)
77
+ Best for: CI/CD pipelines, automated agents, scoped access
128
78
 
129
- Explicitly invoke prompts by typing slash commands. Great when you know exactly which prompt you need.
79
+ - **Scope**: Single project (determined by token)
80
+ - **Access**: Tickets only (prompts not available)
81
+ - **Flag**: `--projects` is **ignored** (token already scoped)
130
82
 
131
- **Usage:**
132
- ```
133
- User: /ppm:personal-code-review
134
- User: /ppm:work-api-design
135
- User: /ppm:personal-technical-analysis
83
+ ```bash
84
+ # Token is already scoped - no --projects needed
85
+ PPM_PROJECT_TOKEN=wst_xxx npx @promptprojectmanager/mcp-server@latest
136
86
  ```
137
87
 
138
- **Pattern:**
139
- - Format: `/ppm:workspace-prompt`
140
- - Example: workspace slug "personal" + prompt slug "code-review" = `/ppm:personal-code-review`
88
+ ### API Key Mode (`PPM_API_KEY`)
141
89
 
142
- **Note:**
143
- - MCP protocol requires tool names to use only `[a-zA-Z0-9_-]` characters
144
- - Hyphens (`-`) are used as separators between workspace and prompt
145
- - **Folders are NOT included in slash commands** (only used for ZIP download organization)
90
+ Best for: Development, multi-project workflows, full prompt access
146
91
 
147
- ### 2. Tools Interface (AI Discovery)
92
+ - **Scope**: All projects in your account
93
+ - **Access**: Prompts + Tickets
94
+ - **Flag**: Use `--projects` to filter
148
95
 
149
- Let AI assistants discover and invoke prompts naturally through conversation. Great for exploratory workflows.
96
+ ```bash
97
+ # Access all projects
98
+ PPM_API_KEY=api_key_xxx npx @promptprojectmanager/mcp-server@latest
150
99
 
151
- **Usage:**
152
- ```
153
- User: "Help me review this code"
154
- AI: [Discovers and invokes code-review tool]
100
+ # Filter to specific projects
101
+ PPM_API_KEY=api_key_xxx npx @promptprojectmanager/mcp-server@latest --projects myproject
155
102
 
156
- User: "I need to plan a new feature"
157
- AI: [Discovers and invokes planning tool from appropriate folder]
103
+ # Multiple projects
104
+ PPM_API_KEY=api_key_xxx npx @promptprojectmanager/mcp-server@latest --projects proj1,proj2
158
105
  ```
159
106
 
160
- **How it works:**
161
- - Each prompt appears as a discoverable tool
162
- - AI reads tool descriptions to match user intent
163
- - AI invokes the appropriate tool automatically
164
- - Same flattened content as slash commands
107
+ ### Agent Identity (`PPM_AGENT`)
165
108
 
166
- ## Command Naming Convention
109
+ When using API key auth, you can specify which agent identity to use for ticket operations:
167
110
 
168
- The MCP server uses a hierarchical workspace-scoped naming convention for slash commands.
169
-
170
- ### Format
171
-
172
- **Pattern:** `workspace:prompt`
173
-
174
- All commands follow this two-part format:
175
- 1. Workspace slug (e.g., `personal`, `work`)
176
- 2. Prompt slug (e.g., `code-review`, `api-design`)
177
- 3. Separated by a colon (`:`)
178
-
179
- ### Examples
180
-
181
- **Command structure:**
182
- - `/ppm:personal:code-review` → workspace: `personal`, prompt: `code-review`
183
- - `/ppm:work:api-design` → workspace: `work`, prompt: `api-design`
184
- - `/ppm:personal:technical-analysis` → workspace: `personal`, prompt: `technical-analysis`
185
-
186
- **Character rules:**
187
- - MCP protocol allows: `[a-zA-Z0-9_:-]`
188
- - Hierarchy separator: `:` (colon) between workspace and prompt
189
- - Word separator: `-` (hyphen) within multi-word slugs
190
- - The `/ppm:` prefix is added by Claude Code (MCP namespace)
191
- - The actual tool name is `workspace:prompt` (e.g., `personal:code-review`)
192
-
193
- ### Folder Organization
194
-
195
- **Important:** Folders are NOT included in slash command names.
196
-
197
- - Folders are used only for ZIP download organization
198
- - Multiple prompts with the same slug but different folders will have identical slash commands
199
- - To avoid conflicts, ensure prompt slugs are unique across your entire workspace
200
-
201
- ## Caching Modes
202
-
203
- The MCP server supports two caching modes to fit different workflows. Choose based on how frequently you update your prompts:
204
-
205
- ### Cached Mode (Default)
111
+ ```bash
112
+ PPM_API_KEY=api_key_xxx PPM_AGENT=my-agent npx @promptprojectmanager/mcp-server@latest
113
+ ```
206
114
 
207
- **How it works:**
208
- - Prompts are fetched **once** when the MCP server starts
209
- - Tool executions use the cached `flattenedPrompt` content
210
- - Fast execution with minimal database queries
115
+ This identifies the agent in ticket history and audit logs.
211
116
 
212
- **Choose this if you:**
213
- - Have stable, infrequently-changed prompts
214
- - Want the fastest possible tool execution
215
- - Prefer minimal network overhead
216
- - Don't mind restarting Claude Code after editing prompts
117
+ ## CLI Arguments
217
118
 
218
- **Configuration:** Use basic config without any flags (see Step 2 above)
119
+ | Argument | Description | API Key | Project Token |
120
+ |----------|-------------|---------|---------------|
121
+ | `--projects`, `-p` | Filter to specific project(s) | ✅ Supported | ❌ Ignored (token scoped) |
122
+ | `--lazy-loading` | Fetch fresh prompts on each call | ✅ Supported | ✅ Supported |
123
+ | `--dev` | Use development Convex deployment | ✅ Supported | ✅ Supported |
219
124
 
220
- ### Lazy Loading Mode
125
+ ### Project Filtering Examples
221
126
 
222
- **How it works:**
223
- - Prompts are fetched **fresh from Convex on each tool invocation**
224
- - Always returns the latest `flattenedPrompt` content
225
- - Falls back to cached version on fetch errors
127
+ ```bash
128
+ # Single project
129
+ --projects myproject
226
130
 
227
- **Choose this if you:**
228
- - Actively iterate on prompt content frequently
229
- - Want changes to take effect immediately without restarts
230
- - Prefer convenience over maximum performance
231
- - Have a stable internet connection
131
+ # Multiple projects (comma-separated)
132
+ --projects proj1,proj2,proj3
232
133
 
233
- **Configuration:** Add `--lazy-loading` to the args array (see Step 2 above)
134
+ # Multiple --projects flags
135
+ --projects proj1 --projects proj2
234
136
 
235
- **Example workflow:**
236
- ```bash
237
- # With lazy loading enabled:
238
- # 1. Edit prompt in ContextFS web UI
239
- # 2. Save changes
240
- # 3. Run tool in Claude Code → sees new content immediately
241
- # 4. No Claude Code restart required!
137
+ # Short flag
138
+ -p myproject
242
139
  ```
243
140
 
244
- **Performance Note:** Lazy loading adds ~50-100ms per tool invocation for the database fetch. For most use cases, this overhead is negligible compared to Claude's processing time.
141
+ ## Available Tools
245
142
 
246
- ## Multi-CLI Support
143
+ ### Prompt Tools (API Key Mode Only)
247
144
 
248
- The MCP server works with multiple CLI tools. Each CLI has slightly different configuration requirements:
145
+ Each prompt with "Expose to MCP" enabled appears as a tool:
249
146
 
250
- ### Claude Code
147
+ - `project_prompt_slug` - Invoke a specific prompt
148
+ - `list_prompts` - List all available prompts
251
149
 
252
- **Configuration Location:** Claude Code settings UI (MCP Servers section)
253
- **Format:** JSON
254
- **Installation:** https://claude.com/claude-code
150
+ ### Ticket Tools (Both Modes)
255
151
 
256
- Claude Code is Anthropic's official AI-powered code editor with native MCP support. Configuration is managed through the settings UI.
152
+ Each project gets ticket management tools:
257
153
 
258
- ### Codex CLI
154
+ | Tool | Description |
155
+ |------|-------------|
156
+ | `project_tickets_work` | Get next ticket or open specific ticket |
157
+ | `project_tickets_close` | Mark a ticket as completed |
158
+ | `project_tickets_update` | Append content to a ticket |
159
+ | `project_tickets_create` | Create a new ticket |
160
+ | `project_tickets_list` | List active tickets |
161
+ | `project_tickets_get` | Get a specific ticket |
162
+ | `project_tickets_search` | Search tickets by content |
259
163
 
260
- **Configuration Location:** `~/.codex/config.toml` (paste into `[mcp_servers]` section)
261
- **Format:** TOML
262
- **Installation:** https://developers.openai.com/codex/mcp
164
+ ### System Tools
263
165
 
264
- OpenAI's command-line interface for AI-assisted development. Uses TOML format for configuration.
166
+ | Tool | Description |
167
+ |------|-------------|
168
+ | `system_prompts` | List all available prompts |
169
+ | `system_run_prompt` | Execute a prompt by slug |
265
170
 
266
- **Note:** When pasting the TOML configuration, add it to the existing `[mcp_servers]` section in your config file, or create the section if it doesn't exist. Don't replace your entire config file.
171
+ ## Configuration Examples
267
172
 
268
- ### Gemini CLI
173
+ ### Claude Code (JSON)
269
174
 
270
- **Configuration Location:** `~/.gemini/settings.json` (paste into `mcpServers` object)
271
- **Format:** JSON
272
- **Installation:** https://ai.google.dev/gemini-api/docs/cli
273
-
274
- Google's Gemini CLI for AI-powered development workflows. Uses the same JSON format as Claude Code.
175
+ **With Project Token:**
176
+ ```json
177
+ {
178
+ "mcpServers": {
179
+ "ppm": {
180
+ "command": "npx",
181
+ "args": ["@promptprojectmanager/mcp-server@latest"],
182
+ "env": {
183
+ "PPM_PROJECT_TOKEN": "wst_your_token"
184
+ }
185
+ }
186
+ }
187
+ }
188
+ ```
275
189
 
276
- **Note:** When pasting the JSON configuration, add the `tpe` object to the existing `mcpServers` object in your settings file, or create the object if it doesn't exist. Don't replace your entire settings file.
190
+ **With API Key + Project Filter:**
191
+ ```json
192
+ {
193
+ "mcpServers": {
194
+ "ppm": {
195
+ "command": "npx",
196
+ "args": ["@promptprojectmanager/mcp-server@latest", "--projects", "my-project"],
197
+ "env": {
198
+ "PPM_API_KEY": "api_key_xxx"
199
+ }
200
+ }
201
+ }
202
+ }
203
+ ```
277
204
 
278
- ### Recommended Approach
205
+ **With API Key + Agent Identity:**
206
+ ```json
207
+ {
208
+ "mcpServers": {
209
+ "ppm": {
210
+ "command": "npx",
211
+ "args": ["@promptprojectmanager/mcp-server@latest", "--projects", "my-project"],
212
+ "env": {
213
+ "PPM_API_KEY": "api_key_xxx",
214
+ "PPM_AGENT": "claude-code-agent"
215
+ }
216
+ }
217
+ }
218
+ }
219
+ ```
279
220
 
280
- For all CLI tools, we recommend using Prompt Project Manager Settings page (https://promptprojectmanager.com/settings) to generate your configuration:
221
+ ### Codex CLI (TOML)
281
222
 
282
- 1. Select your CLI tool from the dropdown
283
- 2. Toggle lazy loading based on your workflow
284
- 3. Click "Copy Configuration"
285
- 4. Paste into the appropriate config file for your CLI
223
+ ```toml
224
+ [mcp_servers.ppm]
225
+ command = "npx"
226
+ args = ["@promptprojectmanager/mcp-server@latest"]
286
227
 
287
- This ensures correct formatting, proper API key inclusion, and use of `@latest` versioning for automatic updates.
228
+ [mcp_servers.ppm.env]
229
+ PPM_PROJECT_TOKEN = "wst_your_token"
230
+ ```
288
231
 
289
- ## System Tools
232
+ ### Gemini CLI (JSON)
290
233
 
291
- In addition to your workspace prompts, the MCP server provides built-in system tools:
234
+ Same format as Claude Code.
292
235
 
293
- ### `list_prompts`
236
+ ## Caching Modes
294
237
 
295
- Lists all available prompts in alphabetical order by tool name. Optionally filter by search term.
238
+ ### Cached Mode (Default)
296
239
 
297
- **Parameters:**
298
- - `search` (optional, string): Filter prompts by name or description (case-insensitive)
240
+ - Prompts fetched **once** at startup
241
+ - Fast execution, minimal network
242
+ - Restart required after editing prompts
299
243
 
300
- **Examples:**
244
+ ### Lazy Loading Mode
301
245
 
302
- ```
303
- # List all available prompts
304
- User: "Use list_prompts to see what's available"
246
+ Add `--lazy-loading` to args:
305
247
 
306
- # Search for specific prompts
307
- User: "Use list_prompts with search 'review'"
308
- User: "Use list_prompts with search 'documentation'"
248
+ ```json
249
+ "args": ["@promptprojectmanager/mcp-server@latest", "--lazy-loading"]
309
250
  ```
310
251
 
311
- **Output Format:**
312
- - Shows tool name, bundle, and description for each prompt
313
- - Displays total count
314
- - Sorts results alphabetically by tool name
252
+ - Prompts fetched **fresh on each invocation**
253
+ - Changes appear immediately
254
+ - ~50-100ms overhead per call
315
255
 
316
256
  ## Environment Variables
317
257
 
318
258
  | Variable | Required | Description |
319
259
  |----------|----------|-------------|
320
- | `PPM_API_KEY` | Yes | Your Prompt Project Manager API key from Settings page |
321
-
322
- ## Usage Example
323
-
324
- Once configured, your prompts appear as tools in Claude Code:
325
-
326
- ```
327
- User: "Use the code-reviewer tool to review my changes"
328
-
329
- Claude Code: [Invokes development/code-reviewer tool]
330
- Tool Response: [Returns flattened prompt with all embedded resources]
331
- ```
260
+ | `PPM_API_KEY` | One of these | Full account access (prompts + tickets) |
261
+ | `PPM_PROJECT_TOKEN` | required | Scoped project access (tickets only) |
262
+ | `PPM_AGENT` | No | Agent identity for ticket operations (API key mode) |
263
+ | `THEPROMPTEDITOR_API_KEY` | No | Legacy alias for `PPM_API_KEY` |
332
264
 
333
265
  ## Troubleshooting
334
266
 
335
267
  ### No Tools Available
336
268
 
337
- **Problem**: Claude Code shows no tools from ContextFS
338
-
339
- **Solutions**:
340
- 1. Verify at least one prompt has "Expose to MCP" enabled
341
- 2. Check that your API key is correct in `.mcp.json`
342
- 3. Restart Claude Code to refresh the tool list
343
- 4. Check MCP server logs for authentication errors
344
-
345
- ### Invalid API Key
346
-
347
- **Problem**: Server fails to start with "Invalid API key" error
348
-
349
- **Solutions**:
350
- 1. Copy your API key exactly from Settings page
351
- 2. Ensure no extra spaces or quotes around the key
352
- 3. Regenerate your API key if it was recently changed
353
- 4. Verify the key is set in the `PPM_API_KEY` environment variable
354
-
355
- ### Missing Flattened Content
356
-
357
- **Problem**: Tool execution fails with "no flattened content" error
358
-
359
- **Solutions**:
360
- 1. Open the affected prompt in ContextFS
361
- 2. Make a minor edit (add a space, then remove it)
362
- 3. Save the prompt to trigger re-flattening
363
- 4. Restart the MCP server
364
-
365
- ### Stale Prompt Content
269
+ 1. Verify authentication: `PPM_API_KEY` or `PPM_PROJECT_TOKEN` is set
270
+ 2. For prompts: Ensure "Expose to MCP" is enabled
271
+ 3. Restart your CLI tool to refresh tools
366
272
 
367
- **Problem**: Prompt edits don't appear when running tools in Claude Code
273
+ ### Wrong Project Scope
368
274
 
369
- **Solutions**:
370
- 1. **Enable lazy loading**: Add `--lazy-loading` to the args array in your `.mcp.json` config to always fetch fresh content
371
- 2. **Or restart Claude Code**: If using cached mode, restart Claude Code to refresh the cached prompts
372
- 3. **Verify mode**: Check MCP server logs to see which caching mode is active (look for "Lazy loading: ENABLED" or "DISABLED")
275
+ - **Project token**: Token is pre-scoped, `--projects` is ignored
276
+ - **API key**: Use `--projects` flag to filter
373
277
 
374
- ## How It Works
278
+ ### Stale Content
375
279
 
376
- 1. **Startup**: Server validates your API key with Convex
377
- 2. **Tool Registration**: Fetches all prompts with `exposeToMcp: true`
378
- 3. **Tool Creation**: Builds MCP tool schemas with bundle-aware naming
379
- 4. **Execution**: Returns `flattenedPrompt` content when tools are invoked
380
- 5. **Logging**: Outputs diagnostic info to stderr (visible in Claude Code logs)
381
-
382
- ## Development
383
-
384
- ### Building from Source
385
-
386
- ```bash
387
- # Clone the repository
388
- git clone https://github.com/promptprojectmanager/promptprojectmanager
389
- cd promptprojectmanager/packages/mcp-server
390
-
391
- # Install dependencies
392
- npm install
393
-
394
- # Build the package
395
- npm run build
396
-
397
- # Test locally
398
- npm link
399
- PPM_API_KEY=your_key npx @promptprojectmanager/mcp-server
400
- ```
401
-
402
- ### Package Structure
403
-
404
- ```
405
- packages/mcp-server/
406
- ├── src/
407
- │ ├── index.ts # CLI entry point with argument parsing
408
- │ ├── server.ts # MCP server logic and tool registration
409
- │ ├── convex-client.ts # Convex HTTP client initialization
410
- │ ├── tool-builder.ts # Tool naming and schema generation
411
- │ └── types.ts # TypeScript type definitions
412
- ├── dist/ # Compiled output (generated)
413
- ├── package.json
414
- ├── tsconfig.json
415
- ├── tsup.config.ts # Build configuration
416
- └── README.md
417
- ```
280
+ 1. Enable `--lazy-loading` for immediate updates
281
+ 2. Or restart CLI tool to refresh cache
418
282
 
419
283
  ## Version History
420
284
 
421
- ### 2.0.6 (2025-11-16) - Bug Fix & Documentation Update
422
-
423
- **Bug Fixes:**
424
- - Fixed field name mismatch (`folderPath` vs `folder`) preventing proper data flow from backend
425
- - Removed folder-based command scoping (folders only used for ZIP downloads now)
426
- - Simplified to clean `workspace-prompt` format for all slash commands
427
-
428
- **Documentation Updates:**
429
- - Clarified that folders are NOT included in slash command names
430
- - Updated all examples to show simple `workspace-prompt` format
431
- - Added warning about slug uniqueness requirement across workspace
432
-
433
- **Impact:**
434
- - Simple, predictable command names
435
- - No breaking changes for existing users (folder scoping never actually worked)
436
-
437
- ### 2.0.2 (2025-11-06) - Character Sanitization Fix (DEPRECATED)
438
-
439
- **Note:** Folder-based scoping was never functional due to field name bug. See v2.0.6 for correct implementation.
440
-
441
- - Attempted to fix period (`.`) handling in folder paths
442
- - Example: `.claude/commands` → `claude-commands`
443
-
444
- ### 2.0.1 (2025-11-06) - MCP Validation Fix (DEPRECATED)
445
-
446
- **Note:** Folder-based scoping was never functional due to field name bug. See v2.0.6 for correct implementation.
447
-
448
- - Changed separators for MCP protocol compliance: colon (`:`) → underscore (`_`), slash (`/`) → hyphen (`-`)
449
- - Tool name pattern: `[a-zA-Z0-9_-]`
450
-
451
- ### 2.0.0 (2025-11-06) - Dual Interface Release (DEPRECATED)
452
-
453
- **Note:** Folder-based scoping was never functional due to field name bug. See v2.0.6 for correct implementation.
454
-
455
- **Working Feature:**
456
- - **Dual Interface Architecture** - Added Tools interface alongside Prompts interface
457
- - Each prompt appears as both a slash command AND an AI-discoverable tool
285
+ ### 4.1.0 (2025-01-15) - Documentation Update
458
286
 
459
- **Non-Functional Feature:**
460
- - Folder-based command scoping was documented but never worked due to backend field mismatch
461
- - Fixed in v2.0.3 with simplified workspace-prompt format
287
+ **Documentation:**
288
+ - Added comprehensive documentation for project token authentication
289
+ - Documented `--projects` flag behavior (ignored for project tokens)
290
+ - Added ticket tools reference
291
+ - Added `PPM_AGENT` environment variable documentation
292
+ - Updated examples for both auth modes
462
293
 
463
- ### 1.4.0 (2025-11-05)
464
- - **Breaking Change**: Namespace shortened from `theprompteditor` to `ppm`
465
- - Users must update `.mcp.json` server key to `"ppm"`
466
- - All slash commands now use `/ppm:workspace:prompt` format
467
- - Migration: One-line config change, see README for details
294
+ **No Code Changes** - Server already handled both auth modes correctly.
468
295
 
469
- ### 1.3.2 (2025-10-30)
470
- - **Breaking Change**: Removed `ppm:` prefix - tools now appear as `promptprojectmanager:workspace:prompt`
471
- - Simplified naming pattern to avoid double prefixing with MCP server name
296
+ ### 4.0.3 (2025-01-08) - Prompt Folders
472
297
 
473
- ### 1.3.1 (2025-10-30)
474
- - **Breaking Change**: Environment variable renamed from `CONTEXTFS_API_KEY` to `PPM_API_KEY`
475
- - Updated all documentation and error messages with new branding
298
+ - Added prompt folder support for organization
476
299
 
477
- ### 1.3.0 (2025-10-30)
478
- - **Breaking Change**: Tool naming pattern changed from `bundle/prompt` to workspace-scoped format
479
- - **New Feature**: Workspace scoping - prompts are now namespaced by workspace
480
- - Folders are now ignored in tool names (UI organization only)
481
- - Fixed lazy loading to match prompts by both slug AND workspace slug
482
- - Backend now includes workspace metadata in MCP prompt responses
483
- - Updated Convex backend URLs to current deployments
300
+ ### 4.0.0 (2024-12-XX) - Project Tokens & Tickets
484
301
 
485
- ### 1.1.0 (2025-10-20)
486
- - **New Feature**: Lazy loading mode (`--lazy-loading` flag)
487
- - Users can now choose between cached (fast) and lazy loading (always fresh) modes
488
- - Lazy loading fetches fresh prompt content on each invocation
489
- - Enhanced Settings page UI with two-option configuration
490
- - Updated documentation with user-centric caching mode guidance
302
+ - Added `PPM_PROJECT_TOKEN` authentication mode
303
+ - Added ticket management tools per project
304
+ - Added `--projects` flag for API key filtering
305
+ - Added `PPM_AGENT` for agent identity
491
306
 
492
- ### 1.0.1 (2025-10-20)
493
- - Documentation improvements
494
- - Bug fixes
307
+ ### 3.x - Earlier Versions
495
308
 
496
- ### 1.0.0 (2025-10-20)
497
- - Initial release
498
- - Production and development deployment support
499
- - Secure API key authentication
500
- - Bundle-aware tool naming
501
- - Flattened prompt execution
502
- - Comprehensive error handling
309
+ See git history for earlier changelog.
503
310
 
504
311
  ## Support
505
312
 
506
- - **Issues**: https://github.com/contextfs/contextfs/issues
507
- - **Documentation**: https://docs.contextfs.com
508
- - **Email**: support@contextfs.com
313
+ - **Issues**: https://github.com/promptprojectmanager/promptprojectmanager/issues
314
+ - **Website**: https://promptprojectmanager.com
509
315
 
510
316
  ## License
511
317
 
@@ -513,4 +319,4 @@ MIT License - See LICENSE file for details
513
319
 
514
320
  ---
515
321
 
516
- **Made with ❤️ by the ContextFS team**
322
+ **Made with ❤️ by the Prompt Project Manager team**
package/dist/index.js CHANGED
@@ -228,10 +228,12 @@ async function startServer(config, convexClient) {
228
228
  console.error(`[MCP] Registering ${globalSystemTools.length} global system tools...`);
229
229
  const dynamicPromptTools = [];
230
230
  for (const prompt of accountScopedPrompts) {
231
+ const folderPrefix = prompt.folderSlug ? `[${prompt.folderSlug}] ` : "";
232
+ const baseDescription = prompt.description || `Execute the "${prompt.slug}" prompt`;
231
233
  dynamicPromptTools.push({
232
234
  name: prompt.slug,
233
235
  // Global tool name, no project prefix
234
- description: prompt.description || `Execute the "${prompt.slug}" prompt`,
236
+ description: folderPrefix + baseDescription,
235
237
  promptSlug: prompt.slug
236
238
  });
237
239
  }
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 --projects CLI argument (Ticket 144)\n // Can be: --projects p1,p2 OR --projects p1 --projects p2 OR -p p1,p2\n const projectsArg = argv.projects || argv.p;\n let selectedProjects: string[] = [];\n if (projectsArg) {\n if (Array.isArray(projectsArg)) {\n // Multiple --projects flags: [\"p1\", \"p2\"]\n selectedProjects = projectsArg.flatMap((p: string) => String(p).split(','));\n } else {\n // Single --projects flag with comma-separated: \"p1,p2\"\n selectedProjects = String(projectsArg).split(',');\n }\n // Clean up whitespace\n selectedProjects = selectedProjects.map((s) => s.trim()).filter(Boolean);\n }\n\n // Check for authentication (API key or project 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 projectToken = process.env.PPM_PROJECT_TOKEN;\n\n // PPM_AGENT: Optional agent identity selection (only with API key auth)\n // When set, uses the named project token as identity while API key provides access\n const ppmAgent = process.env.PPM_AGENT;\n\n if (!apiKey && !projectToken) {\n console.error(\n \"[MCP] ERROR: Missing authentication. Provide either PPM_API_KEY or PPM_PROJECT_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 projects\");\n console.error(\"[MCP] export PPM_PROJECT_TOKEN=ppt_... # Scoped access to one project (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 (projectToken) {\n console.error(\"[MCP] Note: PPM_PROJECT_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: Project Token (scoped access, tickets only)\");\n if (ppmAgent) {\n console.error(\"[MCP] Note: PPM_AGENT ignored when using PPM_PROJECT_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 projectToken: apiKey ? undefined : projectToken, // Only use token if no API key\n isDev,\n convexUrl,\n selectedProjects, // Project slugs to filter (empty = all projects)\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, ProjectTokenValidation } 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, fetchAccountScopedPromptMetadataForProject } 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 projectToken (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 project token by name to use as the actor identity.\n */\nfunction buildAuthArgs(config: ServerConfig): { apiKey?: string; projectToken?: 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.projectToken) {\n return { projectToken: config.projectToken };\n }\n return {};\n}\n\n/**\n * Built-in system tools (no global tools - all are project-scoped now)\n */\nconst SYSTEM_TOOLS: {\n name: string;\n description: string;\n inputSchema: object;\n promptContent: string;\n isSlashCommand: boolean; // true = appears as /ppm:name, false = tool only\n}[] = [\n // All tools are now project-scoped (see dynamic*Tools arrays below)\n];\n\n/**\n * Initialize and start the MCP server\n */\nexport async function startServer(\n config: ServerConfig,\n convexClient: ConvexHttpClient\n): Promise<void> {\n // Track auth mode and project scope\n let tokenProjectSlug: string | undefined;\n\n // Validate authentication - API key or project 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.projectToken) {\n // Project Token mode - scoped access\n console.error(\"[MCP] Validating project token...\");\n const validation = await validateProjectToken(convexClient, config.projectToken);\n if (!validation.valid) {\n throw new Error(`Invalid project token: ${validation.error}`);\n }\n tokenProjectSlug = validation.projectSlug;\n console.error(`[MCP] Project token validated: \"${validation.tokenName}\" for project \"${tokenProjectSlug}\"`);\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_PROJECT_TOKEN.\");\n }\n\n // Fetch account-scoped prompts (global MCP tools) and projects (for ticket tools)\n // Account-scoped prompts: Available as global tools (e.g., \"code-review\")\n // Projects: Needed for ticket tool registration (e.g., \"projects_tickets_work\")\n let accountScopedPrompts: Array<{ slug: string; description?: string }> = [];\n let projectsForTickets: Array<{ slug: string; description?: string }> = [];\n\n console.error(\"[MCP] Fetching prompt and project metadata...\");\n\n if (config.apiKey) {\n // API key mode - fetch account-scoped prompts and projects\n // If a single project is specified, use project-filtered query (Prompt Deselector feature)\n if (config.selectedProjects.length === 1) {\n // Single project: Use filtered query that respects project's excludedPromptIds\n accountScopedPrompts = await fetchAccountScopedPromptMetadataForProject(\n convexClient,\n config.apiKey,\n config.selectedProjects[0]\n );\n console.error(`[MCP] Using project-filtered prompt visibility for: ${config.selectedProjects[0]}`);\n } else {\n // No filter or multiple projects: Use standard query (all prompts visible)\n accountScopedPrompts = await fetchAccountScopedPromptMetadata(convexClient, config.apiKey);\n if (config.selectedProjects.length > 1) {\n console.error(`[MCP] Multiple projects specified - showing all prompts (no exclusion filtering)`);\n }\n }\n\n // Fetch projects for ticket tools\n // If --projects specified, only register ticket tools for those projects\n const allProjects = await fetchMcpProjects(convexClient, config.apiKey);\n if (config.selectedProjects.length > 0) {\n const selectedSet = new Set(config.selectedProjects);\n projectsForTickets = allProjects.filter((p) => selectedSet.has(p.slug));\n } else {\n projectsForTickets = allProjects;\n }\n } else if (config.projectToken && tokenProjectSlug) {\n // Project token mode - no prompts (token mode is tickets-only)\n // Note: Project tokens only have access to tickets, not account-scoped prompts\n console.error(\"[MCP] Project token mode: prompts not available (tickets only)\");\n projectsForTickets = [{ slug: tokenProjectSlug, name: tokenProjectSlug }];\n }\n\n console.error(`[MCP] Found ${accountScopedPrompts.length} account-scoped prompts (global tools)`);\n console.error(`[MCP] Found ${projectsForTickets.length} project(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 project info for ticket tools\n if (tokenProjectSlug) {\n console.error(`[MCP] Ticket project scope: ${tokenProjectSlug} (token-scoped)`);\n } else if (config.selectedProjects.length > 0) {\n console.error(`[MCP] Ticket project filter: ${config.selectedProjects.join(', ')}`);\n } else {\n console.error(`[MCP] Ticket projects: ALL (${projectsForTickets.map(p => p.slug).join(', ')})`);\n }\n\n // Build dynamic ticket tools per project\n // Ticket tools remain project-scoped (e.g., \"projects_tickets_work\")\n const projectSlugs = new Set(projectsForTickets.map((p) => p.slug));\n const dynamicTicketTools: { name: string; description: string; slashDescription: string; projectSlug: string; type: 'work' | 'close' | 'create' | 'search' | 'get' | 'list' | 'update' }[] = [];\n\n for (const projectSlug of projectSlugs) {\n // Unified work command (replaces both tickets_open and tickets_work)\n dynamicTicketTools.push({\n name: `${projectSlug}_tickets_work`,\n description: `Get work from the \"${projectSlug}\" project. With no args: gets next ticket from open queue. With ticket slug/number: opens or resumes that specific ticket.`,\n slashDescription: `Get next ticket from queue, or work on specific ticket by slug/number`,\n projectSlug,\n type: 'work',\n });\n dynamicTicketTools.push({\n name: `${projectSlug}_tickets_close`,\n description: `Mark a working ticket as completed in the \"${projectSlug}\" project`,\n slashDescription: `Mark a working ticket as completed`,\n projectSlug,\n type: 'close',\n });\n dynamicTicketTools.push({\n name: `${projectSlug}_tickets_create`,\n description: `Create a new ticket in the \"${projectSlug}\" project queue`,\n slashDescription: `Create a new ticket in the backlog queue`,\n projectSlug,\n type: 'create',\n });\n dynamicTicketTools.push({\n name: `${projectSlug}_tickets_search`,\n description: `Search for tickets by content in the \"${projectSlug}\" project`,\n slashDescription: `Search for tickets by content`,\n projectSlug,\n type: 'search',\n });\n dynamicTicketTools.push({\n name: `${projectSlug}_tickets_get`,\n description: `Get a specific ticket by number or slug from \"${projectSlug}\" (read-only)`,\n slashDescription: `Get a specific ticket by number or slug (read-only)`,\n projectSlug,\n type: 'get',\n });\n dynamicTicketTools.push({\n name: `${projectSlug}_tickets_list`,\n description: `List active tickets in the \"${projectSlug}\" project (backlog + open + working)`,\n slashDescription: `List active tickets (backlog + open + working)`,\n projectSlug,\n type: 'list',\n });\n dynamicTicketTools.push({\n name: `${projectSlug}_tickets_update`,\n description: `Update a ticket in the \"${projectSlug}\" project by appending content with timestamp`,\n slashDescription: `Update a ticket by appending content with timestamp`,\n projectSlug,\n type: 'update',\n });\n }\n\n console.error(`[MCP] Registering ${dynamicTicketTools.length} ticket tools for ${projectSlugs.size} project(s)...`);\n\n // Build global system tools (not project-scoped)\n // run_prompt and system_prompts are now global since prompts are account-scoped\n const globalSystemTools: { name: string; description: string; slashDescription: string }[] = [\n {\n name: \"system_run_prompt\",\n description: \"Execute a prompt by slug. Use system_prompts to list available prompts.\",\n slashDescription: \"Execute a prompt by slug. Use system_prompts to list available prompts.\",\n },\n {\n name: \"system_prompts\",\n description: \"List all available prompts\",\n slashDescription: \"List all available prompts\",\n },\n ];\n\n console.error(`[MCP] Registering ${globalSystemTools.length} global system tools...`);\n\n // Build dynamic per-prompt tools (each prompt as its own tool)\n // Now global: Direct invocation like `code-review` instead of `projects:code-review`\n const dynamicPromptTools: {\n name: string; // \"code-review\" (global, no project prefix)\n description: string;\n promptSlug: string;\n }[] = [];\n\n for (const prompt of accountScopedPrompts) {\n dynamicPromptTools.push({\n name: prompt.slug, // Global tool name, no project 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 project prefix)\n const promptNames = new Set<string>();\n const duplicates: string[] = [];\n accountScopedPrompts.forEach((p) => {\n if (promptNames.has(p.slug)) {\n duplicates.push(p.slug);\n }\n promptNames.add(p.slug);\n });\n\n if (duplicates.length > 0) {\n console.error(\n `[MCP] WARNING: Duplicate prompt slugs detected: ${duplicates.join(\", \")}. Only the first occurrence will be registered.`\n );\n }\n\n // Create MCP server\n const server = new Server(\n {\n name: \"ppm-mcp-server\",\n version: \"3.1.0\",\n },\n {\n capabilities: {\n prompts: {},\n tools: {},\n },\n }\n );\n\n // Register list_prompts handler\n // Prompts are now global (account-scoped), tickets remain project-scoped\n server.setRequestHandler(ListPromptsRequestSchema, async () => {\n // Build ticket prompt schemas (project-scoped)\n const ticketPromptSchemas = dynamicTicketTools.map((tt) => ({\n name: tt.name,\n description: tt.slashDescription,\n }));\n\n // Build global system prompt schemas\n const systemPromptsSchemas = globalSystemTools.map((st) => ({\n name: st.name,\n description: st.slashDescription,\n }));\n\n return {\n prompts: [\n ...systemPromptsSchemas, // system_prompts, system_run_prompt (global)\n ...ticketPromptSchemas, // project_tickets_* (project-scoped)\n ],\n };\n });\n\n // Register get_prompt handler\n server.setRequestHandler(GetPromptRequestSchema, async (request) => {\n const promptName = request.params.name;\n\n // Check for system tools first (as prompts for slash command access)\n const systemTool = SYSTEM_TOOLS.find((st) => st.name === promptName);\n if (systemTool) {\n return {\n description: systemTool.description,\n messages: [\n {\n role: \"user\" as const,\n content: {\n type: \"text\" as const,\n text: systemTool.promptContent,\n },\n },\n ],\n };\n }\n\n // Check for ticket commands (Ticket 135, 149, 153 - handle as slash commands)\n // Handle exact matches and _work with optional argument (e.g., \"project_tickets_work 102\")\n const ticketTool = dynamicTicketTools.find((tt) => tt.name === promptName);\n const ticketWorkMatch = promptName.match(/^(.+)_tickets_work\\s+(.+)$/);\n\n if (ticketTool || ticketWorkMatch) {\n let promptContent: string;\n let description: string;\n\n if (ticketWorkMatch) {\n // Handle tickets_work with specific ticket argument\n const projectSlug = ticketWorkMatch[1];\n const ticketArg = ticketWorkMatch[2].trim();\n description = `Work on ticket \"${ticketArg}\" from \"${projectSlug}\"`;\n promptContent = `Get work on ticket \"${ticketArg}\" from the \"${projectSlug}\" project.\\n\\nCall the \\`${projectSlug}_tickets_work\\` tool with ticketSlug: \"${ticketArg}\".\\n\\nThis will open the ticket if it's in the open queue, or resume it if already working.`;\n } else if (ticketTool!.type === 'work') {\n // Unified work command (replaces open + work)\n description = ticketTool!.description;\n promptContent = `Get work from the \"${ticketTool!.projectSlug}\" project.\\n\\nCall the \\`${ticketTool!.name}\\` tool to get the next ticket from the open queue.\\n\\nYou can also specify a ticket: /ppm:${ticketTool!.projectSlug}_tickets_work <number-or-slug>\\n\\nThis unified command handles both opening new tickets and resuming in-progress work.`;\n } else if (ticketTool!.type === 'create') {\n // Ticket 149: tickets:create slash command\n description = ticketTool!.description;\n promptContent = `Create a new ticket in the \"${ticketTool!.projectSlug}\" project queue.\n\n## How This Works\nThe user provides **instructions** (not raw content). You interpret those instructions to generate the ticket.\n\n## Instructions\n1. **Read the user's request** - they may reference a file, ask you to summarize a session, or describe what they want\n2. **Process the input** - read files, extract relevant sections, summarize as needed\n3. **Generate ticket content** with a clear, descriptive title as the first line\n4. **Call the tool** with the generated content\n\n## Content Format\n\\`\\`\\`\n[Clear descriptive title - this becomes the slug]\n\n[Body content - tasks, description, context, etc.]\n\\`\\`\\`\n\n## Examples\n- \"create a ticket from /path/to/plan.md\" → Read file, use as ticket content\n- \"summarize our brainstorm into a ticket\" → Extract key points from conversation\n- \"create a ticket for the auth bug we discussed\" → Generate from session context\n- \"just the tasks from this file\" → Extract only task items\n\nCall the \\`${ticketTool!.name}\\` tool with the final generated content.`;\n } else if (ticketTool!.type === 'close') {\n description = ticketTool!.description;\n promptContent = `Close a working ticket in the \"${ticketTool!.projectSlug}\" project.\\n\\nCall the \\`${ticketTool!.name}\\` tool with the ticket number or slug.`;\n } else {\n // Fallback for unknown type\n description = ticketTool!.description;\n promptContent = `Use the \\`${ticketTool!.name}\\` tool.`;\n }\n\n return {\n description,\n messages: [\n {\n role: \"user\" as const,\n content: {\n type: \"text\" as const,\n text: promptContent,\n },\n },\n ],\n };\n }\n\n // Check for global system_prompts command\n if (promptName === \"system_prompts\") {\n return {\n description: \"List all available prompts\",\n messages: [\n {\n role: \"user\" as const,\n content: {\n type: \"text\" as const,\n text: `List all available prompts.\n\nCall the \\`system_prompts\\` tool with optional \\`search\\` parameter to filter results.`,\n },\n },\n ],\n };\n }\n\n // Check for global system_run_prompt command (with optional prompt slug argument)\n const runPromptMatch = promptName.match(/^system_run_prompt\\s+(.+)$/);\n\n if (promptName === \"system_run_prompt\" || runPromptMatch) {\n let promptContent: string;\n let description: string;\n\n if (runPromptMatch) {\n // Handle run_prompt with specific prompt slug argument\n const promptSlug = runPromptMatch[1].trim();\n description = `Execute prompt \"${promptSlug}\"`;\n promptContent = `Execute the \"${promptSlug}\" prompt.\\n\\nCall the \\`system_run_prompt\\` tool with slug: \"${promptSlug}\".`;\n } else {\n // Base run_prompt without argument - show help\n description = \"Execute a prompt by slug\";\n promptContent = `Execute a prompt by slug.\n\n## Usage\nCall the \\`system_run_prompt\\` tool with the prompt slug.\n\n## Available Prompts\nUse \\`system_prompts\\` to list all available prompts.\n\n## Example\n/ppm:system_run_prompt code-review\n\nThis will execute the \"code-review\" prompt.`;\n }\n\n return {\n description,\n messages: [\n {\n role: \"user\" as const,\n content: {\n type: \"text\" as const,\n text: promptContent,\n },\n },\n ],\n };\n }\n\n // Note: Command handling removed - commands resource eliminated\n\n // Unknown prompt\n throw new Error(`Unknown prompt: ${promptName}. Use system_run_prompt to execute prompts.`);\n });\n\n // Register list_tools handler\n // Note: Per-prompt tool registration removed in favor of project-scoped run_prompt tools\n // This reduces token overhead and allows dynamic prompt discovery without server restart\n server.setRequestHandler(ListToolsRequestSchema, async () => {\n // Build tools array: system tools + ticket tools + project:run_prompt tools\n // Individual prompts no longer registered as separate tools\n const tools = [\n // System tools with full input schemas\n ...SYSTEM_TOOLS.map((st) => ({\n name: st.name,\n description: st.description,\n inputSchema: st.inputSchema,\n })),\n // Dynamic ticket tools per project (Ticket 135, 149, 151, 153, unified work)\n ...dynamicTicketTools.map((tt) => {\n // Build inputSchema based on tool type\n let inputSchema: { type: \"object\"; properties: Record<string, object>; required: string[] };\n\n if (tt.type === 'close') {\n // close requires a ticketSlug\n inputSchema = {\n type: \"object\" as const,\n properties: {\n ticketSlug: {\n type: \"string\",\n description: \"Ticket number (e.g., '102') or full slug (e.g., '102-fix-auth')\",\n },\n },\n required: [\"ticketSlug\"],\n };\n } else if (tt.type === 'work') {\n // Unified work command (replaces open + work)\n inputSchema = {\n type: \"object\" as const,\n properties: {\n ticketSlug: {\n type: \"string\",\n description: \"Optional: Ticket number (e.g., '102') or full slug. If not provided, gets next ticket from open queue.\",\n },\n },\n required: [],\n };\n } else if (tt.type === 'create') {\n inputSchema = {\n type: \"object\" as const,\n properties: {\n content: {\n type: \"string\",\n description: \"The generated ticket content. First line should be a clear descriptive title (becomes the slug). Body contains tasks, description, context as needed.\",\n },\n },\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 project filter)\n let filteredPrompts = [...accountScopedPrompts];\n\n // Filter by search term if provided\n if (searchTerm) {\n const lowerSearch = searchTerm.toLowerCase();\n filteredPrompts = filteredPrompts.filter(\n (p) =>\n p.slug.toLowerCase().includes(lowerSearch) ||\n p.description?.toLowerCase().includes(lowerSearch)\n );\n }\n\n // Sort prompts by slug\n filteredPrompts.sort((a, b) => a.slug.localeCompare(b.slug));\n\n // Build user prompts list\n const userPromptList = filteredPrompts\n .map((p) => {\n const desc = p.description || \"No description\";\n return `• ${p.slug}\\n Description: ${desc}`;\n })\n .join(\"\\n\\n\");\n\n const summary = searchTerm\n ? `Found ${filteredPrompts.length} prompt(s) matching \"${searchTerm}\":`\n : `Available prompts (${filteredPrompts.length} total):`;\n\n return {\n content: [\n {\n type: \"text\",\n text: userPromptList\n ? `${summary}\\n\\n${userPromptList}`\n : `${summary}\\n\\nNo prompts found.`,\n },\n ],\n };\n }\n\n // Handle 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 projectSlug: ticketTool.projectSlug,\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 project \"${ticketTool.projectSlug}\".`\n : `No open tickets in project \"${ticketTool.projectSlug}\".`;\n return {\n content: [\n {\n type: \"text\",\n text: message,\n },\n ],\n };\n }\n\n // Handle access denied response\n if ('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 projectSlug: ticketTool.projectSlug,\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 project \"${ticketTool.projectSlug}\".\n\nPosition: #${result.position} of ${result.totalBacklog} backlog tickets\nPreview: ${result.preview}\n\n_Ticket created in backlog. Use \\`tickets_work ${result.slug}\\` to move it to working status._`,\n },\n ],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] tickets_create error:`, error);\n return {\n content: [\n {\n type: \"text\",\n text: `Error creating ticket: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n } else if (ticketTool.type === 'close') {\n // Ticket 151: Handle tickets:close\n const 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 projectSlug: ticketTool.projectSlug,\n ticketSlug: ticketSlug,\n }\n ) as CloseTicketResult;\n\n return {\n content: [\n {\n type: \"text\",\n text: `✅ Ticket [${result.slug}] closed in project \"${ticketTool.projectSlug}\".\n\nClosed at: ${new Date(result.closedAt).toISOString()}\n\n_Reminder: Ensure all embedded \\`[RUN_PROMPT ...]\\` directives were executed before closing._`,\n },\n ],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] tickets_close error:`, error);\n return {\n content: [\n {\n type: \"text\",\n text: `Error closing ticket: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n } else if (ticketTool.type === 'search') {\n // Ticket 155: Handle tickets:search\n const 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 projectSlug: ticketTool.projectSlug,\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 project \"${ticketTool.projectSlug}\".`,\n },\n ],\n };\n }\n\n // Format as list with status badges\n const formattedList = result\n .map((t) => {\n const statusBadge = t.status === 'open' ? '🟢' : t.status === 'working' ? '🟡' : t.status === 'closed' ? '⚫' : '⚪';\n const num = t.ticketNumber ? `#${t.ticketNumber}` : '';\n return `${statusBadge} ${num} ${t.slug}\\n ${t.matchSnippet}`;\n })\n .join('\\n\\n');\n\n return {\n content: [\n {\n type: \"text\",\n text: `Found ${result.length} ticket(s) matching \"${query}\":\\n\\n${formattedList}`,\n },\n ],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] tickets_search error:`, error);\n return {\n content: [\n {\n type: \"text\",\n text: `Error searching tickets: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n } else if (ticketTool.type === 'get') {\n // Ticket 155: Handle tickets:get (read-only inspection)\n const 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 projectSlug: ticketTool.projectSlug,\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 project \"${ticketTool.projectSlug}\".`,\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 projectSlug: ticketTool.projectSlug,\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 project \"${ticketTool.projectSlug}\". All tickets are closed or archived.`,\n },\n ],\n };\n }\n\n // Group by status for clear display\n const openTickets = result.filter(t => t.status === 'open');\n const workingTickets = result.filter(t => t.status === 'working');\n const backlogTickets = result.filter(t => t.status === 'backlog');\n\n const formatTicketLine = (t: typeof result[0]) => {\n const num = t.ticketNumber ? `#${t.ticketNumber}` : '';\n return ` ${t.position}. ${num} ${t.slug}\\n ${t.preview}`;\n };\n\n const sections: string[] = [];\n\n if (openTickets.length > 0) {\n sections.push(`**🟢 Open (${openTickets.length})**\\n${openTickets.map(formatTicketLine).join('\\n')}`);\n }\n if (workingTickets.length > 0) {\n sections.push(`**🟡 Working (${workingTickets.length})**\\n${workingTickets.map(formatTicketLine).join('\\n')}`);\n }\n if (backlogTickets.length > 0) {\n sections.push(`**⚪ Backlog (${backlogTickets.length})**\\n${backlogTickets.map(formatTicketLine).join('\\n')}`);\n }\n\n return {\n content: [\n {\n type: \"text\",\n text: `# Active Queue: ${ticketTool.projectSlug}\\n\\n${result.length} ticket(s) in queue\\n\\n${sections.join('\\n\\n')}`,\n },\n ],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] tickets_list error:`, error);\n return {\n content: [\n {\n type: \"text\",\n text: `Error listing tickets: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n } else if (ticketTool.type === 'update') {\n // Ticket 293: Handle tickets:update (append content to ticket)\n const 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 projectSlug: ticketTool.projectSlug,\n ticketSlug,\n content,\n }\n ) as UpdateTicketResult;\n\n return {\n content: [\n {\n type: \"text\",\n text: `✅ Ticket [${result.slug}] updated in project \"${ticketTool.projectSlug}\".\nUpdated at: ${new Date(result.updatedAt).toISOString()}\n_Ticket content has been appended with your update._`,\n },\n ],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] tickets_update error:`, error);\n return {\n content: [\n {\n type: \"text\",\n text: `Error updating ticket: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n }\n }\n\n // Handle per-prompt tool invocations (e.g., \"code-review\" - now global)\n const promptTool = dynamicPromptTools.find((pt) => pt.name === toolName);\n if (promptTool) {\n try {\n const result = await fetchAndExecuteAccountScopedPrompt(\n promptTool.promptSlug,\n config,\n convexClient\n );\n\n const promptText = result.messages\n .map((msg) => msg.content.text)\n .join('\\n\\n');\n\n return {\n content: [{ type: \"text\", text: promptText }],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] ${toolName} error:`, error);\n return {\n content: [{ type: \"text\", text: `Error executing prompt: ${errorMessage}` }],\n isError: true,\n };\n }\n }\n\n // Unknown tool\n throw new Error(`Unknown tool: ${toolName}. Use system_run_prompt to execute prompts by name, or check available tools.`);\n });\n\n // Start server with stdio transport\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n console.error(\"[MCP] Server started successfully\");\n console.error(`[MCP] Deployment: ${config.isDev ? \"DEVELOPMENT\" : \"PRODUCTION\"}`);\n console.error(`[MCP] Convex URL: ${config.convexUrl}`);\n console.error(`[MCP] Data mode: REAL-TIME (fetches fresh data on each invocation)`);\n\n // List all prompts\n const allPromptNames = [...Array.from(promptNames)].sort();\n console.error(`[MCP] Prompts available: ${allPromptNames.join(\", \")}`);\n console.error(`[MCP] - Total prompts: ${promptNames.size}`);\n\n // Keep the event loop alive with a heartbeat\n // This prevents Node from exiting when there are no active handles\n setInterval(() => {\n // Heartbeat every 60 seconds to keep process alive\n }, 60000);\n\n // Return a promise that never resolves to keep the server running\n return new Promise<void>(() => {\n // The transport handles stdin/stdout communication\n // The interval above keeps the event loop active\n });\n}\n\n/**\n * Validate 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 project token with Convex\n *\n * Project tokens provide scoped access to a single project for ticket operations.\n * Returns project details if valid, error if invalid/revoked.\n */\nasync function validateProjectToken(\n client: ConvexHttpClient,\n token: string\n): Promise<ProjectTokenValidation> {\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const result = await client.query(\"project_tokens:validateProjectToken\" as any, { token });\n if (result && result.valid) {\n return {\n valid: true,\n projectId: result.projectId,\n projectSlug: result.projectSlug,\n ownerId: result.ownerId,\n tokenId: result.tokenId,\n tokenName: result.tokenName,\n };\n }\n return { valid: false, error: \"Invalid or revoked project token\" };\n } catch (error) {\n return {\n valid: false,\n error: error instanceof Error ? error.message : \"Unknown error\",\n };\n }\n}\n\n// Note: fetchMcpPromptMetadata 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 projectId - 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 projects for ticket tool registration\n * Separate from prompts since prompts are now global\n */\nasync function fetchMcpProjects(\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 projects = await client.query(\"mcp_prompts:getMcpProjects\" as any, {\n apiKey,\n });\n return projects;\n } catch (error) {\n throw new Error(\n `Failed to fetch projects: ${error instanceof Error ? error.message : \"Unknown error\"}`\n );\n }\n}\n","import type { ConvexHttpClient } from \"convex/browser\";\nimport type { 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 (project slug, folder path, or prompt slug)\n * @returns Sanitized string safe for MCP tool names\n */\nexport function sanitizeForMcp(str: string): string {\n return str\n .trim() // Remove leading/trailing whitespace first\n .toLowerCase() // Convert to lowercase for consistency\n .replace(/[^a-z0-9_-]/g, '-') // Replace ALL invalid chars (including spaces!) with hyphens\n .replace(/-+/g, '-') // Collapse multiple consecutive hyphens into one\n .replace(/^-+|-+$/g, '') // Remove leading and trailing hyphens\n .trim(); // Final trim to ensure no whitespace\n}\n\n// Note: The following legacy functions have been removed as prompts are now account-scoped:\n// - buildPromptName (was project:prompt format, now prompts are global)\n// - buildPromptNameFromMetadata (was project:prompt format)\n// - buildPromptSchema (used buildPromptName)\n// - buildPromptHandler (used project-scoped getMcpPrompts query)\n// - fetchAndExecutePrompt (used project-scoped getMcpPromptBySlug query)\n//\n// Use fetchAndExecuteAccountScopedPrompt instead for all prompt execution.\n\n/**\n * Custom error class for ambiguous prompt matches\n * Contains suggestions for the user to choose from\n */\nexport class AmbiguousPromptError extends Error {\n suggestions: string[];\n\n constructor(promptSlug: string, suggestions: string[]) {\n super(`Multiple prompts match \"${promptSlug}\". Please be more specific.`);\n this.name = \"AmbiguousPromptError\";\n this.suggestions = suggestions;\n }\n}\n\n/**\n * Fetch account-scoped prompt metadata filtered by project exclusions\n * (Prompt Deselector feature)\n *\n * When a single project is specified via --projects flag, uses the project-filtered query\n * to exclude prompts that are hidden for that project.\n *\n * @param client - Convex HTTP client\n * @param apiKey - User's API key\n * @param projectSlug - Project slug to filter exclusions (optional)\n * @returns Account-scoped prompts (filtered if projectSlug provided)\n */\nexport async function fetchAccountScopedPromptMetadataForProject(\n client: ConvexHttpClient,\n apiKey: string,\n projectSlug: 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:getAccountScopedPromptMetadataForProject\" as any, {\n apiKey,\n projectSlug,\n });\n return metadata;\n } catch (error) {\n throw new Error(\n `Failed to fetch account-scoped prompt metadata for project \"${projectSlug}\": ${error instanceof Error ? error.message : \"Unknown error\"}`\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 project prefix required).\n * This is for prompts that don't have a projectId.\n *\n * Matching algorithm same as fetchAndExecutePrompt but without project 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":";;;AAAA,OAAO,cAAc;;;ACArB,SAAS,wBAAwB;AAEjC,IAAM,WAAW;AACjB,IAAM,UAAU;AAKT,SAAS,mBAAmB,OAAkC;AACnE,QAAM,MAAM,QAAQ,UAAU;AAC9B,SAAO,IAAI,iBAAiB,GAAG;AACjC;;;ACXA,SAAS,cAAc;AACvB,SAAS,4BAA4B;AACrC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;;;AC0BA,IAAM,uBAAN,cAAmC,MAAM;AAAA,EAC9C;AAAA,EAEA,YAAY,YAAoB,aAAuB;AACrD,UAAM,2BAA2B,UAAU,6BAA6B;AACxE,SAAK,OAAO;AACZ,SAAK,cAAc;AAAA,EACrB;AACF;AAcA,eAAsB,2CACpB,QACA,QACA,aACwD;AACxD,MAAI;AAEF,UAAM,WAAW,MAAM,OAAO,MAAM,wDAA+D;AAAA,MACjG;AAAA,MACA;AAAA,IACF,CAAC;AACD,WAAO;AAAA,EACT,SAAS,OAAO;AACd,UAAM,IAAI;AAAA,MACR,+DAA+D,WAAW,MAAM,iBAAiB,QAAQ,MAAM,UAAU,eAAe;AAAA,IAC1I;AAAA,EACF;AACF;AAkBA,eAAsB,mCACpB,YACA,QACA,cACyF;AACzF,MAAI,CAAC,OAAO,QAAQ;AAClB,UAAM,IAAI,MAAM,uDAAuD;AAAA,EACzE;AAGA,QAAM,SAAS,MAAM,aAAa,MAAM,4CAAmD;AAAA,IACzF,QAAQ,OAAO;AAAA,IACf;AAAA,EACF,CAAC;AAGD,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI;AAAA,MACR,WAAW,UAAU;AAAA,IACvB;AAAA,EACF;AAGA,MAAI,OAAO,cAAc,aAAa;AACpC,UAAM,IAAI,qBAAqB,YAAY,OAAO,WAAW;AAAA,EAC/D;AAGA,MAAI,CAAC,OAAO,iBAAiB;AAC3B,UAAM,IAAI;AAAA,MACR,WAAW,OAAO,IAAI;AAAA,IACxB;AAAA,EACF;AAGA,QAAM,YAAY,OAAO,cAAc,UACnC,gBACA,OAAO,cAAc,WACnB,uBAAkB,OAAO,IAAI,KAC7B,yBAAoB,OAAO,IAAI;AACrC,UAAQ,MAAM,wCAAwC,UAAU,KAAK,SAAS,GAAG;AAGjF,SAAO;AAAA,IACL,UAAU;AAAA,MACR;AAAA,QACE,MAAM;AAAA,QACN,SAAS;AAAA,UACP,MAAM;AAAA,UACN,MAAM,OAAO;AAAA,QACf;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;;;ADvGA,SAAS,cAAc,QAAsF;AAC3G,MAAI,OAAO,QAAQ;AACjB,WAAO;AAAA,MACL,QAAQ,OAAO;AAAA,MACf,WAAW,OAAO;AAAA;AAAA,IACpB;AAAA,EACF;AACA,MAAI,OAAO,cAAc;AACvB,WAAO,EAAE,cAAc,OAAO,aAAa;AAAA,EAC7C;AACA,SAAO,CAAC;AACV;AAKA,IAAM,eAMA;AAAA;AAEN;AAKA,eAAsB,YACpB,QACA,cACe;AAEf,MAAI;AAGJ,MAAI,OAAO,QAAQ;AAEjB,YAAQ,MAAM,6BAA6B;AAC3C,UAAM,aAAa,MAAM,eAAe,cAAc,OAAO,MAAM;AACnE,QAAI,CAAC,WAAW,OAAO;AACrB,YAAM,IAAI,MAAM,oBAAoB,WAAW,KAAK,EAAE;AAAA,IACxD;AACA,YAAQ,MAAM,qCAAqC,WAAW,MAAM,EAAE;AAAA,EACxE,WAAW,OAAO,cAAc;AAE9B,YAAQ,MAAM,mCAAmC;AACjD,UAAM,aAAa,MAAM,qBAAqB,cAAc,OAAO,YAAY;AAC/E,QAAI,CAAC,WAAW,OAAO;AACrB,YAAM,IAAI,MAAM,0BAA0B,WAAW,KAAK,EAAE;AAAA,IAC9D;AACA,uBAAmB,WAAW;AAC9B,YAAQ,MAAM,mCAAmC,WAAW,SAAS,kBAAkB,gBAAgB,GAAG;AAAA,EAE5G,OAAO;AACL,UAAM,IAAI,MAAM,mEAAmE;AAAA,EACrF;AAKA,MAAI,uBAAsE,CAAC;AAC3E,MAAI,qBAAoE,CAAC;AAEzE,UAAQ,MAAM,+CAA+C;AAE7D,MAAI,OAAO,QAAQ;AAGjB,QAAI,OAAO,iBAAiB,WAAW,GAAG;AAExC,6BAAuB,MAAM;AAAA,QAC3B;AAAA,QACA,OAAO;AAAA,QACP,OAAO,iBAAiB,CAAC;AAAA,MAC3B;AACA,cAAQ,MAAM,uDAAuD,OAAO,iBAAiB,CAAC,CAAC,EAAE;AAAA,IACnG,OAAO;AAEL,6BAAuB,MAAM,iCAAiC,cAAc,OAAO,MAAM;AACzF,UAAI,OAAO,iBAAiB,SAAS,GAAG;AACtC,gBAAQ,MAAM,kFAAkF;AAAA,MAClG;AAAA,IACF;AAIA,UAAM,cAAc,MAAM,iBAAiB,cAAc,OAAO,MAAM;AACtE,QAAI,OAAO,iBAAiB,SAAS,GAAG;AACtC,YAAM,cAAc,IAAI,IAAI,OAAO,gBAAgB;AACnD,2BAAqB,YAAY,OAAO,CAAC,MAAM,YAAY,IAAI,EAAE,IAAI,CAAC;AAAA,IACxE,OAAO;AACL,2BAAqB;AAAA,IACvB;AAAA,EACF,WAAW,OAAO,gBAAgB,kBAAkB;AAGlD,YAAQ,MAAM,gEAAgE;AAC9E,yBAAqB,CAAC,EAAE,MAAM,kBAAkB,MAAM,iBAAiB,CAAC;AAAA,EAC1E;AAEA,UAAQ,MAAM,eAAe,qBAAqB,MAAM,wCAAwC;AAChG,UAAQ,MAAM,eAAe,mBAAmB,MAAM,8BAA8B;AAEpF,MAAI,qBAAqB,WAAW,KAAK,OAAO,QAAQ;AACtD,YAAQ;AAAA,MACN;AAAA,IACF;AAAA,EACF;AAGA,MAAI,kBAAkB;AACpB,YAAQ,MAAM,+BAA+B,gBAAgB,iBAAiB;AAAA,EAChF,WAAW,OAAO,iBAAiB,SAAS,GAAG;AAC7C,YAAQ,MAAM,gCAAgC,OAAO,iBAAiB,KAAK,IAAI,CAAC,EAAE;AAAA,EACpF,OAAO;AACL,YAAQ,MAAM,+BAA+B,mBAAmB,IAAI,OAAK,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC,GAAG;AAAA,EAChG;AAIA,QAAM,eAAe,IAAI,IAAI,mBAAmB,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC;AAClE,QAAM,qBAAuL,CAAC;AAE9L,aAAW,eAAe,cAAc;AAEtC,uBAAmB,KAAK;AAAA,MACtB,MAAM,GAAG,WAAW;AAAA,MACpB,aAAa,sBAAsB,WAAW;AAAA,MAC9C,kBAAkB;AAAA,MAClB;AAAA,MACA,MAAM;AAAA,IACR,CAAC;AACD,uBAAmB,KAAK;AAAA,MACtB,MAAM,GAAG,WAAW;AAAA,MACpB,aAAa,8CAA8C,WAAW;AAAA,MACtE,kBAAkB;AAAA,MAClB;AAAA,MACA,MAAM;AAAA,IACR,CAAC;AACD,uBAAmB,KAAK;AAAA,MACtB,MAAM,GAAG,WAAW;AAAA,MACpB,aAAa,+BAA+B,WAAW;AAAA,MACvD,kBAAkB;AAAA,MAClB;AAAA,MACA,MAAM;AAAA,IACR,CAAC;AACD,uBAAmB,KAAK;AAAA,MACtB,MAAM,GAAG,WAAW;AAAA,MACpB,aAAa,yCAAyC,WAAW;AAAA,MACjE,kBAAkB;AAAA,MAClB;AAAA,MACA,MAAM;AAAA,IACR,CAAC;AACD,uBAAmB,KAAK;AAAA,MACtB,MAAM,GAAG,WAAW;AAAA,MACpB,aAAa,iDAAiD,WAAW;AAAA,MACzE,kBAAkB;AAAA,MAClB;AAAA,MACA,MAAM;AAAA,IACR,CAAC;AACD,uBAAmB,KAAK;AAAA,MACtB,MAAM,GAAG,WAAW;AAAA,MACpB,aAAa,+BAA+B,WAAW;AAAA,MACvD,kBAAkB;AAAA,MAClB;AAAA,MACA,MAAM;AAAA,IACR,CAAC;AACD,uBAAmB,KAAK;AAAA,MACtB,MAAM,GAAG,WAAW;AAAA,MACpB,aAAa,2BAA2B,WAAW;AAAA,MACnD,kBAAkB;AAAA,MAClB;AAAA,MACA,MAAM;AAAA,IACR,CAAC;AAAA,EACH;AAEA,UAAQ,MAAM,qBAAqB,mBAAmB,MAAM,qBAAqB,aAAa,IAAI,gBAAgB;AAIlH,QAAM,oBAAuF;AAAA,IAC3F;AAAA,MACE,MAAM;AAAA,MACN,aAAa;AAAA,MACb,kBAAkB;AAAA,IACpB;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,aAAa;AAAA,MACb,kBAAkB;AAAA,IACpB;AAAA,EACF;AAEA,UAAQ,MAAM,qBAAqB,kBAAkB,MAAM,yBAAyB;AAIpF,QAAM,qBAIA,CAAC;AAEP,aAAW,UAAU,sBAAsB;AACzC,uBAAmB,KAAK;AAAA,MACtB,MAAM,OAAO;AAAA;AAAA,MACb,aAAa,OAAO,eAAe,gBAAgB,OAAO,IAAI;AAAA,MAC9D,YAAY,OAAO;AAAA,IACrB,CAAC;AAAA,EACH;AAEA,UAAQ,MAAM,qBAAqB,mBAAmB,MAAM,+BAA+B;AAK3F,QAAM,cAAc,oBAAI,IAAY;AACpC,QAAM,aAAuB,CAAC;AAC9B,uBAAqB,QAAQ,CAAC,MAAM;AAClC,QAAI,YAAY,IAAI,EAAE,IAAI,GAAG;AAC3B,iBAAW,KAAK,EAAE,IAAI;AAAA,IACxB;AACA,gBAAY,IAAI,EAAE,IAAI;AAAA,EACxB,CAAC;AAED,MAAI,WAAW,SAAS,GAAG;AACzB,YAAQ;AAAA,MACN,mDAAmD,WAAW,KAAK,IAAI,CAAC;AAAA,IAC1E;AAAA,EACF;AAGA,QAAM,SAAS,IAAI;AAAA,IACjB;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,IACX;AAAA,IACA;AAAA,MACE,cAAc;AAAA,QACZ,SAAS,CAAC;AAAA,QACV,OAAO,CAAC;AAAA,MACV;AAAA,IACF;AAAA,EACF;AAIA,SAAO,kBAAkB,0BAA0B,YAAY;AAE7D,UAAM,sBAAsB,mBAAmB,IAAI,CAAC,QAAQ;AAAA,MAC1D,MAAM,GAAG;AAAA,MACT,aAAa,GAAG;AAAA,IAClB,EAAE;AAGF,UAAM,uBAAuB,kBAAkB,IAAI,CAAC,QAAQ;AAAA,MAC1D,MAAM,GAAG;AAAA,MACT,aAAa,GAAG;AAAA,IAClB,EAAE;AAEF,WAAO;AAAA,MACL,SAAS;AAAA,QACP,GAAG;AAAA;AAAA,QACH,GAAG;AAAA;AAAA,MACL;AAAA,IACF;AAAA,EACF,CAAC;AAGD,SAAO,kBAAkB,wBAAwB,OAAO,YAAY;AAClE,UAAM,aAAa,QAAQ,OAAO;AAGlC,UAAM,aAAa,aAAa,KAAK,CAAC,OAAO,GAAG,SAAS,UAAU;AACnE,QAAI,YAAY;AACd,aAAO;AAAA,QACL,aAAa,WAAW;AAAA,QACxB,UAAU;AAAA,UACR;AAAA,YACE,MAAM;AAAA,YACN,SAAS;AAAA,cACP,MAAM;AAAA,cACN,MAAM,WAAW;AAAA,YACnB;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAIA,UAAM,aAAa,mBAAmB,KAAK,CAAC,OAAO,GAAG,SAAS,UAAU;AACzE,UAAM,kBAAkB,WAAW,MAAM,4BAA4B;AAErE,QAAI,cAAc,iBAAiB;AACjC,UAAI;AACJ,UAAI;AAEJ,UAAI,iBAAiB;AAEnB,cAAM,cAAc,gBAAgB,CAAC;AACrC,cAAM,YAAY,gBAAgB,CAAC,EAAE,KAAK;AAC1C,sBAAc,mBAAmB,SAAS,WAAW,WAAW;AAChE,wBAAgB,uBAAuB,SAAS,eAAe,WAAW;AAAA;AAAA,aAA4B,WAAW,0CAA0C,SAAS;AAAA;AAAA;AAAA,MACtK,WAAW,WAAY,SAAS,QAAQ;AAEtC,sBAAc,WAAY;AAC1B,wBAAgB,sBAAsB,WAAY,WAAW;AAAA;AAAA,aAA4B,WAAY,IAAI;AAAA;AAAA,sCAA8F,WAAY,WAAW;AAAA;AAAA;AAAA,MAChO,WAAW,WAAY,SAAS,UAAU;AAExC,sBAAc,WAAY;AAC1B,wBAAgB,+BAA+B,WAAY,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,aAwBjE,WAAY,IAAI;AAAA,MACvB,WAAW,WAAY,SAAS,SAAS;AACvC,sBAAc,WAAY;AAC1B,wBAAgB,kCAAkC,WAAY,WAAW;AAAA;AAAA,aAA4B,WAAY,IAAI;AAAA,MACvH,OAAO;AAEL,sBAAc,WAAY;AAC1B,wBAAgB,aAAa,WAAY,IAAI;AAAA,MAC/C;AAEA,aAAO;AAAA,QACL;AAAA,QACA,UAAU;AAAA,UACR;AAAA,YACE,MAAM;AAAA,YACN,SAAS;AAAA,cACP,MAAM;AAAA,cACN,MAAM;AAAA,YACR;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,QAAI,eAAe,kBAAkB;AACnC,aAAO;AAAA,QACL,aAAa;AAAA,QACb,UAAU;AAAA,UACR;AAAA,YACE,MAAM;AAAA,YACN,SAAS;AAAA,cACP,MAAM;AAAA,cACN,MAAM;AAAA;AAAA;AAAA,YAGR;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,UAAM,iBAAiB,WAAW,MAAM,4BAA4B;AAEpE,QAAI,eAAe,uBAAuB,gBAAgB;AACxD,UAAI;AACJ,UAAI;AAEJ,UAAI,gBAAgB;AAElB,cAAM,aAAa,eAAe,CAAC,EAAE,KAAK;AAC1C,sBAAc,mBAAmB,UAAU;AAC3C,wBAAgB,gBAAgB,UAAU;AAAA;AAAA,kDAAgE,UAAU;AAAA,MACtH,OAAO;AAEL,sBAAc;AACd,wBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAYlB;AAEA,aAAO;AAAA,QACL;AAAA,QACA,UAAU;AAAA,UACR;AAAA,YACE,MAAM;AAAA,YACN,SAAS;AAAA,cACP,MAAM;AAAA,cACN,MAAM;AAAA,YACR;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAKA,UAAM,IAAI,MAAM,mBAAmB,UAAU,6CAA6C;AAAA,EAC5F,CAAC;AAKD,SAAO,kBAAkB,wBAAwB,YAAY;AAG3D,UAAM,QAAQ;AAAA;AAAA,MAEZ,GAAG,aAAa,IAAI,CAAC,QAAQ;AAAA,QAC3B,MAAM,GAAG;AAAA,QACT,aAAa,GAAG;AAAA,QAChB,aAAa,GAAG;AAAA,MAClB,EAAE;AAAA;AAAA,MAEF,GAAG,mBAAmB,IAAI,CAAC,OAAO;AAEhC,YAAI;AAEJ,YAAI,GAAG,SAAS,SAAS;AAEvB,wBAAc;AAAA,YACZ,MAAM;AAAA,YACN,YAAY;AAAA,cACV,YAAY;AAAA,gBACV,MAAM;AAAA,gBACN,aAAa;AAAA,cACf;AAAA,YACF;AAAA,YACA,UAAU,CAAC,YAAY;AAAA,UACzB;AAAA,QACF,WAAW,GAAG,SAAS,QAAQ;AAE7B,wBAAc;AAAA,YACZ,MAAM;AAAA,YACN,YAAY;AAAA,cACV,YAAY;AAAA,gBACV,MAAM;AAAA,gBACN,aAAa;AAAA,cACf;AAAA,YACF;AAAA,YACA,UAAU,CAAC;AAAA,UACb;AAAA,QACF,WAAW,GAAG,SAAS,UAAU;AAC/B,wBAAc;AAAA,YACZ,MAAM;AAAA,YACN,YAAY;AAAA,cACV,SAAS;AAAA,gBACP,MAAM;AAAA,gBACN,aAAa;AAAA,cACf;AAAA,YACF;AAAA,YACA,UAAU,CAAC,SAAS;AAAA,UACtB;AAAA,QACF,WAAW,GAAG,SAAS,UAAU;AAE/B,wBAAc;AAAA,YACZ,MAAM;AAAA,YACN,YAAY;AAAA,cACV,OAAO;AAAA,gBACL,MAAM;AAAA,gBACN,aAAa;AAAA,cACf;AAAA,YACF;AAAA,YACA,UAAU,CAAC,OAAO;AAAA,UACpB;AAAA,QACF,WAAW,GAAG,SAAS,OAAO;AAE5B,wBAAc;AAAA,YACZ,MAAM;AAAA,YACN,YAAY;AAAA,cACV,YAAY;AAAA,gBACV,MAAM;AAAA,gBACN,aAAa;AAAA,cACf;AAAA,YACF;AAAA,YACA,UAAU,CAAC,YAAY;AAAA,UACzB;AAAA,QACF,WAAW,GAAG,SAAS,QAAQ;AAE7B,wBAAc;AAAA,YACZ,MAAM;AAAA,YACN,YAAY,CAAC;AAAA,YACb,UAAU,CAAC;AAAA,UACb;AAAA,QACF,WAAW,GAAG,SAAS,UAAU;AAE/B,wBAAc;AAAA,YACZ,MAAM;AAAA,YACN,YAAY;AAAA,cACV,YAAY;AAAA,gBACV,MAAM;AAAA,gBACN,aAAa;AAAA,cACf;AAAA,cACA,SAAS;AAAA,gBACP,MAAM;AAAA,gBACN,aAAa;AAAA,cACf;AAAA,YACF;AAAA,YACA,UAAU,CAAC,cAAc,SAAS;AAAA,UACpC;AAAA,QACF,OAAO;AAEL,wBAAc;AAAA,YACZ,MAAM;AAAA,YACN,YAAY,CAAC;AAAA,YACb,UAAU,CAAC;AAAA,UACb;AAAA,QACF;AAEA,eAAO;AAAA,UACL,MAAM,GAAG;AAAA,UACT,aAAa,GAAG;AAAA,UAChB;AAAA,QACF;AAAA,MACF,CAAC;AAAA;AAAA,MAED;AAAA,QACE,MAAM;AAAA,QACN,aAAa;AAAA,QACb,aAAa;AAAA,UACX,MAAM;AAAA,UACN,YAAY;AAAA,YACV,MAAM;AAAA,cACJ,MAAM;AAAA,cACN,aAAa;AAAA,YACf;AAAA,UACF;AAAA,UACA,UAAU,CAAC,MAAM;AAAA,QACnB;AAAA,MACF;AAAA;AAAA,MAEA;AAAA,QACE,MAAM;AAAA,QACN,aAAa;AAAA,QACb,aAAa;AAAA,UACX,MAAM;AAAA,UACN,YAAY;AAAA,YACV,QAAQ;AAAA,cACN,MAAM;AAAA,cACN,aAAa;AAAA,YACf;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA;AAAA,MAEA,GAAG,mBAAmB,IAAI,CAAC,QAAQ;AAAA,QACjC,MAAM,GAAG;AAAA,QACT,aAAa,GAAG;AAAA,QAChB,aAAa;AAAA,UACX,MAAM;AAAA,UACN,YAAY,CAAC;AAAA,UACb,UAAU,CAAC;AAAA,QACb;AAAA,MACF,EAAE;AAAA,IACJ;AAEA,WAAO,EAAE,MAAM;AAAA,EACjB,CAAC;AAGD,SAAO,kBAAkB,uBAAuB,OAAO,YAAY;AACjE,UAAM,WAAW,QAAQ,OAAO;AAGhC,QAAI,aAAa,qBAAqB;AACpC,YAAM,aAAa,QAAQ,OAAO,WAAW;AAE7C,UAAI,CAAC,YAAY;AACf,eAAO;AAAA,UACL,SAAS;AAAA,YACP;AAAA,cACE,MAAM;AAAA,cACN,MAAM;AAAA;AAAA;AAAA,YAGR;AAAA,UACF;AAAA,UACA,SAAS;AAAA,QACX;AAAA,MACF;AAGA,UAAI;AACF,cAAM,SAAS,MAAM;AAAA,UACnB;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAGA,cAAM,aAAa,OAAO,SACvB,IAAI,CAAC,QAAQ,IAAI,QAAQ,IAAI,EAC7B,KAAK,MAAM;AAEd,eAAO;AAAA,UACL,SAAS;AAAA,YACP;AAAA,cACE,MAAM;AAAA,cACN,MAAM;AAAA,YACR;AAAA,UACF;AAAA,QACF;AAAA,MACF,SAAS,OAAO;AAEd,YAAI,iBAAiB,sBAAsB;AACzC,gBAAM,kBAAkB,MAAM,YAAY,IAAI,CAAC,MAAM,YAAO,CAAC,EAAE,EAAE,KAAK,IAAI;AAC1E,kBAAQ,MAAM,SAAS,QAAQ,qBAAqB,MAAM,WAAW;AACrE,iBAAO;AAAA,YACL,SAAS;AAAA,cACP;AAAA,gBACE,MAAM;AAAA,gBACN,MAAM,2BAA2B,UAAU;AAAA;AAAA,EAAgC,eAAe;AAAA;AAAA,+BAAoC,MAAM,YAAY,CAAC,CAAC;AAAA,cACpJ;AAAA,YACF;AAAA,YACA,SAAS;AAAA,UACX;AAAA,QACF;AAEA,cAAM,eAAe,iBAAiB,QAAQ,MAAM,UAAU;AAC9D,gBAAQ,MAAM,SAAS,QAAQ,WAAW,KAAK;AAC/C,eAAO;AAAA,UACL,SAAS;AAAA,YACP;AAAA,cACE,MAAM;AAAA,cACN,MAAM,2BAA2B,UAAU,MAAM,YAAY;AAAA,YAC/D;AAAA,UACF;AAAA,UACA,SAAS;AAAA,QACX;AAAA,MACF;AAAA,IACF;AAIA,QAAI,aAAa,kBAAkB;AACjC,YAAM,aAAa,QAAQ,OAAO,WAAW;AAG7C,UAAI,kBAAkB,CAAC,GAAG,oBAAoB;AAG9C,UAAI,YAAY;AACd,cAAM,cAAc,WAAW,YAAY;AAC3C,0BAAkB,gBAAgB;AAAA,UAChC,CAAC,MACC,EAAE,KAAK,YAAY,EAAE,SAAS,WAAW,KACzC,EAAE,aAAa,YAAY,EAAE,SAAS,WAAW;AAAA,QACrD;AAAA,MACF;AAGA,sBAAgB,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;AAG3D,YAAM,iBAAiB,gBACpB,IAAI,CAAC,MAAM;AACV,cAAM,OAAO,EAAE,eAAe;AAC9B,eAAO,UAAK,EAAE,IAAI;AAAA,iBAAoB,IAAI;AAAA,MAC5C,CAAC,EACA,KAAK,MAAM;AAEd,YAAM,UAAU,aACZ,SAAS,gBAAgB,MAAM,wBAAwB,UAAU,OACjE,sBAAsB,gBAAgB,MAAM;AAEhD,aAAO;AAAA,QACL,SAAS;AAAA,UACP;AAAA,YACE,MAAM;AAAA,YACN,MAAM,iBACF,GAAG,OAAO;AAAA;AAAA,EAAO,cAAc,KAC/B,GAAG,OAAO;AAAA;AAAA;AAAA,UAChB;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,UAAM,aAAa,mBAAmB,KAAK,CAAC,OAAO,GAAG,SAAS,QAAQ;AACvE,QAAI,YAAY;AACd,UAAI,WAAW,SAAS,QAAQ;AAE9B,cAAM,aAAa,QAAQ,OAAO,WAAW;AAE7C,YAAI;AAGF,gBAAM,SAAS,MAAO,aAAqB;AAAA,YACzC;AAAA,YACA;AAAA,cACE,GAAG,cAAc,MAAM;AAAA,cACvB,aAAa,WAAW;AAAA,cACxB;AAAA;AAAA,YACF;AAAA,UACF;AAUA,cAAI,CAAC,QAAQ;AACX,kBAAM,UAAU,aACZ,WAAW,UAAU,yDAAyD,WAAW,WAAW,OACpG,+BAA+B,WAAW,WAAW;AACzD,mBAAO;AAAA,cACL,SAAS;AAAA,gBACP;AAAA,kBACE,MAAM;AAAA,kBACN,MAAM;AAAA,gBACR;AAAA,cACF;AAAA,YACF;AAAA,UACF;AAGA,cAAI,kBAAkB,UAAU,OAAO,cAAc;AACnD,mBAAO;AAAA,cACL,SAAS;AAAA,gBACP;AAAA,kBACE,MAAM;AAAA,kBACN,MAAM,kBAAkB,OAAO,KAAK;AAAA,gBACtC;AAAA,cACF;AAAA,cACA,SAAS;AAAA,YACX;AAAA,UACF;AAEA,gBAAM,cAAc,OAAO,YACvB;AAAA,WAAc,IAAI,KAAK,OAAO,SAAS,EAAE,YAAY,CAAC,KACtD;AAGJ,gBAAM,aAAa,OAAO,YACtB,oCAAoC,OAAO,gBAAgB,oCAC3D,kCAAkC,OAAO,gBAAgB;AAE7D,iBAAO;AAAA,YACL,SAAS;AAAA,cACP;AAAA,gBACE,MAAM;AAAA,gBACN,MAAM,aAAa,OAAO,IAAI,aAAa,WAAW;AAAA;AAAA,EAEpE,OAAO,OAAO;AAAA;AAAA;AAAA,EAGd,UAAU;AAAA,cACE;AAAA,YACF;AAAA,UACF;AAAA,QACF,SAAS,OAAO;AACd,gBAAM,eAAe,iBAAiB,QAAQ,MAAM,UAAU;AAC9D,kBAAQ,MAAM,6BAA6B,KAAK;AAChD,iBAAO;AAAA,YACL,SAAS;AAAA,cACP;AAAA,gBACE,MAAM;AAAA,gBACN,MAAM,uBAAuB,YAAY;AAAA,cAC3C;AAAA,YACF;AAAA,YACA,SAAS;AAAA,UACX;AAAA,QACF;AAAA,MACF,WAAW,WAAW,SAAS,UAAU;AAEvC,cAAM,UAAU,QAAQ,OAAO,WAAW;AAE1C,YAAI,CAAC,SAAS;AACZ,iBAAO;AAAA,YACL,SAAS;AAAA,cACP;AAAA,gBACE,MAAM;AAAA,gBACN,MAAM;AAAA,cACR;AAAA,YACF;AAAA,YACA,SAAS;AAAA,UACX;AAAA,QACF;AAEA,YAAI;AAEF,gBAAM,SAAS,MAAO,aAAqB;AAAA,YACzC;AAAA,YACA;AAAA,cACE,GAAG,cAAc,MAAM;AAAA,cACvB,aAAa,WAAW;AAAA,cACxB;AAAA,YACF;AAAA,UACF;AAEA,iBAAO;AAAA,YACL,SAAS;AAAA,cACP;AAAA,gBACE,MAAM;AAAA,gBACN,MAAM,0BAAqB,OAAO,IAAI,6BAA6B,WAAW,WAAW;AAAA;AAAA,aAE5F,OAAO,QAAQ,OAAO,OAAO,YAAY;AAAA,WAC3C,OAAO,OAAO;AAAA;AAAA,iDAEwB,OAAO,IAAI;AAAA,cAC9C;AAAA,YACF;AAAA,UACF;AAAA,QACF,SAAS,OAAO;AACd,gBAAM,eAAe,iBAAiB,QAAQ,MAAM,UAAU;AAC9D,kBAAQ,MAAM,+BAA+B,KAAK;AAClD,iBAAO;AAAA,YACL,SAAS;AAAA,cACP;AAAA,gBACE,MAAM;AAAA,gBACN,MAAM,0BAA0B,YAAY;AAAA,cAC9C;AAAA,YACF;AAAA,YACA,SAAS;AAAA,UACX;AAAA,QACF;AAAA,MACF,WAAW,WAAW,SAAS,SAAS;AAEtC,cAAM,aAAa,QAAQ,OAAO,WAAW;AAE7C,YAAI,CAAC,YAAY;AACf,iBAAO;AAAA,YACL,SAAS;AAAA,cACP;AAAA,gBACE,MAAM;AAAA,gBACN,MAAM;AAAA,cACR;AAAA,YACF;AAAA,YACA,SAAS;AAAA,UACX;AAAA,QACF;AAEA,YAAI;AAEF,gBAAM,SAAS,MAAO,aAAqB;AAAA,YACzC;AAAA,YACA;AAAA,cACE,GAAG,cAAc,MAAM;AAAA,cACvB,aAAa,WAAW;AAAA,cACxB;AAAA,YACF;AAAA,UACF;AAEA,iBAAO;AAAA,YACL,SAAS;AAAA,cACP;AAAA,gBACE,MAAM;AAAA,gBACN,MAAM,kBAAa,OAAO,IAAI,wBAAwB,WAAW,WAAW;AAAA;AAAA,aAE/E,IAAI,KAAK,OAAO,QAAQ,EAAE,YAAY,CAAC;AAAA;AAAA;AAAA,cAGtC;AAAA,YACF;AAAA,UACF;AAAA,QACF,SAAS,OAAO;AACd,gBAAM,eAAe,iBAAiB,QAAQ,MAAM,UAAU;AAC9D,kBAAQ,MAAM,8BAA8B,KAAK;AACjD,iBAAO;AAAA,YACL,SAAS;AAAA,cACP;AAAA,gBACE,MAAM;AAAA,gBACN,MAAM,yBAAyB,YAAY;AAAA,cAC7C;AAAA,YACF;AAAA,YACA,SAAS;AAAA,UACX;AAAA,QACF;AAAA,MACF,WAAW,WAAW,SAAS,UAAU;AAEvC,cAAM,QAAQ,QAAQ,OAAO,WAAW;AAExC,YAAI,CAAC,SAAS,MAAM,SAAS,GAAG;AAC9B,iBAAO;AAAA,YACL,SAAS;AAAA,cACP;AAAA,gBACE,MAAM;AAAA,gBACN,MAAM;AAAA,cACR;AAAA,YACF;AAAA,YACA,SAAS;AAAA,UACX;AAAA,QACF;AAEA,YAAI;AAEF,gBAAM,SAAS,MAAO,aAAqB;AAAA,YACzC;AAAA,YACA;AAAA,cACE,GAAG,cAAc,MAAM;AAAA,cACvB,aAAa,WAAW;AAAA,cACxB;AAAA,YACF;AAAA,UACF;AAEA,cAAI,OAAO,WAAW,GAAG;AACvB,mBAAO;AAAA,cACL,SAAS;AAAA,gBACP;AAAA,kBACE,MAAM;AAAA,kBACN,MAAM,8BAA8B,KAAK,iBAAiB,WAAW,WAAW;AAAA,gBAClF;AAAA,cACF;AAAA,YACF;AAAA,UACF;AAGA,gBAAM,gBAAgB,OACnB,IAAI,CAAC,MAAM;AACV,kBAAM,cAAc,EAAE,WAAW,SAAS,cAAO,EAAE,WAAW,YAAY,cAAO,EAAE,WAAW,WAAW,WAAM;AAC/G,kBAAM,MAAM,EAAE,eAAe,IAAI,EAAE,YAAY,KAAK;AACpD,mBAAO,GAAG,WAAW,IAAI,GAAG,IAAI,EAAE,IAAI;AAAA,KAAQ,EAAE,YAAY;AAAA,UAC9D,CAAC,EACA,KAAK,MAAM;AAEd,iBAAO;AAAA,YACL,SAAS;AAAA,cACP;AAAA,gBACE,MAAM;AAAA,gBACN,MAAM,SAAS,OAAO,MAAM,wBAAwB,KAAK;AAAA;AAAA,EAAS,aAAa;AAAA,cACjF;AAAA,YACF;AAAA,UACF;AAAA,QACF,SAAS,OAAO;AACd,gBAAM,eAAe,iBAAiB,QAAQ,MAAM,UAAU;AAC9D,kBAAQ,MAAM,+BAA+B,KAAK;AAClD,iBAAO;AAAA,YACL,SAAS;AAAA,cACP;AAAA,gBACE,MAAM;AAAA,gBACN,MAAM,4BAA4B,YAAY;AAAA,cAChD;AAAA,YACF;AAAA,YACA,SAAS;AAAA,UACX;AAAA,QACF;AAAA,MACF,WAAW,WAAW,SAAS,OAAO;AAEpC,cAAM,aAAa,QAAQ,OAAO,WAAW;AAE7C,YAAI,CAAC,YAAY;AACf,iBAAO;AAAA,YACL,SAAS;AAAA,cACP;AAAA,gBACE,MAAM;AAAA,gBACN,MAAM;AAAA,cACR;AAAA,YACF;AAAA,YACA,SAAS;AAAA,UACX;AAAA,QACF;AAEA,YAAI;AAGF,gBAAM,SAAS,MAAO,aAAqB;AAAA,YACzC;AAAA,YACA;AAAA,cACE,GAAG,cAAc,MAAM;AAAA,cACvB,aAAa,WAAW;AAAA,cACxB;AAAA,YACF;AAAA,UACF;AAEA,cAAI,CAAC,QAAQ;AACX,mBAAO;AAAA,cACL,SAAS;AAAA,gBACP;AAAA,kBACE,MAAM;AAAA,kBACN,MAAM,WAAW,UAAU,2BAA2B,WAAW,WAAW;AAAA,gBAC9E;AAAA,cACF;AAAA,YACF;AAAA,UACF;AAGA,cAAI,kBAAkB,UAAU,OAAO,cAAc;AACnD,mBAAO;AAAA,cACL,SAAS;AAAA,gBACP;AAAA,kBACE,MAAM;AAAA,kBACN,MAAM,kBAAkB,OAAO,KAAK;AAAA,gBACtC;AAAA,cACF;AAAA,cACA,SAAS;AAAA,YACX;AAAA,UACF;AAGA,gBAAM,cAAc,OAAO,OAAO,YAAY;AAC9C,gBAAM,cAAc,OAAO,YAAY;AAAA,WAAc,IAAI,KAAK,OAAO,SAAS,EAAE,YAAY,CAAC,KAAK;AAClG,gBAAM,aAAa,OAAO,WAAW;AAAA,UAAa,IAAI,KAAK,OAAO,QAAQ,EAAE,YAAY,CAAC,KAAK;AAC9F,gBAAM,eAAe,OAAO,aAAa;AAAA,eAAkB,OAAO,UAAU,KAAK;AAEjF,iBAAO;AAAA,YACL,SAAS;AAAA,cACP;AAAA,gBACE,MAAM;AAAA,gBACN,MAAM,aAAa,OAAO,IAAI,KAAK,WAAW,IAAI,WAAW,GAAG,UAAU,GAAG,YAAY;AAAA;AAAA,EAEvG,OAAO,OAAO;AAAA;AAAA;AAAA;AAAA,cAIF;AAAA,YACF;AAAA,UACF;AAAA,QACF,SAAS,OAAO;AACd,gBAAM,eAAe,iBAAiB,QAAQ,MAAM,UAAU;AAC9D,kBAAQ,MAAM,4BAA4B,KAAK;AAC/C,iBAAO;AAAA,YACL,SAAS;AAAA,cACP;AAAA,gBACE,MAAM;AAAA,gBACN,MAAM,yBAAyB,YAAY;AAAA,cAC7C;AAAA,YACF;AAAA,YACA,SAAS;AAAA,UACX;AAAA,QACF;AAAA,MACF,WAAW,WAAW,SAAS,QAAQ;AAErC,YAAI;AAEF,gBAAM,SAAS,MAAO,aAAqB;AAAA,YACzC;AAAA,YACA;AAAA,cACE,GAAG,cAAc,MAAM;AAAA,cACvB,aAAa,WAAW;AAAA,YAC1B;AAAA,UACF;AAEA,cAAI,OAAO,WAAW,GAAG;AACvB,mBAAO;AAAA,cACL,SAAS;AAAA,gBACP;AAAA,kBACE,MAAM;AAAA,kBACN,MAAM,iCAAiC,WAAW,WAAW;AAAA,gBAC/D;AAAA,cACF;AAAA,YACF;AAAA,UACF;AAGA,gBAAM,cAAc,OAAO,OAAO,OAAK,EAAE,WAAW,MAAM;AAC1D,gBAAM,iBAAiB,OAAO,OAAO,OAAK,EAAE,WAAW,SAAS;AAChE,gBAAM,iBAAiB,OAAO,OAAO,OAAK,EAAE,WAAW,SAAS;AAEhE,gBAAM,mBAAmB,CAAC,MAAwB;AAChD,kBAAM,MAAM,EAAE,eAAe,IAAI,EAAE,YAAY,KAAK;AACpD,mBAAO,KAAK,EAAE,QAAQ,KAAK,GAAG,IAAI,EAAE,IAAI;AAAA,OAAU,EAAE,OAAO;AAAA,UAC7D;AAEA,gBAAM,WAAqB,CAAC;AAE5B,cAAI,YAAY,SAAS,GAAG;AAC1B,qBAAS,KAAK,qBAAc,YAAY,MAAM;AAAA,EAAQ,YAAY,IAAI,gBAAgB,EAAE,KAAK,IAAI,CAAC,EAAE;AAAA,UACtG;AACA,cAAI,eAAe,SAAS,GAAG;AAC7B,qBAAS,KAAK,wBAAiB,eAAe,MAAM;AAAA,EAAQ,eAAe,IAAI,gBAAgB,EAAE,KAAK,IAAI,CAAC,EAAE;AAAA,UAC/G;AACA,cAAI,eAAe,SAAS,GAAG;AAC7B,qBAAS,KAAK,qBAAgB,eAAe,MAAM;AAAA,EAAQ,eAAe,IAAI,gBAAgB,EAAE,KAAK,IAAI,CAAC,EAAE;AAAA,UAC9G;AAEA,iBAAO;AAAA,YACL,SAAS;AAAA,cACP;AAAA,gBACE,MAAM;AAAA,gBACN,MAAM,mBAAmB,WAAW,WAAW;AAAA;AAAA,EAAO,OAAO,MAAM;AAAA;AAAA,EAA0B,SAAS,KAAK,MAAM,CAAC;AAAA,cACpH;AAAA,YACF;AAAA,UACF;AAAA,QACF,SAAS,OAAO;AACd,gBAAM,eAAe,iBAAiB,QAAQ,MAAM,UAAU;AAC9D,kBAAQ,MAAM,6BAA6B,KAAK;AAChD,iBAAO;AAAA,YACL,SAAS;AAAA,cACP;AAAA,gBACE,MAAM;AAAA,gBACN,MAAM,0BAA0B,YAAY;AAAA,cAC9C;AAAA,YACF;AAAA,YACA,SAAS;AAAA,UACX;AAAA,QACF;AAAA,MACF,WAAW,WAAW,SAAS,UAAU;AAEvC,cAAM,aAAa,QAAQ,OAAO,WAAW;AAC7C,cAAM,UAAU,QAAQ,OAAO,WAAW;AAE1C,YAAI,CAAC,YAAY;AACf,iBAAO;AAAA,YACL,SAAS;AAAA,cACP;AAAA,gBACE,MAAM;AAAA,gBACN,MAAM;AAAA,cACR;AAAA,YACF;AAAA,YACA,SAAS;AAAA,UACX;AAAA,QACF;AAEA,YAAI,CAAC,SAAS;AACZ,iBAAO;AAAA,YACL,SAAS;AAAA,cACP;AAAA,gBACE,MAAM;AAAA,gBACN,MAAM;AAAA,cACR;AAAA,YACF;AAAA,YACA,SAAS;AAAA,UACX;AAAA,QACF;AAEA,YAAI;AAEF,gBAAM,SAAS,MAAO,aAAqB;AAAA,YACzC;AAAA,YACA;AAAA,cACE,GAAG,cAAc,MAAM;AAAA,cACvB,aAAa,WAAW;AAAA,cACxB;AAAA,cACA;AAAA,YACF;AAAA,UACF;AAEA,iBAAO;AAAA,YACL,SAAS;AAAA,cACP;AAAA,gBACE,MAAM;AAAA,gBACN,MAAM,kBAAa,OAAO,IAAI,yBAAyB,WAAW,WAAW;AAAA,cAC/E,IAAI,KAAK,OAAO,SAAS,EAAE,YAAY,CAAC;AAAA;AAAA,cAExC;AAAA,YACF;AAAA,UACF;AAAA,QACF,SAAS,OAAO;AACd,gBAAM,eAAe,iBAAiB,QAAQ,MAAM,UAAU;AAC9D,kBAAQ,MAAM,+BAA+B,KAAK;AAClD,iBAAO;AAAA,YACL,SAAS;AAAA,cACP;AAAA,gBACE,MAAM;AAAA,gBACN,MAAM,0BAA0B,YAAY;AAAA,cAC9C;AAAA,YACF;AAAA,YACA,SAAS;AAAA,UACX;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,UAAM,aAAa,mBAAmB,KAAK,CAAC,OAAO,GAAG,SAAS,QAAQ;AACvE,QAAI,YAAY;AACd,UAAI;AACF,cAAM,SAAS,MAAM;AAAA,UACnB,WAAW;AAAA,UACX;AAAA,UACA;AAAA,QACF;AAEA,cAAM,aAAa,OAAO,SACvB,IAAI,CAAC,QAAQ,IAAI,QAAQ,IAAI,EAC7B,KAAK,MAAM;AAEd,eAAO;AAAA,UACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,WAAW,CAAC;AAAA,QAC9C;AAAA,MACF,SAAS,OAAO;AACd,cAAM,eAAe,iBAAiB,QAAQ,MAAM,UAAU;AAC9D,gBAAQ,MAAM,SAAS,QAAQ,WAAW,KAAK;AAC/C,eAAO;AAAA,UACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,2BAA2B,YAAY,GAAG,CAAC;AAAA,UAC3E,SAAS;AAAA,QACX;AAAA,MACF;AAAA,IACF;AAGA,UAAM,IAAI,MAAM,iBAAiB,QAAQ,+EAA+E;AAAA,EAC1H,CAAC;AAGD,QAAM,YAAY,IAAI,qBAAqB;AAC3C,QAAM,OAAO,QAAQ,SAAS;AAE9B,UAAQ,MAAM,mCAAmC;AACjD,UAAQ,MAAM,qBAAqB,OAAO,QAAQ,gBAAgB,YAAY,EAAE;AAChF,UAAQ,MAAM,qBAAqB,OAAO,SAAS,EAAE;AACrD,UAAQ,MAAM,oEAAoE;AAGlF,QAAM,iBAAiB,CAAC,GAAG,MAAM,KAAK,WAAW,CAAC,EAAE,KAAK;AACzD,UAAQ,MAAM,4BAA4B,eAAe,KAAK,IAAI,CAAC,EAAE;AACrE,UAAQ,MAAM,4BAA4B,YAAY,IAAI,EAAE;AAI5D,cAAY,MAAM;AAAA,EAElB,GAAG,GAAK;AAGR,SAAO,IAAI,QAAc,MAAM;AAAA,EAG/B,CAAC;AACH;AAKA,eAAe,eACb,QACA,QAC8D;AAC9D,MAAI;AAEF,UAAM,SAAS,MAAM,OAAO,MAAM,0BAAiC,EAAE,KAAK,OAAO,CAAC;AAClF,QAAI,QAAQ;AACV,aAAO,EAAE,OAAO,MAAM,QAAQ,OAAO,OAAO;AAAA,IAC9C;AACA,WAAO,EAAE,OAAO,OAAO,OAAO,kBAAkB;AAAA,EAClD,SAAS,OAAO;AACd,WAAO;AAAA,MACL,OAAO;AAAA,MACP,OAAO,iBAAiB,QAAQ,MAAM,UAAU;AAAA,IAClD;AAAA,EACF;AACF;AAQA,eAAe,qBACb,QACA,OACiC;AACjC,MAAI;AAEF,UAAM,SAAS,MAAM,OAAO,MAAM,uCAA8C,EAAE,MAAM,CAAC;AACzF,QAAI,UAAU,OAAO,OAAO;AAC1B,aAAO;AAAA,QACL,OAAO;AAAA,QACP,WAAW,OAAO;AAAA,QAClB,aAAa,OAAO;AAAA,QACpB,SAAS,OAAO;AAAA,QAChB,SAAS,OAAO;AAAA,QAChB,WAAW,OAAO;AAAA,MACpB;AAAA,IACF;AACA,WAAO,EAAE,OAAO,OAAO,OAAO,mCAAmC;AAAA,EACnE,SAAS,OAAO;AACd,WAAO;AAAA,MACL,OAAO;AAAA,MACP,OAAO,iBAAiB,QAAQ,MAAM,UAAU;AAAA,IAClD;AAAA,EACF;AACF;AASA,eAAe,iCACb,QACA,QACwD;AACxD,MAAI;AAEF,UAAM,WAAW,MAAM,OAAO,MAAM,8CAAqD;AAAA,MACvF;AAAA,IACF,CAAC;AACD,WAAO;AAAA,EACT,SAAS,OAAO;AACd,UAAM,IAAI;AAAA,MACR,mDAAmD,iBAAiB,QAAQ,MAAM,UAAU,eAAe;AAAA,IAC7G;AAAA,EACF;AACF;AAMA,eAAe,iBACb,QACA,QACwD;AACxD,MAAI;AAEF,UAAM,WAAW,MAAM,OAAO,MAAM,8BAAqC;AAAA,MACvE;AAAA,IACF,CAAC;AACD,WAAO;AAAA,EACT,SAAS,OAAO;AACd,UAAM,IAAI;AAAA,MACR,6BAA6B,iBAAiB,QAAQ,MAAM,UAAU,eAAe;AAAA,IACvF;AAAA,EACF;AACF;;;AF51CA,eAAe,OAAO;AACpB,MAAI;AAEF,UAAM,OAAO,SAAS,QAAQ,KAAK,MAAM,CAAC,CAAC;AAC3C,UAAM,QAAQ,KAAK,QAAQ;AAI3B,UAAM,cAAc,KAAK,YAAY,KAAK;AAC1C,QAAI,mBAA6B,CAAC;AAClC,QAAI,aAAa;AACf,UAAI,MAAM,QAAQ,WAAW,GAAG;AAE9B,2BAAmB,YAAY,QAAQ,CAAC,MAAc,OAAO,CAAC,EAAE,MAAM,GAAG,CAAC;AAAA,MAC5E,OAAO;AAEL,2BAAmB,OAAO,WAAW,EAAE,MAAM,GAAG;AAAA,MAClD;AAEA,yBAAmB,iBAAiB,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,OAAO;AAAA,IACzE;AAIA,UAAM,SAAS,QAAQ,IAAI,eAAe,QAAQ,IAAI;AACtD,UAAM,eAAe,QAAQ,IAAI;AAIjC,UAAM,WAAW,QAAQ,IAAI;AAE7B,QAAI,CAAC,UAAU,CAAC,cAAc;AAC5B,cAAQ;AAAA,QACN;AAAA,MACF;AACA,cAAQ;AAAA,QACN;AAAA,MACF;AACA,cAAQ,MAAM,mFAAmF;AACjG,cAAQ,MAAM,mGAAmG;AACjH,cAAQ,KAAK,CAAC;AAAA,IAChB;AAGA,QAAI,QAAQ;AACV,cAAQ,MAAM,wCAAwC;AACtD,UAAI,cAAc;AAChB,gBAAQ,MAAM,+DAA+D;AAAA,MAC/E;AACA,UAAI,UAAU;AACZ,gBAAQ,MAAM,yBAAyB,QAAQ,EAAE;AAAA,MACnD;AAAA,IACF,OAAO;AACL,cAAQ,MAAM,8DAA8D;AAC5E,UAAI,UAAU;AACZ,gBAAQ,MAAM,qEAAqE;AAAA,MACrF;AAAA,IACF;AAGA,UAAM,eAAe,mBAAmB,KAAK;AAC7C,UAAM,YAAY,QACd,6CACA;AAGJ,UAAM,SAAuB;AAAA,MAC3B,QAAQ,UAAU;AAAA;AAAA,MAClB,cAAc,SAAS,SAAY;AAAA;AAAA,MACnC;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MACA,WAAW,UAAU,WAAW,WAAW;AAAA;AAAA,IAC7C;AAGA,UAAM,YAAY,QAAQ,YAAY;AAGtC,YAAQ,MAAM,2DAA2D;AAAA,EAC3E,SAAS,OAAO;AACd,YAAQ;AAAA,MACN;AAAA,MACA,iBAAiB,QAAQ,MAAM,UAAU;AAAA,IAC3C;AACA,QAAI,iBAAiB,SAAS,MAAM,OAAO;AACzC,cAAQ,MAAM,MAAM,KAAK;AAAA,IAC3B;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;AAGA,QAAQ,GAAG,UAAU,MAAM;AACzB,UAAQ,MAAM,oDAAoD;AAClE,UAAQ,KAAK,CAAC;AAChB,CAAC;AAED,QAAQ,GAAG,WAAW,MAAM;AAC1B,UAAQ,MAAM,qDAAqD;AACnE,UAAQ,KAAK,CAAC;AAChB,CAAC;AAED,KAAK;","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 --projects CLI argument (Ticket 144)\n // Can be: --projects p1,p2 OR --projects p1 --projects p2 OR -p p1,p2\n const projectsArg = argv.projects || argv.p;\n let selectedProjects: string[] = [];\n if (projectsArg) {\n if (Array.isArray(projectsArg)) {\n // Multiple --projects flags: [\"p1\", \"p2\"]\n selectedProjects = projectsArg.flatMap((p: string) => String(p).split(','));\n } else {\n // Single --projects flag with comma-separated: \"p1,p2\"\n selectedProjects = String(projectsArg).split(',');\n }\n // Clean up whitespace\n selectedProjects = selectedProjects.map((s) => s.trim()).filter(Boolean);\n }\n\n // Check for authentication (API key or project 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 projectToken = process.env.PPM_PROJECT_TOKEN;\n\n // PPM_AGENT: Optional agent identity selection (only with API key auth)\n // When set, uses the named project token as identity while API key provides access\n const ppmAgent = process.env.PPM_AGENT;\n\n if (!apiKey && !projectToken) {\n console.error(\n \"[MCP] ERROR: Missing authentication. Provide either PPM_API_KEY or PPM_PROJECT_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 projects\");\n console.error(\"[MCP] export PPM_PROJECT_TOKEN=ppt_... # Scoped access to one project (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 (projectToken) {\n console.error(\"[MCP] Note: PPM_PROJECT_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: Project Token (scoped access, tickets only)\");\n if (ppmAgent) {\n console.error(\"[MCP] Note: PPM_AGENT ignored when using PPM_PROJECT_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 projectToken: apiKey ? undefined : projectToken, // Only use token if no API key\n isDev,\n convexUrl,\n selectedProjects, // Project slugs to filter (empty = all projects)\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, ProjectTokenValidation } 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, fetchAccountScopedPromptMetadataForProject } 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 projectToken (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 project token by name to use as the actor identity.\n */\nfunction buildAuthArgs(config: ServerConfig): { apiKey?: string; projectToken?: 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.projectToken) {\n return { projectToken: config.projectToken };\n }\n return {};\n}\n\n/**\n * Built-in system tools (no global tools - all are project-scoped now)\n */\nconst SYSTEM_TOOLS: {\n name: string;\n description: string;\n inputSchema: object;\n promptContent: string;\n isSlashCommand: boolean; // true = appears as /ppm:name, false = tool only\n}[] = [\n // All tools are now project-scoped (see dynamic*Tools arrays below)\n];\n\n/**\n * Initialize and start the MCP server\n */\nexport async function startServer(\n config: ServerConfig,\n convexClient: ConvexHttpClient\n): Promise<void> {\n // Track auth mode and project scope\n let tokenProjectSlug: string | undefined;\n\n // Validate authentication - API key or project 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.projectToken) {\n // Project Token mode - scoped access\n console.error(\"[MCP] Validating project token...\");\n const validation = await validateProjectToken(convexClient, config.projectToken);\n if (!validation.valid) {\n throw new Error(`Invalid project token: ${validation.error}`);\n }\n tokenProjectSlug = validation.projectSlug;\n console.error(`[MCP] Project token validated: \"${validation.tokenName}\" for project \"${tokenProjectSlug}\"`);\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_PROJECT_TOKEN.\");\n }\n\n // Fetch account-scoped prompts (global MCP tools) and projects (for ticket tools)\n // Account-scoped prompts: Available as global tools (e.g., \"code-review\")\n // Projects: Needed for ticket tool registration (e.g., \"projects_tickets_work\")\n let accountScopedPrompts: Array<{ slug: string; description?: string }> = [];\n let projectsForTickets: Array<{ slug: string; description?: string }> = [];\n\n console.error(\"[MCP] Fetching prompt and project metadata...\");\n\n if (config.apiKey) {\n // API key mode - fetch account-scoped prompts and projects\n // If a single project is specified, use project-filtered query (Prompt Deselector feature)\n if (config.selectedProjects.length === 1) {\n // Single project: Use filtered query that respects project's excludedPromptIds\n accountScopedPrompts = await fetchAccountScopedPromptMetadataForProject(\n convexClient,\n config.apiKey,\n config.selectedProjects[0]\n );\n console.error(`[MCP] Using project-filtered prompt visibility for: ${config.selectedProjects[0]}`);\n } else {\n // No filter or multiple projects: Use standard query (all prompts visible)\n accountScopedPrompts = await fetchAccountScopedPromptMetadata(convexClient, config.apiKey);\n if (config.selectedProjects.length > 1) {\n console.error(`[MCP] Multiple projects specified - showing all prompts (no exclusion filtering)`);\n }\n }\n\n // Fetch projects for ticket tools\n // If --projects specified, only register ticket tools for those projects\n const allProjects = await fetchMcpProjects(convexClient, config.apiKey);\n if (config.selectedProjects.length > 0) {\n const selectedSet = new Set(config.selectedProjects);\n projectsForTickets = allProjects.filter((p) => selectedSet.has(p.slug));\n } else {\n projectsForTickets = allProjects;\n }\n } else if (config.projectToken && tokenProjectSlug) {\n // Project token mode - no prompts (token mode is tickets-only)\n // Note: Project tokens only have access to tickets, not account-scoped prompts\n console.error(\"[MCP] Project token mode: prompts not available (tickets only)\");\n projectsForTickets = [{ slug: tokenProjectSlug, name: tokenProjectSlug }];\n }\n\n console.error(`[MCP] Found ${accountScopedPrompts.length} account-scoped prompts (global tools)`);\n console.error(`[MCP] Found ${projectsForTickets.length} project(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 project info for ticket tools\n if (tokenProjectSlug) {\n console.error(`[MCP] Ticket project scope: ${tokenProjectSlug} (token-scoped)`);\n } else if (config.selectedProjects.length > 0) {\n console.error(`[MCP] Ticket project filter: ${config.selectedProjects.join(', ')}`);\n } else {\n console.error(`[MCP] Ticket projects: ALL (${projectsForTickets.map(p => p.slug).join(', ')})`);\n }\n\n // Build dynamic ticket tools per project\n // Ticket tools remain project-scoped (e.g., \"projects_tickets_work\")\n const projectSlugs = new Set(projectsForTickets.map((p) => p.slug));\n const dynamicTicketTools: { name: string; description: string; slashDescription: string; projectSlug: string; type: 'work' | 'close' | 'create' | 'search' | 'get' | 'list' | 'update' }[] = [];\n\n for (const projectSlug of projectSlugs) {\n // Unified work command (replaces both tickets_open and tickets_work)\n dynamicTicketTools.push({\n name: `${projectSlug}_tickets_work`,\n description: `Get work from the \"${projectSlug}\" project. With no args: gets next ticket from open queue. With ticket slug/number: opens or resumes that specific ticket.`,\n slashDescription: `Get next ticket from queue, or work on specific ticket by slug/number`,\n projectSlug,\n type: 'work',\n });\n dynamicTicketTools.push({\n name: `${projectSlug}_tickets_close`,\n description: `Mark a working ticket as completed in the \"${projectSlug}\" project`,\n slashDescription: `Mark a working ticket as completed`,\n projectSlug,\n type: 'close',\n });\n dynamicTicketTools.push({\n name: `${projectSlug}_tickets_create`,\n description: `Create a new ticket in the \"${projectSlug}\" project queue`,\n slashDescription: `Create a new ticket in the backlog queue`,\n projectSlug,\n type: 'create',\n });\n dynamicTicketTools.push({\n name: `${projectSlug}_tickets_search`,\n description: `Search for tickets by content in the \"${projectSlug}\" project`,\n slashDescription: `Search for tickets by content`,\n projectSlug,\n type: 'search',\n });\n dynamicTicketTools.push({\n name: `${projectSlug}_tickets_get`,\n description: `Get a specific ticket by number or slug from \"${projectSlug}\" (read-only)`,\n slashDescription: `Get a specific ticket by number or slug (read-only)`,\n projectSlug,\n type: 'get',\n });\n dynamicTicketTools.push({\n name: `${projectSlug}_tickets_list`,\n description: `List active tickets in the \"${projectSlug}\" project (backlog + open + working)`,\n slashDescription: `List active tickets (backlog + open + working)`,\n projectSlug,\n type: 'list',\n });\n dynamicTicketTools.push({\n name: `${projectSlug}_tickets_update`,\n description: `Update a ticket in the \"${projectSlug}\" project by appending content with timestamp`,\n slashDescription: `Update a ticket by appending content with timestamp`,\n projectSlug,\n type: 'update',\n });\n }\n\n console.error(`[MCP] Registering ${dynamicTicketTools.length} ticket tools for ${projectSlugs.size} project(s)...`);\n\n // Build global system tools (not project-scoped)\n // run_prompt and system_prompts are now global since prompts are account-scoped\n const globalSystemTools: { name: string; description: string; slashDescription: string }[] = [\n {\n name: \"system_run_prompt\",\n description: \"Execute a prompt by slug. Use system_prompts to list available prompts.\",\n slashDescription: \"Execute a prompt by slug. Use system_prompts to list available prompts.\",\n },\n {\n name: \"system_prompts\",\n description: \"List all available prompts\",\n slashDescription: \"List all available prompts\",\n },\n ];\n\n console.error(`[MCP] Registering ${globalSystemTools.length} global system tools...`);\n\n // Build dynamic per-prompt tools (each prompt as its own tool)\n // Now global: Direct invocation like `code-review` instead of `projects:code-review`\n const dynamicPromptTools: {\n name: string; // \"code-review\" (global, no project prefix)\n description: string;\n promptSlug: string;\n }[] = [];\n\n for (const prompt of accountScopedPrompts) {\n // Build description with optional folder prefix\n const folderPrefix = prompt.folderSlug ? `[${prompt.folderSlug}] ` : '';\n const baseDescription = prompt.description || `Execute the \"${prompt.slug}\" prompt`;\n\n dynamicPromptTools.push({\n name: prompt.slug, // Global tool name, no project prefix\n description: folderPrefix + baseDescription,\n promptSlug: prompt.slug,\n });\n }\n\n console.error(`[MCP] Registering ${dynamicPromptTools.length} per-prompt tools (global)...`);\n\n // Note: Commands resource removed - users create slash commands directly from flattened prompts\n\n // Check for duplicate prompt names (now global, no project prefix)\n const promptNames = new Set<string>();\n const duplicates: string[] = [];\n accountScopedPrompts.forEach((p) => {\n if (promptNames.has(p.slug)) {\n duplicates.push(p.slug);\n }\n promptNames.add(p.slug);\n });\n\n if (duplicates.length > 0) {\n console.error(\n `[MCP] WARNING: Duplicate prompt slugs detected: ${duplicates.join(\", \")}. Only the first occurrence will be registered.`\n );\n }\n\n // Create MCP server\n const server = new Server(\n {\n name: \"ppm-mcp-server\",\n version: \"3.1.0\",\n },\n {\n capabilities: {\n prompts: {},\n tools: {},\n },\n }\n );\n\n // Register list_prompts handler\n // Prompts are now global (account-scoped), tickets remain project-scoped\n server.setRequestHandler(ListPromptsRequestSchema, async () => {\n // Build ticket prompt schemas (project-scoped)\n const ticketPromptSchemas = dynamicTicketTools.map((tt) => ({\n name: tt.name,\n description: tt.slashDescription,\n }));\n\n // Build global system prompt schemas\n const systemPromptsSchemas = globalSystemTools.map((st) => ({\n name: st.name,\n description: st.slashDescription,\n }));\n\n return {\n prompts: [\n ...systemPromptsSchemas, // system_prompts, system_run_prompt (global)\n ...ticketPromptSchemas, // project_tickets_* (project-scoped)\n ],\n };\n });\n\n // Register get_prompt handler\n server.setRequestHandler(GetPromptRequestSchema, async (request) => {\n const promptName = request.params.name;\n\n // Check for system tools first (as prompts for slash command access)\n const systemTool = SYSTEM_TOOLS.find((st) => st.name === promptName);\n if (systemTool) {\n return {\n description: systemTool.description,\n messages: [\n {\n role: \"user\" as const,\n content: {\n type: \"text\" as const,\n text: systemTool.promptContent,\n },\n },\n ],\n };\n }\n\n // Check for ticket commands (Ticket 135, 149, 153 - handle as slash commands)\n // Handle exact matches and _work with optional argument (e.g., \"project_tickets_work 102\")\n const ticketTool = dynamicTicketTools.find((tt) => tt.name === promptName);\n const ticketWorkMatch = promptName.match(/^(.+)_tickets_work\\s+(.+)$/);\n\n if (ticketTool || ticketWorkMatch) {\n let promptContent: string;\n let description: string;\n\n if (ticketWorkMatch) {\n // Handle tickets_work with specific ticket argument\n const projectSlug = ticketWorkMatch[1];\n const ticketArg = ticketWorkMatch[2].trim();\n description = `Work on ticket \"${ticketArg}\" from \"${projectSlug}\"`;\n promptContent = `Get work on ticket \"${ticketArg}\" from the \"${projectSlug}\" project.\\n\\nCall the \\`${projectSlug}_tickets_work\\` tool with ticketSlug: \"${ticketArg}\".\\n\\nThis will open the ticket if it's in the open queue, or resume it if already working.`;\n } else if (ticketTool!.type === 'work') {\n // Unified work command (replaces open + work)\n description = ticketTool!.description;\n promptContent = `Get work from the \"${ticketTool!.projectSlug}\" project.\\n\\nCall the \\`${ticketTool!.name}\\` tool to get the next ticket from the open queue.\\n\\nYou can also specify a ticket: /ppm:${ticketTool!.projectSlug}_tickets_work <number-or-slug>\\n\\nThis unified command handles both opening new tickets and resuming in-progress work.`;\n } else if (ticketTool!.type === 'create') {\n // Ticket 149: tickets:create slash command\n description = ticketTool!.description;\n promptContent = `Create a new ticket in the \"${ticketTool!.projectSlug}\" project queue.\n\n## How This Works\nThe user provides **instructions** (not raw content). You interpret those instructions to generate the ticket.\n\n## Instructions\n1. **Read the user's request** - they may reference a file, ask you to summarize a session, or describe what they want\n2. **Process the input** - read files, extract relevant sections, summarize as needed\n3. **Generate ticket content** with a clear, descriptive title as the first line\n4. **Call the tool** with the generated content\n\n## Content Format\n\\`\\`\\`\n[Clear descriptive title - this becomes the slug]\n\n[Body content - tasks, description, context, etc.]\n\\`\\`\\`\n\n## Examples\n- \"create a ticket from /path/to/plan.md\" → Read file, use as ticket content\n- \"summarize our brainstorm into a ticket\" → Extract key points from conversation\n- \"create a ticket for the auth bug we discussed\" → Generate from session context\n- \"just the tasks from this file\" → Extract only task items\n\nCall the \\`${ticketTool!.name}\\` tool with the final generated content.`;\n } else if (ticketTool!.type === 'close') {\n description = ticketTool!.description;\n promptContent = `Close a working ticket in the \"${ticketTool!.projectSlug}\" project.\\n\\nCall the \\`${ticketTool!.name}\\` tool with the ticket number or slug.`;\n } else {\n // Fallback for unknown type\n description = ticketTool!.description;\n promptContent = `Use the \\`${ticketTool!.name}\\` tool.`;\n }\n\n return {\n description,\n messages: [\n {\n role: \"user\" as const,\n content: {\n type: \"text\" as const,\n text: promptContent,\n },\n },\n ],\n };\n }\n\n // Check for global system_prompts command\n if (promptName === \"system_prompts\") {\n return {\n description: \"List all available prompts\",\n messages: [\n {\n role: \"user\" as const,\n content: {\n type: \"text\" as const,\n text: `List all available prompts.\n\nCall the \\`system_prompts\\` tool with optional \\`search\\` parameter to filter results.`,\n },\n },\n ],\n };\n }\n\n // Check for global system_run_prompt command (with optional prompt slug argument)\n const runPromptMatch = promptName.match(/^system_run_prompt\\s+(.+)$/);\n\n if (promptName === \"system_run_prompt\" || runPromptMatch) {\n let promptContent: string;\n let description: string;\n\n if (runPromptMatch) {\n // Handle run_prompt with specific prompt slug argument\n const promptSlug = runPromptMatch[1].trim();\n description = `Execute prompt \"${promptSlug}\"`;\n promptContent = `Execute the \"${promptSlug}\" prompt.\\n\\nCall the \\`system_run_prompt\\` tool with slug: \"${promptSlug}\".`;\n } else {\n // Base run_prompt without argument - show help\n description = \"Execute a prompt by slug\";\n promptContent = `Execute a prompt by slug.\n\n## Usage\nCall the \\`system_run_prompt\\` tool with the prompt slug.\n\n## Available Prompts\nUse \\`system_prompts\\` to list all available prompts.\n\n## Example\n/ppm:system_run_prompt code-review\n\nThis will execute the \"code-review\" prompt.`;\n }\n\n return {\n description,\n messages: [\n {\n role: \"user\" as const,\n content: {\n type: \"text\" as const,\n text: promptContent,\n },\n },\n ],\n };\n }\n\n // Note: Command handling removed - commands resource eliminated\n\n // Unknown prompt\n throw new Error(`Unknown prompt: ${promptName}. Use system_run_prompt to execute prompts.`);\n });\n\n // Register list_tools handler\n // Note: Per-prompt tool registration removed in favor of project-scoped run_prompt tools\n // This reduces token overhead and allows dynamic prompt discovery without server restart\n server.setRequestHandler(ListToolsRequestSchema, async () => {\n // Build tools array: system tools + ticket tools + project:run_prompt tools\n // Individual prompts no longer registered as separate tools\n const tools = [\n // System tools with full input schemas\n ...SYSTEM_TOOLS.map((st) => ({\n name: st.name,\n description: st.description,\n inputSchema: st.inputSchema,\n })),\n // Dynamic ticket tools per project (Ticket 135, 149, 151, 153, unified work)\n ...dynamicTicketTools.map((tt) => {\n // Build inputSchema based on tool type\n let inputSchema: { type: \"object\"; properties: Record<string, object>; required: string[] };\n\n if (tt.type === 'close') {\n // close requires a ticketSlug\n inputSchema = {\n type: \"object\" as const,\n properties: {\n ticketSlug: {\n type: \"string\",\n description: \"Ticket number (e.g., '102') or full slug (e.g., '102-fix-auth')\",\n },\n },\n required: [\"ticketSlug\"],\n };\n } else if (tt.type === 'work') {\n // Unified work command (replaces open + work)\n inputSchema = {\n type: \"object\" as const,\n properties: {\n ticketSlug: {\n type: \"string\",\n description: \"Optional: Ticket number (e.g., '102') or full slug. If not provided, gets next ticket from open queue.\",\n },\n },\n required: [],\n };\n } else if (tt.type === 'create') {\n inputSchema = {\n type: \"object\" as const,\n properties: {\n content: {\n type: \"string\",\n description: \"The generated ticket content. First line should be a clear descriptive title (becomes the slug). Body contains tasks, description, context as needed.\",\n },\n },\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 project filter)\n let filteredPrompts = [...accountScopedPrompts];\n\n // Filter by search term if provided\n if (searchTerm) {\n const lowerSearch = searchTerm.toLowerCase();\n filteredPrompts = filteredPrompts.filter(\n (p) =>\n p.slug.toLowerCase().includes(lowerSearch) ||\n p.description?.toLowerCase().includes(lowerSearch)\n );\n }\n\n // Sort prompts by slug\n filteredPrompts.sort((a, b) => a.slug.localeCompare(b.slug));\n\n // Build user prompts list\n const userPromptList = filteredPrompts\n .map((p) => {\n const desc = p.description || \"No description\";\n return `• ${p.slug}\\n Description: ${desc}`;\n })\n .join(\"\\n\\n\");\n\n const summary = searchTerm\n ? `Found ${filteredPrompts.length} prompt(s) matching \"${searchTerm}\":`\n : `Available prompts (${filteredPrompts.length} total):`;\n\n return {\n content: [\n {\n type: \"text\",\n text: userPromptList\n ? `${summary}\\n\\n${userPromptList}`\n : `${summary}\\n\\nNo prompts found.`,\n },\n ],\n };\n }\n\n // Handle 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 projectSlug: ticketTool.projectSlug,\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 project \"${ticketTool.projectSlug}\".`\n : `No open tickets in project \"${ticketTool.projectSlug}\".`;\n return {\n content: [\n {\n type: \"text\",\n text: message,\n },\n ],\n };\n }\n\n // Handle access denied response\n if ('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 projectSlug: ticketTool.projectSlug,\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 project \"${ticketTool.projectSlug}\".\n\nPosition: #${result.position} of ${result.totalBacklog} backlog tickets\nPreview: ${result.preview}\n\n_Ticket created in backlog. Use \\`tickets_work ${result.slug}\\` to move it to working status._`,\n },\n ],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] tickets_create error:`, error);\n return {\n content: [\n {\n type: \"text\",\n text: `Error creating ticket: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n } else if (ticketTool.type === 'close') {\n // Ticket 151: Handle tickets:close\n const 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 projectSlug: ticketTool.projectSlug,\n ticketSlug: ticketSlug,\n }\n ) as CloseTicketResult;\n\n return {\n content: [\n {\n type: \"text\",\n text: `✅ Ticket [${result.slug}] closed in project \"${ticketTool.projectSlug}\".\n\nClosed at: ${new Date(result.closedAt).toISOString()}\n\n_Reminder: Ensure all embedded \\`[RUN_PROMPT ...]\\` directives were executed before closing._`,\n },\n ],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] tickets_close error:`, error);\n return {\n content: [\n {\n type: \"text\",\n text: `Error closing ticket: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n } else if (ticketTool.type === 'search') {\n // Ticket 155: Handle tickets:search\n const 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 projectSlug: ticketTool.projectSlug,\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 project \"${ticketTool.projectSlug}\".`,\n },\n ],\n };\n }\n\n // Format as list with status badges\n const formattedList = result\n .map((t) => {\n const statusBadge = t.status === 'open' ? '🟢' : t.status === 'working' ? '🟡' : t.status === 'closed' ? '⚫' : '⚪';\n const num = t.ticketNumber ? `#${t.ticketNumber}` : '';\n return `${statusBadge} ${num} ${t.slug}\\n ${t.matchSnippet}`;\n })\n .join('\\n\\n');\n\n return {\n content: [\n {\n type: \"text\",\n text: `Found ${result.length} ticket(s) matching \"${query}\":\\n\\n${formattedList}`,\n },\n ],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] tickets_search error:`, error);\n return {\n content: [\n {\n type: \"text\",\n text: `Error searching tickets: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n } else if (ticketTool.type === 'get') {\n // Ticket 155: Handle tickets:get (read-only inspection)\n const 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 projectSlug: ticketTool.projectSlug,\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 project \"${ticketTool.projectSlug}\".`,\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 projectSlug: ticketTool.projectSlug,\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 project \"${ticketTool.projectSlug}\". All tickets are closed or archived.`,\n },\n ],\n };\n }\n\n // Group by status for clear display\n const openTickets = result.filter(t => t.status === 'open');\n const workingTickets = result.filter(t => t.status === 'working');\n const backlogTickets = result.filter(t => t.status === 'backlog');\n\n const formatTicketLine = (t: typeof result[0]) => {\n const num = t.ticketNumber ? `#${t.ticketNumber}` : '';\n return ` ${t.position}. ${num} ${t.slug}\\n ${t.preview}`;\n };\n\n const sections: string[] = [];\n\n if (openTickets.length > 0) {\n sections.push(`**🟢 Open (${openTickets.length})**\\n${openTickets.map(formatTicketLine).join('\\n')}`);\n }\n if (workingTickets.length > 0) {\n sections.push(`**🟡 Working (${workingTickets.length})**\\n${workingTickets.map(formatTicketLine).join('\\n')}`);\n }\n if (backlogTickets.length > 0) {\n sections.push(`**⚪ Backlog (${backlogTickets.length})**\\n${backlogTickets.map(formatTicketLine).join('\\n')}`);\n }\n\n return {\n content: [\n {\n type: \"text\",\n text: `# Active Queue: ${ticketTool.projectSlug}\\n\\n${result.length} ticket(s) in queue\\n\\n${sections.join('\\n\\n')}`,\n },\n ],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] tickets_list error:`, error);\n return {\n content: [\n {\n type: \"text\",\n text: `Error listing tickets: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n } else if (ticketTool.type === 'update') {\n // Ticket 293: Handle tickets:update (append content to ticket)\n const 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 projectSlug: ticketTool.projectSlug,\n ticketSlug,\n content,\n }\n ) as UpdateTicketResult;\n\n return {\n content: [\n {\n type: \"text\",\n text: `✅ Ticket [${result.slug}] updated in project \"${ticketTool.projectSlug}\".\nUpdated at: ${new Date(result.updatedAt).toISOString()}\n_Ticket content has been appended with your update._`,\n },\n ],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] tickets_update error:`, error);\n return {\n content: [\n {\n type: \"text\",\n text: `Error updating ticket: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n }\n }\n\n // Handle per-prompt tool invocations (e.g., \"code-review\" - now global)\n const promptTool = dynamicPromptTools.find((pt) => pt.name === toolName);\n if (promptTool) {\n try {\n const result = await fetchAndExecuteAccountScopedPrompt(\n promptTool.promptSlug,\n config,\n convexClient\n );\n\n const promptText = result.messages\n .map((msg) => msg.content.text)\n .join('\\n\\n');\n\n return {\n content: [{ type: \"text\", text: promptText }],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n console.error(`[MCP] ${toolName} error:`, error);\n return {\n content: [{ type: \"text\", text: `Error executing prompt: ${errorMessage}` }],\n isError: true,\n };\n }\n }\n\n // Unknown tool\n throw new Error(`Unknown tool: ${toolName}. Use system_run_prompt to execute prompts by name, or check available tools.`);\n });\n\n // Start server with stdio transport\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n console.error(\"[MCP] Server started successfully\");\n console.error(`[MCP] Deployment: ${config.isDev ? \"DEVELOPMENT\" : \"PRODUCTION\"}`);\n console.error(`[MCP] Convex URL: ${config.convexUrl}`);\n console.error(`[MCP] Data mode: REAL-TIME (fetches fresh data on each invocation)`);\n\n // List all prompts\n const allPromptNames = [...Array.from(promptNames)].sort();\n console.error(`[MCP] Prompts available: ${allPromptNames.join(\", \")}`);\n console.error(`[MCP] - Total prompts: ${promptNames.size}`);\n\n // Keep the event loop alive with a heartbeat\n // This prevents Node from exiting when there are no active handles\n setInterval(() => {\n // Heartbeat every 60 seconds to keep process alive\n }, 60000);\n\n // Return a promise that never resolves to keep the server running\n return new Promise<void>(() => {\n // The transport handles stdin/stdout communication\n // The interval above keeps the event loop active\n });\n}\n\n/**\n * Validate 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 project token with Convex\n *\n * Project tokens provide scoped access to a single project for ticket operations.\n * Returns project details if valid, error if invalid/revoked.\n */\nasync function validateProjectToken(\n client: ConvexHttpClient,\n token: string\n): Promise<ProjectTokenValidation> {\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const result = await client.query(\"project_tokens:validateProjectToken\" as any, { token });\n if (result && result.valid) {\n return {\n valid: true,\n projectId: result.projectId,\n projectSlug: result.projectSlug,\n ownerId: result.ownerId,\n tokenId: result.tokenId,\n tokenName: result.tokenName,\n };\n }\n return { valid: false, error: \"Invalid or revoked project token\" };\n } catch (error) {\n return {\n valid: false,\n error: error instanceof Error ? error.message : \"Unknown error\",\n };\n }\n}\n\n// Note: fetchMcpPromptMetadata 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 projectId - 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 projects for ticket tool registration\n * Separate from prompts since prompts are now global\n */\nasync function fetchMcpProjects(\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 projects = await client.query(\"mcp_prompts:getMcpProjects\" as any, {\n apiKey,\n });\n return projects;\n } catch (error) {\n throw new Error(\n `Failed to fetch projects: ${error instanceof Error ? error.message : \"Unknown error\"}`\n );\n }\n}\n","import type { ConvexHttpClient } from \"convex/browser\";\nimport type { 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 (project slug, folder path, or prompt slug)\n * @returns Sanitized string safe for MCP tool names\n */\nexport function sanitizeForMcp(str: string): string {\n return str\n .trim() // Remove leading/trailing whitespace first\n .toLowerCase() // Convert to lowercase for consistency\n .replace(/[^a-z0-9_-]/g, '-') // Replace ALL invalid chars (including spaces!) with hyphens\n .replace(/-+/g, '-') // Collapse multiple consecutive hyphens into one\n .replace(/^-+|-+$/g, '') // Remove leading and trailing hyphens\n .trim(); // Final trim to ensure no whitespace\n}\n\n// Note: The following legacy functions have been removed as prompts are now account-scoped:\n// - buildPromptName (was project:prompt format, now prompts are global)\n// - buildPromptNameFromMetadata (was project:prompt format)\n// - buildPromptSchema (used buildPromptName)\n// - buildPromptHandler (used project-scoped getMcpPrompts query)\n// - fetchAndExecutePrompt (used project-scoped getMcpPromptBySlug query)\n//\n// Use fetchAndExecuteAccountScopedPrompt instead for all prompt execution.\n\n/**\n * Custom error class for ambiguous prompt matches\n * Contains suggestions for the user to choose from\n */\nexport class AmbiguousPromptError extends Error {\n suggestions: string[];\n\n constructor(promptSlug: string, suggestions: string[]) {\n super(`Multiple prompts match \"${promptSlug}\". Please be more specific.`);\n this.name = \"AmbiguousPromptError\";\n this.suggestions = suggestions;\n }\n}\n\n/**\n * Fetch account-scoped prompt metadata filtered by project exclusions\n * (Prompt Deselector feature)\n *\n * When a single project is specified via --projects flag, uses the project-filtered query\n * to exclude prompts that are hidden for that project.\n *\n * @param client - Convex HTTP client\n * @param apiKey - User's API key\n * @param projectSlug - Project slug to filter exclusions (optional)\n * @returns Account-scoped prompts (filtered if projectSlug provided)\n */\nexport async function fetchAccountScopedPromptMetadataForProject(\n client: ConvexHttpClient,\n apiKey: string,\n projectSlug: 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:getAccountScopedPromptMetadataForProject\" as any, {\n apiKey,\n projectSlug,\n });\n return metadata;\n } catch (error) {\n throw new Error(\n `Failed to fetch account-scoped prompt metadata for project \"${projectSlug}\": ${error instanceof Error ? error.message : \"Unknown error\"}`\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 project prefix required).\n * This is for prompts that don't have a projectId.\n *\n * Matching algorithm same as fetchAndExecutePrompt but without project 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@promptprojectmanager/mcp-server",
3
- "version": "4.0.2",
3
+ "version": "4.1.0",
4
4
  "description": "MCP server that exposes Prompt Project Manager project prompts as slash commands",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",