spm-mcp 0.3.0 → 0.3.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/src/index.js CHANGED
@@ -20,10 +20,25 @@ import { handleEvaluate } from './tools/evaluate.js';
20
20
  import { handleImprove } from './tools/improve.js';
21
21
  import { handleCreateCustomNanoApp } from './tools/create-custom-nano-app.js';
22
22
  import { SpmApiError } from './client/spm-api.js';
23
+ function logToolCall(tool, input) {
24
+ const summary = {};
25
+ for (const [k, v] of Object.entries(input)) {
26
+ if (typeof v === 'string' && v.length > 100) {
27
+ summary[k] = `[${v.length} chars]`;
28
+ }
29
+ else if (Array.isArray(v)) {
30
+ summary[k] = `[${v.length} items]`;
31
+ }
32
+ else {
33
+ summary[k] = v ?? '(empty)';
34
+ }
35
+ }
36
+ console.log(`[MCP] ${tool}:`, JSON.stringify(summary));
37
+ }
23
38
  export function createSpmMcpServer(options) {
24
39
  const server = new McpServer({
25
40
  name: 'Super Product Manager',
26
- version: '0.1.0',
41
+ version: '0.3.1',
27
42
  });
28
43
  const apiKey = options?.apiKey;
29
44
  // Tool: spm_list_nano_apps
@@ -31,6 +46,7 @@ export function createSpmMcpServer(options) {
31
46
  'Returns template keys, names, descriptions, and categories. ' +
32
47
  'Use this to discover which analysis types are available before calling spm_analyze.', {}, async () => {
33
48
  try {
49
+ logToolCall('spm_list_nano_apps', {});
34
50
  const result = await handleListNanoApps(apiKey);
35
51
  return {
36
52
  content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
@@ -44,11 +60,14 @@ export function createSpmMcpServer(options) {
44
60
  server.tool('spm_analyze', 'Analyze a product document against SPM expert expectations. ' +
45
61
  'Pass a document and a nano_app_id (e.g., "prd_critique", "user_story", "growth_strategy") ' +
46
62
  'to get an expert analysis with expectations, sub-expectations, scores, and evidence. ' +
47
- 'Call spm_list_nano_apps first if you need to discover available templates.', {
63
+ 'Call spm_list_nano_apps first if you need to discover available templates. ' +
64
+ 'IMPORTANT: Present results as a clear scorecard table (Expectation | Score | Verdict). ' +
65
+ 'Highlight critical gaps (score < 50%) and suggest running spm_clarify on the weakest gap next.', {
48
66
  document: z.string().describe('The product document text to analyze'),
49
67
  nano_app_id: z.string().describe('The nano app template key (e.g., "prd_critique"). Call spm_list_nano_apps to see available options.'),
50
68
  }, async (input) => {
51
69
  try {
70
+ logToolCall('spm_analyze', input);
52
71
  const result = await handleAnalyze(input, apiKey);
53
72
  return {
54
73
  content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
@@ -61,7 +80,12 @@ export function createSpmMcpServer(options) {
61
80
  // Tool: spm_clarify
62
81
  server.tool('spm_clarify', 'Get clarification questions for gaps identified in an SPM analysis. ' +
63
82
  'Pass the original document, the expert analysis context, and a target gap. ' +
64
- 'Returns targeted questions with suggested answers to close document gaps.', {
83
+ 'Returns targeted questions with suggested answers to close document gaps. ' +
84
+ 'IMPORTANT: When presenting results to the user, minimize cognitive load. ' +
85
+ 'Show the question prominently, then present each answer option as a clear ' +
86
+ 'numbered choice the user can pick (1, 2, 3...). Mark the [recommended] option. ' +
87
+ 'If you have native interactive UI (buttons, selectable options, AskUserQuestion), use it. ' +
88
+ 'The user should be able to pick an option or type their own answer with minimal effort.', {
65
89
  document: z.string().describe('The original product document text'),
66
90
  nano_app_id: z.string().describe('The nano app template key used in the analysis'),
67
91
  main_expectation: z.string().describe('The main expectation name/text from the analysis result'),
@@ -75,6 +99,7 @@ export function createSpmMcpServer(options) {
75
99
  about_company: z.string().optional().describe('Context about the company/product: stage, team size, market, existing users, business model.'),
76
100
  }, async (input) => {
77
101
  try {
102
+ logToolCall('spm_clarify', input);
78
103
  const result = await handleClarify(input, apiKey);
79
104
  return {
80
105
  content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
@@ -101,6 +126,7 @@ export function createSpmMcpServer(options) {
101
126
  about_company: z.string().optional().describe('Context about the company/product: stage, team size, market, existing users, business model.'),
102
127
  }, async (input) => {
103
128
  try {
129
+ logToolCall('spm_evaluate', input);
104
130
  const result = await handleEvaluate(input, apiKey);
105
131
  return {
106
132
  content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
@@ -114,7 +140,9 @@ export function createSpmMcpServer(options) {
114
140
  server.tool('spm_improve', 'Generate improved document content for a specific gap. ' +
115
141
  'Pass the document, the target sub-expectation, and all clarification Q&A pairs. ' +
116
142
  'Returns paste-ready content the PM can insert into their document. ' +
117
- 'Use after spm_evaluate confirms the gap is sufficiently covered by clarification answers.', {
143
+ 'Use after spm_evaluate confirms the gap is sufficiently covered by clarification answers. ' +
144
+ 'IMPORTANT: Present the generated content in clean markdown the user can copy-paste directly. ' +
145
+ 'If there are [ACTION NEEDED] markers, highlight them so the user knows what to fill in.', {
118
146
  document: z.string().describe('The original product document text'),
119
147
  nano_app_id: z.string().describe('The nano app template key used in the analysis'),
120
148
  main_expectation: z.string().describe('The main expectation name/text from the analysis result'),
@@ -127,6 +155,7 @@ export function createSpmMcpServer(options) {
127
155
  current_content: z.string().optional().describe('The current generated content (live_artifact) to improve upon. Empty for first generation.'),
128
156
  }, async (input) => {
129
157
  try {
158
+ logToolCall('spm_improve', input);
130
159
  const result = await handleImprove(input, apiKey);
131
160
  return {
132
161
  content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
@@ -158,6 +187,7 @@ export function createSpmMcpServer(options) {
158
187
  }).optional().describe('Optional preferences to customize the nano app behavior'),
159
188
  }, async (input) => {
160
189
  try {
190
+ logToolCall('spm_create_custom_nano_app', input);
161
191
  const result = await handleCreateCustomNanoApp(input, apiKey);
162
192
  return {
163
193
  content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
package/dist/src/stdio.js CHANGED
@@ -94,13 +94,12 @@ if (wantsSetup || (isInteractive() && !hasApiKey())) {
94
94
  const ok = await runSetup();
95
95
  process.exit(ok ? 0 : 1);
96
96
  }
97
- else if (!hasApiKey() && isInteractive()) {
98
- // No key and interactive — guide the user
99
- process.stderr.write('\n No SPM_API_KEY found. Run: npx spm-mcp --setup\n\n');
100
- process.exit(1);
101
- }
102
97
  else {
103
98
  // MCP server mode (non-interactive, started by AI tool)
99
+ if (!hasApiKey()) {
100
+ process.stderr.write('[spm-mcp] Warning: No API key found. Tool calls will fail with 401.\n' +
101
+ ' Fix: run "npx spm-mcp --setup" or set SPM_API_KEY env var.\n');
102
+ }
104
103
  const server = createSpmMcpServer({ apiKey: config.apiKey });
105
104
  const transport = new StdioServerTransport();
106
105
  await server.connect(transport);
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "spm-mcp",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "Super Product Manager MCP Server - AI-powered product document analysis for PRDs, roadmaps, and 30 PM document types",
5
5
  "author": "Super Product Manager <chiranjeevi.gunturi@superproductmanager.ai>",
6
6
  "homepage": "https://superproductmanager.ai",
7
7
  "type": "module",
8
- "main": "dist/index.js",
8
+ "main": "dist/src/index.js",
9
9
  "bin": {
10
10
  "spm-mcp": "dist/src/stdio.js"
11
11
  },
package/src/index.ts CHANGED
@@ -22,10 +22,24 @@ import { handleImprove } from './tools/improve.js';
22
22
  import { handleCreateCustomNanoApp } from './tools/create-custom-nano-app.js';
23
23
  import { SpmApiError } from './client/spm-api.js';
24
24
 
25
+ function logToolCall(tool: string, input: Record<string, unknown>) {
26
+ const summary: Record<string, unknown> = {};
27
+ for (const [k, v] of Object.entries(input)) {
28
+ if (typeof v === 'string' && v.length > 100) {
29
+ summary[k] = `[${v.length} chars]`;
30
+ } else if (Array.isArray(v)) {
31
+ summary[k] = `[${v.length} items]`;
32
+ } else {
33
+ summary[k] = v ?? '(empty)';
34
+ }
35
+ }
36
+ console.log(`[MCP] ${tool}:`, JSON.stringify(summary));
37
+ }
38
+
25
39
  export function createSpmMcpServer(options?: { apiKey?: string }): McpServer {
26
40
  const server = new McpServer({
27
41
  name: 'Super Product Manager',
28
- version: '0.1.0',
42
+ version: '0.3.1',
29
43
  });
30
44
 
31
45
  const apiKey = options?.apiKey;
@@ -39,6 +53,7 @@ export function createSpmMcpServer(options?: { apiKey?: string }): McpServer {
39
53
  {},
40
54
  async () => {
41
55
  try {
56
+ logToolCall('spm_list_nano_apps', {});
42
57
  const result = await handleListNanoApps(apiKey);
43
58
  return {
44
59
  content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }],
@@ -55,7 +70,9 @@ export function createSpmMcpServer(options?: { apiKey?: string }): McpServer {
55
70
  'Analyze a product document against SPM expert expectations. ' +
56
71
  'Pass a document and a nano_app_id (e.g., "prd_critique", "user_story", "growth_strategy") ' +
57
72
  'to get an expert analysis with expectations, sub-expectations, scores, and evidence. ' +
58
- 'Call spm_list_nano_apps first if you need to discover available templates.',
73
+ 'Call spm_list_nano_apps first if you need to discover available templates. ' +
74
+ 'IMPORTANT: Present results as a clear scorecard table (Expectation | Score | Verdict). ' +
75
+ 'Highlight critical gaps (score < 50%) and suggest running spm_clarify on the weakest gap next.',
59
76
  {
60
77
  document: z.string().describe('The product document text to analyze'),
61
78
  nano_app_id: z.string().describe(
@@ -64,6 +81,7 @@ export function createSpmMcpServer(options?: { apiKey?: string }): McpServer {
64
81
  },
65
82
  async (input) => {
66
83
  try {
84
+ logToolCall('spm_analyze', input);
67
85
  const result = await handleAnalyze(input, apiKey);
68
86
  return {
69
87
  content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }],
@@ -79,7 +97,12 @@ export function createSpmMcpServer(options?: { apiKey?: string }): McpServer {
79
97
  'spm_clarify',
80
98
  'Get clarification questions for gaps identified in an SPM analysis. ' +
81
99
  'Pass the original document, the expert analysis context, and a target gap. ' +
82
- 'Returns targeted questions with suggested answers to close document gaps.',
100
+ 'Returns targeted questions with suggested answers to close document gaps. ' +
101
+ 'IMPORTANT: When presenting results to the user, minimize cognitive load. ' +
102
+ 'Show the question prominently, then present each answer option as a clear ' +
103
+ 'numbered choice the user can pick (1, 2, 3...). Mark the [recommended] option. ' +
104
+ 'If you have native interactive UI (buttons, selectable options, AskUserQuestion), use it. ' +
105
+ 'The user should be able to pick an option or type their own answer with minimal effort.',
83
106
  {
84
107
  document: z.string().describe('The original product document text'),
85
108
  nano_app_id: z.string().describe('The nano app template key used in the analysis'),
@@ -105,6 +128,7 @@ export function createSpmMcpServer(options?: { apiKey?: string }): McpServer {
105
128
  },
106
129
  async (input) => {
107
130
  try {
131
+ logToolCall('spm_clarify', input);
108
132
  const result = await handleClarify(input, apiKey);
109
133
  return {
110
134
  content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }],
@@ -144,6 +168,7 @@ export function createSpmMcpServer(options?: { apiKey?: string }): McpServer {
144
168
  },
145
169
  async (input) => {
146
170
  try {
171
+ logToolCall('spm_evaluate', input);
147
172
  const result = await handleEvaluate(input, apiKey);
148
173
  return {
149
174
  content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }],
@@ -160,7 +185,9 @@ export function createSpmMcpServer(options?: { apiKey?: string }): McpServer {
160
185
  'Generate improved document content for a specific gap. ' +
161
186
  'Pass the document, the target sub-expectation, and all clarification Q&A pairs. ' +
162
187
  'Returns paste-ready content the PM can insert into their document. ' +
163
- 'Use after spm_evaluate confirms the gap is sufficiently covered by clarification answers.',
188
+ 'Use after spm_evaluate confirms the gap is sufficiently covered by clarification answers. ' +
189
+ 'IMPORTANT: Present the generated content in clean markdown the user can copy-paste directly. ' +
190
+ 'If there are [ACTION NEEDED] markers, highlight them so the user knows what to fill in.',
164
191
  {
165
192
  document: z.string().describe('The original product document text'),
166
193
  nano_app_id: z.string().describe('The nano app template key used in the analysis'),
@@ -181,6 +208,7 @@ export function createSpmMcpServer(options?: { apiKey?: string }): McpServer {
181
208
  },
182
209
  async (input) => {
183
210
  try {
211
+ logToolCall('spm_improve', input);
184
212
  const result = await handleImprove(input, apiKey);
185
213
  return {
186
214
  content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }],
@@ -221,6 +249,7 @@ export function createSpmMcpServer(options?: { apiKey?: string }): McpServer {
221
249
  },
222
250
  async (input) => {
223
251
  try {
252
+ logToolCall('spm_create_custom_nano_app', input);
224
253
  const result = await handleCreateCustomNanoApp(input, apiKey);
225
254
  return {
226
255
  content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }],
package/src/stdio.ts CHANGED
@@ -107,12 +107,14 @@ if (wantsSetup || (isInteractive() && !hasApiKey())) {
107
107
  // Interactive setup mode
108
108
  const ok = await runSetup();
109
109
  process.exit(ok ? 0 : 1);
110
- } else if (!hasApiKey() && isInteractive()) {
111
- // No key and interactive — guide the user
112
- process.stderr.write('\n No SPM_API_KEY found. Run: npx spm-mcp --setup\n\n');
113
- process.exit(1);
114
110
  } else {
115
111
  // MCP server mode (non-interactive, started by AI tool)
112
+ if (!hasApiKey()) {
113
+ process.stderr.write(
114
+ '[spm-mcp] Warning: No API key found. Tool calls will fail with 401.\n' +
115
+ ' Fix: run "npx spm-mcp --setup" or set SPM_API_KEY env var.\n'
116
+ );
117
+ }
116
118
  const server = createSpmMcpServer({ apiKey: config.apiKey });
117
119
  const transport = new StdioServerTransport();
118
120
  await server.connect(transport);
package/spm-mcp-0.1.0.tgz DELETED
Binary file