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 +34 -4
- package/dist/src/stdio.js +4 -5
- package/package.json +2 -2
- package/src/index.ts +33 -4
- package/src/stdio.ts +6 -4
- package/spm-mcp-0.1.0.tgz +0 -0
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
|
|
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.
|
|
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
|
|
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
|