@promptprojectmanager/mcp-server 4.2.0 → 4.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -22,6 +22,60 @@ import {
22
22
  ListToolsRequestSchema
23
23
  } from "@modelcontextprotocol/sdk/types.js";
24
24
 
25
+ // src/types.ts
26
+ function isAccessDenied(result) {
27
+ return typeof result === "object" && result !== null && "accessDenied" in result && result.accessDenied === true;
28
+ }
29
+ function parseWorkArgs(args) {
30
+ const parsed = args;
31
+ return {
32
+ ticketSlug: typeof parsed?.ticketSlug === "string" ? parsed.ticketSlug : void 0
33
+ };
34
+ }
35
+ function parseCloseArgs(args) {
36
+ const parsed = args;
37
+ const ticketSlug = typeof parsed?.ticketSlug === "string" ? parsed.ticketSlug : void 0;
38
+ if (!ticketSlug) return null;
39
+ return { ticketSlug };
40
+ }
41
+ function parseCreateArgs(args) {
42
+ const parsed = args;
43
+ const content = typeof parsed?.content === "string" ? parsed.content : void 0;
44
+ if (!content) return null;
45
+ return { content };
46
+ }
47
+ function parseSearchArgs(args) {
48
+ const parsed = args;
49
+ const query = typeof parsed?.query === "string" ? parsed.query : void 0;
50
+ if (!query || query.length < 3) return null;
51
+ return { query };
52
+ }
53
+ function parseGetArgs(args) {
54
+ const parsed = args;
55
+ const ticketSlug = typeof parsed?.ticketSlug === "string" ? parsed.ticketSlug : void 0;
56
+ if (!ticketSlug) return null;
57
+ return { ticketSlug };
58
+ }
59
+ function parseUpdateArgs(args) {
60
+ const parsed = args;
61
+ const ticketSlug = typeof parsed?.ticketSlug === "string" ? parsed.ticketSlug : void 0;
62
+ const content = typeof parsed?.content === "string" ? parsed.content : void 0;
63
+ if (!ticketSlug || !content) return null;
64
+ return { ticketSlug, content };
65
+ }
66
+ function parseRunPromptArgs(args) {
67
+ const parsed = args;
68
+ const slug = typeof parsed?.slug === "string" ? parsed.slug : void 0;
69
+ if (!slug) return null;
70
+ return { slug };
71
+ }
72
+ function parseSystemPromptsArgs(args) {
73
+ const parsed = args;
74
+ return {
75
+ search: typeof parsed?.search === "string" ? parsed.search : void 0
76
+ };
77
+ }
78
+
25
79
  // src/prompt-builder.ts
26
80
  var AmbiguousPromptError = class extends Error {
27
81
  suggestions;
@@ -31,24 +85,13 @@ var AmbiguousPromptError = class extends Error {
31
85
  this.suggestions = suggestions;
32
86
  }
33
87
  };
34
- async function fetchAccountScopedPromptMetadataForProject(client, apiKey, projectSlug) {
35
- try {
36
- const metadata = await client.query("mcp_prompts:getAccountScopedPromptMetadataForProject", {
37
- apiKey,
38
- projectSlug
39
- });
40
- return metadata;
41
- } catch (error) {
42
- throw new Error(
43
- `Failed to fetch account-scoped prompt metadata for project "${projectSlug}": ${error instanceof Error ? error.message : "Unknown error"}`
44
- );
45
- }
46
- }
47
88
  async function fetchAccountScopedPromptMetadataByToken(client, projectToken) {
48
89
  try {
49
- const metadata = await client.query("mcp_prompts:getAccountScopedPromptMetadataByToken", {
50
- projectToken
51
- });
90
+ const typedClient = client;
91
+ const metadata = await typedClient.query(
92
+ "mcp_prompts:getAccountScopedPromptMetadataByToken",
93
+ { projectToken }
94
+ );
52
95
  return metadata;
53
96
  } catch (error) {
54
97
  throw new Error(
@@ -57,21 +100,14 @@ async function fetchAccountScopedPromptMetadataByToken(client, projectToken) {
57
100
  }
58
101
  }
59
102
  async function fetchAndExecuteAccountScopedPrompt(promptSlug, config, convexClient) {
60
- if (!config.apiKey && !config.projectToken) {
61
- throw new Error("Authentication required: provide API key or project token");
62
- }
63
- let result;
64
- if (config.apiKey) {
65
- result = await convexClient.query("mcp_prompts:getAccountScopedPromptBySlug", {
66
- apiKey: config.apiKey,
67
- promptSlug
68
- });
69
- } else {
70
- result = await convexClient.query("mcp_prompts:getAccountScopedPromptBySlugByToken", {
103
+ const typedClient = convexClient;
104
+ const result = await typedClient.query(
105
+ "mcp_prompts:getAccountScopedPromptBySlugByToken",
106
+ {
71
107
  projectToken: config.projectToken,
72
108
  promptSlug
73
- });
74
- }
109
+ }
110
+ );
75
111
  if (!result) {
76
112
  throw new Error(
77
113
  `Prompt "${promptSlug}" not found. Use system:prompts to list available prompts.`
@@ -102,87 +138,34 @@ async function fetchAndExecuteAccountScopedPrompt(promptSlug, config, convexClie
102
138
 
103
139
  // src/server.ts
104
140
  function buildAuthArgs(config) {
105
- if (config.apiKey) {
106
- return {
107
- apiKey: config.apiKey,
108
- agentName: config.agentName
109
- // Pass agent name for identity resolution
110
- };
111
- }
112
- if (config.projectToken) {
113
- return { projectToken: config.projectToken };
114
- }
115
- return {};
141
+ return { projectToken: config.projectToken };
116
142
  }
117
143
  var SYSTEM_TOOLS = [
118
144
  // All tools are now project-scoped (see dynamic*Tools arrays below)
119
145
  ];
120
- async function startServer(config, convexClient) {
121
- let tokenProjectSlug;
122
- if (config.apiKey) {
123
- console.error("[MCP] Validating API key...");
124
- const validation = await validateApiKey(convexClient, config.apiKey);
125
- if (!validation.valid) {
126
- throw new Error(`Invalid API key: ${validation.error}`);
127
- }
128
- console.error(`[MCP] API key validated for user: ${validation.userId}`);
129
- } else if (config.projectToken) {
130
- console.error("[MCP] Validating project token...");
131
- const validation = await validateProjectToken(convexClient, config.projectToken);
132
- if (!validation.valid) {
133
- throw new Error(`Invalid project token: ${validation.error}`);
134
- }
135
- tokenProjectSlug = validation.projectSlug;
136
- console.error(`[MCP] Project token validated: "${validation.tokenName}" for project "${tokenProjectSlug}"`);
137
- } else {
138
- throw new Error("No authentication provided. Set PPM_API_KEY or PPM_PROJECT_TOKEN.");
139
- }
140
- let accountScopedPrompts = [];
141
- let projectsForTickets = [];
142
- console.error("[MCP] Fetching prompt and project metadata...");
143
- if (config.apiKey) {
144
- if (config.selectedProjects.length === 1) {
145
- accountScopedPrompts = await fetchAccountScopedPromptMetadataForProject(
146
- convexClient,
147
- config.apiKey,
148
- config.selectedProjects[0]
149
- );
150
- console.error(`[MCP] Using project-filtered prompt visibility for: ${config.selectedProjects[0]}`);
151
- } else {
152
- accountScopedPrompts = await fetchAccountScopedPromptMetadata(convexClient, config.apiKey);
153
- if (config.selectedProjects.length > 1) {
154
- console.error(`[MCP] Multiple projects specified - showing all prompts (no exclusion filtering)`);
155
- }
156
- }
157
- const allProjects = await fetchMcpProjects(convexClient, config.apiKey);
158
- if (config.selectedProjects.length > 0) {
159
- const selectedSet = new Set(config.selectedProjects);
160
- projectsForTickets = allProjects.filter((p) => selectedSet.has(p.slug));
161
- } else {
162
- projectsForTickets = allProjects;
163
- }
164
- } else if (config.projectToken && tokenProjectSlug) {
165
- accountScopedPrompts = await fetchAccountScopedPromptMetadataByToken(
166
- convexClient,
167
- config.projectToken
168
- );
169
- console.error(`[MCP] Project token mode: loaded ${accountScopedPrompts.length} prompts`);
170
- projectsForTickets = [{ slug: tokenProjectSlug, name: tokenProjectSlug }];
146
+ async function startServer(config, convexClientRaw) {
147
+ const convexClient = convexClientRaw;
148
+ console.error("[MCP] Validating project token...");
149
+ const validation = await validateProjectToken(convexClient, config.projectToken);
150
+ if (!validation.valid) {
151
+ throw new Error(`Invalid project token: ${validation.error}`);
171
152
  }
153
+ const tokenProjectSlug = validation.projectSlug;
154
+ console.error(`[MCP] Project token validated: "${validation.tokenName}" for project "${tokenProjectSlug}"`);
155
+ console.error("[MCP] Fetching prompt metadata...");
156
+ const accountScopedPrompts = await fetchAccountScopedPromptMetadataByToken(
157
+ convexClient,
158
+ config.projectToken
159
+ );
160
+ console.error(`[MCP] Project token mode: loaded ${accountScopedPrompts.length} prompts`);
161
+ const projectsForTickets = [{ slug: tokenProjectSlug, name: tokenProjectSlug }];
172
162
  console.error(`[MCP] Found ${accountScopedPrompts.length} account-scoped prompts (global tools)`);
173
- console.error(`[MCP] Found ${projectsForTickets.length} project(s) for ticket tools`);
174
- if (accountScopedPrompts.length === 0 && config.apiKey) {
163
+ console.error(`[MCP] Ticket project scope: ${tokenProjectSlug} (token-scoped)`);
164
+ if (accountScopedPrompts.length === 0) {
175
165
  console.error(
176
- "[MCP] WARNING: No account-scoped prompts found. Create prompts in the 'prompts' section to expose them via MCP."
166
+ "[MCP] WARNING: No prompts found. Create prompts in the 'prompts' section to expose them via MCP."
177
167
  );
178
168
  }
179
- if (tokenProjectSlug) {
180
- console.error(`[MCP] Ticket project scope: ${tokenProjectSlug} (token-scoped)`);
181
- } else if (config.selectedProjects.length > 0) {
182
- console.error(`[MCP] Ticket project filter: ${config.selectedProjects.join(", ")}`);
183
- } else {
184
- console.error(`[MCP] Ticket projects: ALL (${projectsForTickets.map((p) => p.slug).join(", ")})`);
185
- }
186
169
  const projectSlugs = new Set(projectsForTickets.map((p) => p.slug));
187
170
  const dynamicTicketTools = [];
188
171
  for (const projectSlug of projectSlugs) {
@@ -595,8 +578,8 @@ This will execute the "code-review" prompt.`;
595
578
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
596
579
  const toolName = request.params.name;
597
580
  if (toolName === "system_run_prompt") {
598
- const promptSlug = request.params.arguments?.slug;
599
- if (!promptSlug) {
581
+ const parsedArgs = parseRunPromptArgs(request.params.arguments);
582
+ if (!parsedArgs) {
600
583
  return {
601
584
  content: [
602
585
  {
@@ -609,6 +592,7 @@ Use \`system_prompts\` to list available prompts.`
609
592
  isError: true
610
593
  };
611
594
  }
595
+ const { slug: promptSlug } = parsedArgs;
612
596
  try {
613
597
  const result = await fetchAndExecuteAccountScopedPrompt(
614
598
  promptSlug,
@@ -656,7 +640,7 @@ Example: \`system_run_prompt ${error.suggestions[0]}\``
656
640
  }
657
641
  }
658
642
  if (toolName === "system_prompts") {
659
- const searchTerm = request.params.arguments?.search;
643
+ const { search: searchTerm } = parseSystemPromptsArgs(request.params.arguments);
660
644
  let filteredPrompts = [...accountScopedPrompts];
661
645
  if (searchTerm) {
662
646
  const lowerSearch = searchTerm.toLowerCase();
@@ -687,7 +671,7 @@ No prompts found.`
687
671
  const ticketTool = dynamicTicketTools.find((tt) => tt.name === toolName);
688
672
  if (ticketTool) {
689
673
  if (ticketTool.type === "work") {
690
- const ticketSlug = request.params.arguments?.ticketSlug;
674
+ const { ticketSlug } = parseWorkArgs(request.params.arguments);
691
675
  try {
692
676
  const result = await convexClient.mutation(
693
677
  "mcp_tickets:workMcpTicket",
@@ -709,7 +693,7 @@ No prompts found.`
709
693
  ]
710
694
  };
711
695
  }
712
- if ("accessDenied" in result && result.accessDenied) {
696
+ if (isAccessDenied(result)) {
713
697
  return {
714
698
  content: [
715
699
  {
@@ -720,16 +704,17 @@ No prompts found.`
720
704
  isError: true
721
705
  };
722
706
  }
723
- const startedInfo = result.startedAt ? `
724
- Started: ${new Date(result.startedAt).toISOString()}` : "";
725
- const statusNote = result.wasOpened ? `_Ticket moved to working status. ${result.remainingTickets} ticket(s) remaining in queue._` : `_Resuming work on this ticket. ${result.remainingTickets} ticket(s) in queue._`;
707
+ const workResult = result;
708
+ const startedInfo = workResult.startedAt ? `
709
+ Started: ${new Date(workResult.startedAt).toISOString()}` : "";
710
+ const statusNote = workResult.wasOpened ? `_Ticket moved to working status. ${workResult.remainingTickets} ticket(s) remaining in queue._` : `_Resuming work on this ticket. ${workResult.remainingTickets} ticket(s) in queue._`;
726
711
  return {
727
712
  content: [
728
713
  {
729
714
  type: "text",
730
- text: `# Ticket: ${result.slug} [WORKING]${startedInfo}
715
+ text: `# Ticket: ${workResult.slug} [WORKING]${startedInfo}
731
716
 
732
- ${result.content}
717
+ ${workResult.content}
733
718
 
734
719
  ---
735
720
  ${statusNote}`
@@ -750,8 +735,8 @@ ${statusNote}`
750
735
  };
751
736
  }
752
737
  } else if (ticketTool.type === "create") {
753
- const content = request.params.arguments?.content;
754
- if (!content) {
738
+ const parsedArgs = parseCreateArgs(request.params.arguments);
739
+ if (!parsedArgs) {
755
740
  return {
756
741
  content: [
757
742
  {
@@ -762,6 +747,7 @@ ${statusNote}`
762
747
  isError: true
763
748
  };
764
749
  }
750
+ const { content } = parsedArgs;
765
751
  try {
766
752
  const result = await convexClient.mutation(
767
753
  "mcp_tickets:createMcpTicket",
@@ -798,8 +784,8 @@ _Ticket created in backlog. Use \`tickets_work ${result.slug}\` to move it to wo
798
784
  };
799
785
  }
800
786
  } else if (ticketTool.type === "close") {
801
- const ticketSlug = request.params.arguments?.ticketSlug;
802
- if (!ticketSlug) {
787
+ const parsedArgs = parseCloseArgs(request.params.arguments);
788
+ if (!parsedArgs) {
803
789
  return {
804
790
  content: [
805
791
  {
@@ -810,6 +796,7 @@ _Ticket created in backlog. Use \`tickets_work ${result.slug}\` to move it to wo
810
796
  isError: true
811
797
  };
812
798
  }
799
+ const { ticketSlug } = parsedArgs;
813
800
  try {
814
801
  const result = await convexClient.mutation(
815
802
  "mcp_tickets:closeMcpTicket",
@@ -845,8 +832,8 @@ _Reminder: Ensure all embedded \`[RUN_PROMPT ...]\` directives were executed bef
845
832
  };
846
833
  }
847
834
  } else if (ticketTool.type === "search") {
848
- const query = request.params.arguments?.query;
849
- if (!query || query.length < 3) {
835
+ const parsedArgs = parseSearchArgs(request.params.arguments);
836
+ if (!parsedArgs) {
850
837
  return {
851
838
  content: [
852
839
  {
@@ -857,6 +844,7 @@ _Reminder: Ensure all embedded \`[RUN_PROMPT ...]\` directives were executed bef
857
844
  isError: true
858
845
  };
859
846
  }
847
+ const { query } = parsedArgs;
860
848
  try {
861
849
  const result = await convexClient.query(
862
850
  "mcp_tickets:searchMcpTickets",
@@ -906,8 +894,8 @@ ${formattedList}`
906
894
  };
907
895
  }
908
896
  } else if (ticketTool.type === "get") {
909
- const ticketSlug = request.params.arguments?.ticketSlug;
910
- if (!ticketSlug) {
897
+ const parsedArgs = parseGetArgs(request.params.arguments);
898
+ if (!parsedArgs) {
911
899
  return {
912
900
  content: [
913
901
  {
@@ -918,6 +906,7 @@ ${formattedList}`
918
906
  isError: true
919
907
  };
920
908
  }
909
+ const { ticketSlug } = parsedArgs;
921
910
  try {
922
911
  const result = await convexClient.query(
923
912
  "mcp_tickets:getMcpTicket",
@@ -937,7 +926,7 @@ ${formattedList}`
937
926
  ]
938
927
  };
939
928
  }
940
- if ("accessDenied" in result && result.accessDenied) {
929
+ if (isAccessDenied(result)) {
941
930
  return {
942
931
  content: [
943
932
  {
@@ -948,20 +937,21 @@ ${formattedList}`
948
937
  isError: true
949
938
  };
950
939
  }
951
- const statusBadge = result.status.toUpperCase();
952
- const startedInfo = result.startedAt ? `
953
- Started: ${new Date(result.startedAt).toISOString()}` : "";
954
- const closedInfo = result.closedAt ? `
955
- Closed: ${new Date(result.closedAt).toISOString()}` : "";
956
- const assignedInfo = result.assignedTo ? `
957
- Assigned to: ${result.assignedTo}` : "\nUnassigned";
940
+ const getResult = result;
941
+ const statusBadge = getResult.status.toUpperCase();
942
+ const startedInfo = getResult.startedAt ? `
943
+ Started: ${new Date(getResult.startedAt).toISOString()}` : "";
944
+ const closedInfo = getResult.closedAt ? `
945
+ Closed: ${new Date(getResult.closedAt).toISOString()}` : "";
946
+ const assignedInfo = getResult.assignedTo ? `
947
+ Assigned to: ${getResult.assignedTo}` : "\nUnassigned";
958
948
  return {
959
949
  content: [
960
950
  {
961
951
  type: "text",
962
- text: `# Ticket: ${result.slug} [${statusBadge}]${startedInfo}${closedInfo}${assignedInfo}
952
+ text: `# Ticket: ${getResult.slug} [${statusBadge}]${startedInfo}${closedInfo}${assignedInfo}
963
953
 
964
- ${result.content}
954
+ ${getResult.content}
965
955
 
966
956
  ---
967
957
  _Read-only inspection. Use tickets_work to start working on this ticket._`
@@ -1047,30 +1037,44 @@ ${sections.join("\n\n")}`
1047
1037
  };
1048
1038
  }
1049
1039
  } else if (ticketTool.type === "update") {
1050
- const ticketSlug = request.params.arguments?.ticketSlug;
1051
- const content = request.params.arguments?.content;
1052
- if (!ticketSlug) {
1053
- return {
1054
- content: [
1055
- {
1056
- type: "text",
1057
- text: `Error: Missing ticketSlug parameter. Provide a ticket number (e.g., "102") or full slug.`
1058
- }
1059
- ],
1060
- isError: true
1061
- };
1062
- }
1063
- if (!content) {
1040
+ const parsedArgs = parseUpdateArgs(request.params.arguments);
1041
+ if (!parsedArgs) {
1042
+ const args = request.params.arguments;
1043
+ const hasTicketSlug = typeof args?.ticketSlug === "string" && args.ticketSlug;
1044
+ const hasContent = typeof args?.content === "string" && args.content;
1045
+ if (!hasTicketSlug) {
1046
+ return {
1047
+ content: [
1048
+ {
1049
+ type: "text",
1050
+ text: `Error: Missing ticketSlug parameter. Provide a ticket number (e.g., "102") or full slug.`
1051
+ }
1052
+ ],
1053
+ isError: true
1054
+ };
1055
+ }
1056
+ if (!hasContent) {
1057
+ return {
1058
+ content: [
1059
+ {
1060
+ type: "text",
1061
+ text: `Error: Missing content parameter. Provide the update content to append to the ticket.`
1062
+ }
1063
+ ],
1064
+ isError: true
1065
+ };
1066
+ }
1064
1067
  return {
1065
1068
  content: [
1066
1069
  {
1067
1070
  type: "text",
1068
- text: `Error: Missing content parameter. Provide the update content to append to the ticket.`
1071
+ text: `Error: Missing required parameters.`
1069
1072
  }
1070
1073
  ],
1071
1074
  isError: true
1072
1075
  };
1073
1076
  }
1077
+ const { ticketSlug, content } = parsedArgs;
1074
1078
  try {
1075
1079
  const result = await convexClient.mutation(
1076
1080
  "mcp_tickets:updateMcpTicket",
@@ -1143,23 +1147,13 @@ _Ticket content has been appended with your update._`
1143
1147
  return new Promise(() => {
1144
1148
  });
1145
1149
  }
1146
- async function validateApiKey(client, apiKey) {
1147
- try {
1148
- const result = await client.query("apiKeys:validateApiKey", { key: apiKey });
1149
- if (result) {
1150
- return { valid: true, userId: result.userId };
1151
- }
1152
- return { valid: false, error: "Invalid API key" };
1153
- } catch (error) {
1154
- return {
1155
- valid: false,
1156
- error: error instanceof Error ? error.message : "Unknown error"
1157
- };
1158
- }
1159
- }
1160
1150
  async function validateProjectToken(client, token) {
1161
1151
  try {
1162
- const result = await client.query("project_tokens:validateProjectToken", { token });
1152
+ const typedClient = client;
1153
+ const result = await typedClient.query(
1154
+ "project_tokens:validateProjectToken",
1155
+ { token }
1156
+ );
1163
1157
  if (result && result.valid) {
1164
1158
  return {
1165
1159
  valid: true,
@@ -1178,87 +1172,30 @@ async function validateProjectToken(client, token) {
1178
1172
  };
1179
1173
  }
1180
1174
  }
1181
- async function fetchAccountScopedPromptMetadata(client, apiKey) {
1182
- try {
1183
- const metadata = await client.query("mcp_prompts:getAccountScopedPromptMetadata", {
1184
- apiKey
1185
- });
1186
- return metadata;
1187
- } catch (error) {
1188
- throw new Error(
1189
- `Failed to fetch account-scoped prompt metadata: ${error instanceof Error ? error.message : "Unknown error"}`
1190
- );
1191
- }
1192
- }
1193
- async function fetchMcpProjects(client, apiKey) {
1194
- try {
1195
- const projects = await client.query("mcp_prompts:getMcpProjects", {
1196
- apiKey
1197
- });
1198
- return projects;
1199
- } catch (error) {
1200
- throw new Error(
1201
- `Failed to fetch projects: ${error instanceof Error ? error.message : "Unknown error"}`
1202
- );
1203
- }
1204
- }
1205
1175
 
1206
1176
  // src/index.ts
1207
1177
  async function main() {
1208
1178
  try {
1209
1179
  const argv = minimist(process.argv.slice(2));
1210
1180
  const isDev = argv.dev === true;
1211
- const projectsArg = argv.projects || argv.p;
1212
- let selectedProjects = [];
1213
- if (projectsArg) {
1214
- if (Array.isArray(projectsArg)) {
1215
- selectedProjects = projectsArg.flatMap((p) => String(p).split(","));
1216
- } else {
1217
- selectedProjects = String(projectsArg).split(",");
1218
- }
1219
- selectedProjects = selectedProjects.map((s) => s.trim()).filter(Boolean);
1220
- }
1221
- const apiKey = process.env.PPM_API_KEY || process.env.THEPROMPTEDITOR_API_KEY;
1222
1181
  const projectToken = process.env.PPM_PROJECT_TOKEN;
1223
- const ppmAgent = process.env.PPM_AGENT;
1224
- if (!apiKey && !projectToken) {
1182
+ if (!projectToken) {
1225
1183
  console.error(
1226
- "[MCP] ERROR: Missing authentication. Provide either PPM_API_KEY or PPM_PROJECT_TOKEN"
1184
+ "[MCP] ERROR: Missing authentication. Set PPM_PROJECT_TOKEN environment variable."
1227
1185
  );
1228
1186
  console.error(
1229
- "[MCP] Options:"
1187
+ "[MCP] Example:"
1230
1188
  );
1231
- console.error("[MCP] export PPM_API_KEY=your_api_key_here # Full access to all projects");
1232
- console.error("[MCP] export PPM_PROJECT_TOKEN=ppt_... # Scoped access to one project (tickets only)");
1189
+ console.error("[MCP] export PPM_PROJECT_TOKEN=wst_... # Get from project settings");
1233
1190
  process.exit(1);
1234
1191
  }
1235
- if (apiKey) {
1236
- console.error("[MCP] Auth mode: API Key (full access)");
1237
- if (projectToken) {
1238
- console.error("[MCP] Note: PPM_PROJECT_TOKEN ignored when PPM_API_KEY is set");
1239
- }
1240
- if (ppmAgent) {
1241
- console.error(`[MCP] Agent identity: ${ppmAgent}`);
1242
- }
1243
- } else {
1244
- console.error("[MCP] Auth mode: Project Token (scoped access, tickets only)");
1245
- if (ppmAgent) {
1246
- console.error("[MCP] Note: PPM_AGENT ignored when using PPM_PROJECT_TOKEN directly");
1247
- }
1248
- }
1192
+ console.error("[MCP] Auth mode: Project Token");
1249
1193
  const convexClient = createConvexClient(isDev);
1250
1194
  const convexUrl = isDev ? "https://hallowed-shrimp-344.convex.cloud" : "https://trustworthy-squirrel-735.convex.cloud";
1251
1195
  const config = {
1252
- apiKey: apiKey || void 0,
1253
- // Use undefined if not set (not empty string)
1254
- projectToken: apiKey ? void 0 : projectToken,
1255
- // Only use token if no API key
1196
+ projectToken,
1256
1197
  isDev,
1257
- convexUrl,
1258
- selectedProjects,
1259
- // Project slugs to filter (empty = all projects)
1260
- agentName: apiKey && ppmAgent ? ppmAgent : void 0
1261
- // Only use agent name with API key auth
1198
+ convexUrl
1262
1199
  };
1263
1200
  await startServer(config, convexClient);
1264
1201
  console.error("[MCP] WARNING: startServer promise resolved unexpectedly!");