spm-mcp 0.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.
Files changed (43) hide show
  1. package/README.md +156 -0
  2. package/dist/src/client/cloud-functions.d.ts +5 -0
  3. package/dist/src/client/cloud-functions.js +25 -0
  4. package/dist/src/client/spm-api.d.ts +30 -0
  5. package/dist/src/client/spm-api.js +75 -0
  6. package/dist/src/client/sse-parser.d.ts +23 -0
  7. package/dist/src/client/sse-parser.js +85 -0
  8. package/dist/src/client/supabase.d.ts +46 -0
  9. package/dist/src/client/supabase.js +167 -0
  10. package/dist/src/config.d.ts +16 -0
  11. package/dist/src/config.js +23 -0
  12. package/dist/src/index.d.ts +18 -0
  13. package/dist/src/index.js +185 -0
  14. package/dist/src/stdio.d.ts +19 -0
  15. package/dist/src/stdio.js +24 -0
  16. package/dist/src/tools/analyze.d.ts +32 -0
  17. package/dist/src/tools/analyze.js +96 -0
  18. package/dist/src/tools/clarify.d.ts +65 -0
  19. package/dist/src/tools/clarify.js +101 -0
  20. package/dist/src/tools/create-custom-nano-app.d.ts +15 -0
  21. package/dist/src/tools/create-custom-nano-app.js +9 -0
  22. package/dist/src/tools/evaluate.d.ts +21 -0
  23. package/dist/src/tools/evaluate.js +64 -0
  24. package/dist/src/tools/improve.d.ts +23 -0
  25. package/dist/src/tools/improve.js +55 -0
  26. package/dist/src/tools/list-nano-apps.d.ts +14 -0
  27. package/dist/src/tools/list-nano-apps.js +24 -0
  28. package/package.json +29 -0
  29. package/spm-mcp-0.1.0.tgz +0 -0
  30. package/src/client/cloud-functions.ts +30 -0
  31. package/src/client/spm-api.ts +103 -0
  32. package/src/client/sse-parser.ts +100 -0
  33. package/src/client/supabase.ts +210 -0
  34. package/src/config.ts +27 -0
  35. package/src/index.ts +242 -0
  36. package/src/stdio.ts +25 -0
  37. package/src/tools/analyze.ts +117 -0
  38. package/src/tools/clarify.ts +131 -0
  39. package/src/tools/create-custom-nano-app.ts +24 -0
  40. package/src/tools/evaluate.ts +85 -0
  41. package/src/tools/improve.ts +82 -0
  42. package/src/tools/list-nano-apps.ts +40 -0
  43. package/tsconfig.json +18 -0
@@ -0,0 +1,185 @@
1
+ /**
2
+ * SPM MCP Server — Main entry point.
3
+ *
4
+ * Creates an MCP server with 6 stateless tools:
5
+ * - spm_list_nano_apps: Discover available analysis templates
6
+ * - spm_analyze: Expert analysis of a product document
7
+ * - spm_clarify: Clarification questions for identified gaps
8
+ * - spm_evaluate: Re-score gaps after clarification rounds
9
+ * - spm_improve: Generate improved content for gaps
10
+ * - spm_create_custom_nano_app: Create a custom nano app
11
+ *
12
+ * Used by both stdio (local NPM) and HTTP (Cloud Run) transports.
13
+ */
14
+ import { z } from 'zod';
15
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
16
+ import { handleListNanoApps } from './tools/list-nano-apps.js';
17
+ import { handleAnalyze } from './tools/analyze.js';
18
+ import { handleClarify } from './tools/clarify.js';
19
+ import { handleEvaluate } from './tools/evaluate.js';
20
+ import { handleImprove } from './tools/improve.js';
21
+ import { handleCreateCustomNanoApp } from './tools/create-custom-nano-app.js';
22
+ import { SpmApiError } from './client/spm-api.js';
23
+ export function createSpmMcpServer(options) {
24
+ const server = new McpServer({
25
+ name: 'Super Product Manager',
26
+ version: '0.1.0',
27
+ });
28
+ const apiKey = options?.apiKey;
29
+ // Tool: spm_list_nano_apps
30
+ server.tool('spm_list_nano_apps', 'List all available SPM nano app templates for product document analysis. ' +
31
+ 'Returns template keys, names, descriptions, and categories. ' +
32
+ 'Use this to discover which analysis types are available before calling spm_analyze.', {}, async () => {
33
+ try {
34
+ const result = await handleListNanoApps(apiKey);
35
+ return {
36
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
37
+ };
38
+ }
39
+ catch (err) {
40
+ return errorResponse(err);
41
+ }
42
+ });
43
+ // Tool: spm_analyze
44
+ server.tool('spm_analyze', 'Analyze a product document against SPM expert expectations. ' +
45
+ 'Pass a document and a nano_app_id (e.g., "prd_critique", "user_story", "growth_strategy") ' +
46
+ '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.', {
48
+ document: z.string().describe('The product document text to analyze'),
49
+ 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
+ }, async (input) => {
51
+ try {
52
+ const result = await handleAnalyze(input, apiKey);
53
+ return {
54
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
55
+ };
56
+ }
57
+ catch (err) {
58
+ return errorResponse(err);
59
+ }
60
+ });
61
+ // Tool: spm_clarify
62
+ server.tool('spm_clarify', 'Get clarification questions for gaps identified in an SPM analysis. ' +
63
+ 'Pass the original document, the expert analysis context, and a target gap. ' +
64
+ 'Returns targeted questions with suggested answers to close document gaps.', {
65
+ document: z.string().describe('The original product document text'),
66
+ nano_app_id: z.string().describe('The nano app template key used in the analysis'),
67
+ main_expectation: z.string().describe('The main expectation name/text from the analysis result'),
68
+ all_sub_expectations: z.string().describe('JSON string of all sub-expectations from the analysis. Pass the not_met_expectations array from spm_analyze result.'),
69
+ target_sub_expectation: z.string().describe('The specific sub-expectation (gap) to ask about. Use the title or description of the gap.'),
70
+ previous_answers: z.array(z.object({
71
+ question: z.string(),
72
+ answer: z.string(),
73
+ })).optional().describe('Array of previous Q&A pairs from this clarification session. Empty for first question.'),
74
+ }, async (input) => {
75
+ try {
76
+ const result = await handleClarify(input, apiKey);
77
+ return {
78
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
79
+ };
80
+ }
81
+ catch (err) {
82
+ return errorResponse(err);
83
+ }
84
+ });
85
+ // Tool: spm_evaluate
86
+ server.tool('spm_evaluate', 'Re-score sub-expectations after clarification rounds. ' +
87
+ 'Pass the original document, the expectations from spm_analyze, and all Q&A pairs from spm_clarify. ' +
88
+ 'Returns updated scores showing which gaps are now covered (>=0.81) and which still need work. ' +
89
+ 'Use after every 3 rounds of spm_clarify to measure progress.', {
90
+ document: z.string().describe('The original product document text'),
91
+ nano_app_id: z.string().describe('The nano app template key used in the analysis'),
92
+ main_expectation: z.string().describe('The main expectation name/text from the analysis result'),
93
+ all_sub_expectations: z.string().describe('JSON string of sub-expectations to evaluate. Pass the subEx array from spm_analyze.'),
94
+ prior_clarifications: z.array(z.object({
95
+ question: z.string(),
96
+ answer: z.string(),
97
+ })).describe('All Q&A pairs accumulated from spm_clarify rounds. The evaluator uses these as evidence.'),
98
+ }, async (input) => {
99
+ try {
100
+ const result = await handleEvaluate(input, apiKey);
101
+ return {
102
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
103
+ };
104
+ }
105
+ catch (err) {
106
+ return errorResponse(err);
107
+ }
108
+ });
109
+ // Tool: spm_improve
110
+ server.tool('spm_improve', 'Generate improved document content for a specific gap. ' +
111
+ 'Pass the document, the target sub-expectation, and all clarification Q&A pairs. ' +
112
+ 'Returns paste-ready content the PM can insert into their document. ' +
113
+ 'Use after spm_evaluate confirms the gap is sufficiently covered by clarification answers.', {
114
+ document: z.string().describe('The original product document text'),
115
+ nano_app_id: z.string().describe('The nano app template key used in the analysis'),
116
+ main_expectation: z.string().describe('The main expectation name/text from the analysis result'),
117
+ sub_expectation: z.string().describe('The specific sub-expectation/gap to generate improved content for'),
118
+ all_sub_expectations: z.string().optional().describe('JSON string of all sub-expectations for context. Optional but recommended.'),
119
+ prior_clarifications: z.array(z.object({
120
+ question: z.string(),
121
+ answer: z.string(),
122
+ })).describe('All Q&A pairs from clarification rounds. The improviser uses these to generate grounded content.'),
123
+ current_content: z.string().optional().describe('The current generated content (live_artifact) to improve upon. Empty for first generation.'),
124
+ }, async (input) => {
125
+ try {
126
+ const result = await handleImprove(input, apiKey);
127
+ return {
128
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
129
+ };
130
+ }
131
+ catch (err) {
132
+ return errorResponse(err);
133
+ }
134
+ });
135
+ // Tool: spm_create_custom_nano_app
136
+ server.tool('spm_create_custom_nano_app', 'Create a custom nano app when the user\'s document doesn\'t match any existing nano app ' +
137
+ '(call spm_list_nano_apps first to check).\n\n' +
138
+ 'BEFORE calling this tool, gather preferences from the user:\n' +
139
+ '1. WHAT TO EVALUATE → expectations + rules\n' +
140
+ '2. DOMAIN LENS → domainContext\n' +
141
+ '3. GOOD vs BAD SIGNALS → reviewer examples\n' +
142
+ '4. CLARIFICATION STYLE → clarification examples + instructions\n' +
143
+ '5. IMPROVEMENT FORMAT → improviser instructions\n\n' +
144
+ 'IMPORTANT: Do NOT run a rigid 5-question interview. Extract what you can from the user\'s ' +
145
+ 'initial message. Only ask 1-2 clarifying questions for what\'s genuinely missing. ' +
146
+ 'Bias toward ACTION over interrogation. The endpoint handles missing preferences gracefully.', {
147
+ description: z.string().describe('What kind of document this nano app analyzes and what it should evaluate'),
148
+ name: z.string().optional().describe('Human-readable name for the nano app (auto-generated if omitted)'),
149
+ preferences: z.object({
150
+ domain_lens: z.string().optional().describe('Domain context and expertise lens'),
151
+ good_bad_signals: z.string().optional().describe('Examples of good vs bad signals for review'),
152
+ clarification_style: z.string().optional().describe('Style and examples for clarification questions'),
153
+ improvement_format: z.string().optional().describe('Format instructions for generated improvements'),
154
+ }).optional().describe('Optional preferences to customize the nano app behavior'),
155
+ }, async (input) => {
156
+ try {
157
+ const result = await handleCreateCustomNanoApp(input, apiKey);
158
+ return {
159
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
160
+ };
161
+ }
162
+ catch (err) {
163
+ return errorResponse(err);
164
+ }
165
+ });
166
+ return server;
167
+ }
168
+ function errorResponse(err) {
169
+ if (err instanceof SpmApiError) {
170
+ return {
171
+ content: [{
172
+ type: 'text',
173
+ text: JSON.stringify({ error: err.message, code: err.code }),
174
+ }],
175
+ isError: true,
176
+ };
177
+ }
178
+ const message = err instanceof Error ? err.message : String(err);
179
+ return {
180
+ content: [{ type: 'text', text: JSON.stringify({ error: message }) }],
181
+ isError: true,
182
+ };
183
+ }
184
+ export { SpmApiError } from './client/spm-api.js';
185
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Stdio transport entry point for the SPM MCP Server.
4
+ *
5
+ * Used by Claude Code and other local MCP clients that spawn the server
6
+ * as a subprocess. API key is read from SPM_API_KEY env var.
7
+ *
8
+ * Configure in .claude/settings.json:
9
+ * {
10
+ * "mcpServers": {
11
+ * "spm": {
12
+ * "command": "node",
13
+ * "args": ["/path/to/packages/mcp-server/dist/src/stdio.js"],
14
+ * "env": { "SPM_API_KEY": "spm_k_..." }
15
+ * }
16
+ * }
17
+ * }
18
+ */
19
+ export {};
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Stdio transport entry point for the SPM MCP Server.
4
+ *
5
+ * Used by Claude Code and other local MCP clients that spawn the server
6
+ * as a subprocess. API key is read from SPM_API_KEY env var.
7
+ *
8
+ * Configure in .claude/settings.json:
9
+ * {
10
+ * "mcpServers": {
11
+ * "spm": {
12
+ * "command": "node",
13
+ * "args": ["/path/to/packages/mcp-server/dist/src/stdio.js"],
14
+ * "env": { "SPM_API_KEY": "spm_k_..." }
15
+ * }
16
+ * }
17
+ * }
18
+ */
19
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
20
+ import { createSpmMcpServer } from './index.js';
21
+ const server = createSpmMcpServer();
22
+ const transport = new StdioServerTransport();
23
+ await server.connect(transport);
24
+ //# sourceMappingURL=stdio.js.map
@@ -0,0 +1,32 @@
1
+ /**
2
+ * spm_analyze — Expert analysis tool.
3
+ *
4
+ * Stateless: takes a document + optional nano_app_id, calls the expert agent,
5
+ * returns expectations + scores + gaps. No session state needed.
6
+ *
7
+ * If nano_app_id is omitted, returns the full list of available nano apps
8
+ * for the user/Claude to pick from.
9
+ */
10
+ export declare const analyzeDefinition: {
11
+ name: string;
12
+ description: string;
13
+ inputSchema: {
14
+ type: "object";
15
+ properties: {
16
+ document: {
17
+ type: string;
18
+ description: string;
19
+ };
20
+ nano_app_id: {
21
+ type: string;
22
+ description: string;
23
+ };
24
+ };
25
+ required: string[];
26
+ };
27
+ };
28
+ export interface AnalyzeInput {
29
+ document: string;
30
+ nano_app_id: string;
31
+ }
32
+ export declare function handleAnalyze(input: AnalyzeInput, apiKey?: string): Promise<unknown>;
@@ -0,0 +1,96 @@
1
+ /**
2
+ * spm_analyze — Expert analysis tool.
3
+ *
4
+ * Stateless: takes a document + optional nano_app_id, calls the expert agent,
5
+ * returns expectations + scores + gaps. No session state needed.
6
+ *
7
+ * If nano_app_id is omitted, returns the full list of available nano apps
8
+ * for the user/Claude to pick from.
9
+ */
10
+ import { callExpertAgent, SpmApiError } from '../client/spm-api.js';
11
+ import { buildRulesPayload } from '../client/supabase.js';
12
+ import { callJsonEndpoint } from '../client/cloud-functions.js';
13
+ export const analyzeDefinition = {
14
+ name: 'spm_analyze',
15
+ description: 'Analyze a product document against SPM expert expectations. ' +
16
+ 'Pass a document and a nano_app_id (e.g., "prd_critique", "user_story", "growth_strategy") ' +
17
+ 'to get an expert analysis with expectations, sub-expectations, scores, and evidence. ' +
18
+ 'Call spm_list_nano_apps first if you need to discover available templates. ' +
19
+ 'Returns the full expert analysis as structured JSON.',
20
+ inputSchema: {
21
+ type: 'object',
22
+ properties: {
23
+ document: {
24
+ type: 'string',
25
+ description: 'The product document text to analyze',
26
+ },
27
+ nano_app_id: {
28
+ type: 'string',
29
+ description: 'The nano app template key (e.g., "prd_critique"). ' +
30
+ 'Call spm_list_nano_apps to see available options.',
31
+ },
32
+ },
33
+ required: ['document', 'nano_app_id'],
34
+ },
35
+ };
36
+ export async function handleAnalyze(input, apiKey) {
37
+ const { document, nano_app_id } = input;
38
+ if (!document || document.trim().length === 0) {
39
+ throw new SpmApiError('Document text is required', 'INVALID_INPUT');
40
+ }
41
+ if (!nano_app_id || nano_app_id.trim().length === 0) {
42
+ throw new SpmApiError('nano_app_id is required. Call spm_list_nano_apps to see available options.', 'INVALID_INPUT');
43
+ }
44
+ // Fetch nano app config + rules from Cloud Function (supports custom nano apps with RLS)
45
+ // CF returns flat object with snake_case fields — normalize to NanoApp interface (camelCase)
46
+ const cfResponse = await callJsonEndpoint('getNanoApp', { nano_app_id }, apiKey);
47
+ if (!cfResponse || !cfResponse.key) {
48
+ throw new SpmApiError(`Nano app '${nano_app_id}' not found. Call spm_list_nano_apps to see available options.`, 'INVALID_INPUT');
49
+ }
50
+ const nanoApp = {
51
+ key: cfResponse.key,
52
+ name: cfResponse.name,
53
+ description: cfResponse.description ?? null,
54
+ category: cfResponse.category ?? null,
55
+ flowType: cfResponse.flow_type ?? 'normal',
56
+ expectations: (cfResponse.expectations ?? []).map((e) => ({
57
+ id: e.id ?? '',
58
+ slug: e.slug ?? '',
59
+ name: e.name,
60
+ description: e.description ?? null,
61
+ orderIndex: e.order_index ?? 0,
62
+ rules: (e.rules ?? []).map((r) => ({
63
+ id: r.id ?? '',
64
+ name: r.name,
65
+ description: r.description ?? null,
66
+ spec: typeof r.spec === 'string' ? r.spec : JSON.stringify(r.spec ?? ''),
67
+ isDefault: true,
68
+ orderIndex: r.order_index ?? 0,
69
+ })),
70
+ })),
71
+ };
72
+ // Build the rules payload (same format the Chrome extension sends)
73
+ const rules = buildRulesPayload(nanoApp);
74
+ // Call the expert agent Cloud Function.
75
+ // userId is a label for logging — real identity comes from X-SPM-API-Key
76
+ // resolved by verifyAuthToken on the Cloud Function side.
77
+ const payload = {
78
+ userId: 'mcp-api-key-auth',
79
+ role: 'expert',
80
+ rules,
81
+ requirement_document: document,
82
+ review: '',
83
+ about_company: '',
84
+ user_context: '',
85
+ load_mode: '',
86
+ previously_shown_expectations: '',
87
+ nano_app_id,
88
+ };
89
+ const result = await callExpertAgent(payload, apiKey);
90
+ return {
91
+ nano_app_id,
92
+ nano_app_name: nanoApp.name,
93
+ analysis: result,
94
+ };
95
+ }
96
+ //# sourceMappingURL=analyze.js.map
@@ -0,0 +1,65 @@
1
+ /**
2
+ * spm_clarify — Clarification questions tool.
3
+ *
4
+ * Stateless: takes the document + expectations + gap context + optional previous answers,
5
+ * calls the clarification agent, returns questions with suggested answers.
6
+ *
7
+ * Claude's context window is the session state — each call is self-contained.
8
+ */
9
+ export declare const clarifyDefinition: {
10
+ name: string;
11
+ description: string;
12
+ inputSchema: {
13
+ type: "object";
14
+ properties: {
15
+ document: {
16
+ type: string;
17
+ description: string;
18
+ };
19
+ nano_app_id: {
20
+ type: string;
21
+ description: string;
22
+ };
23
+ main_expectation: {
24
+ type: string;
25
+ description: string;
26
+ };
27
+ all_sub_expectations: {
28
+ type: string;
29
+ description: string;
30
+ };
31
+ target_sub_expectation: {
32
+ type: string;
33
+ description: string;
34
+ };
35
+ previous_answers: {
36
+ type: string;
37
+ items: {
38
+ type: string;
39
+ properties: {
40
+ question: {
41
+ type: string;
42
+ };
43
+ answer: {
44
+ type: string;
45
+ };
46
+ };
47
+ };
48
+ description: string;
49
+ };
50
+ };
51
+ required: string[];
52
+ };
53
+ };
54
+ export interface ClarifyInput {
55
+ document: string;
56
+ nano_app_id: string;
57
+ main_expectation: string;
58
+ all_sub_expectations: string;
59
+ target_sub_expectation: string;
60
+ previous_answers?: Array<{
61
+ question: string;
62
+ answer: string;
63
+ }>;
64
+ }
65
+ export declare function handleClarify(input: ClarifyInput, apiKey?: string): Promise<unknown>;
@@ -0,0 +1,101 @@
1
+ /**
2
+ * spm_clarify — Clarification questions tool.
3
+ *
4
+ * Stateless: takes the document + expectations + gap context + optional previous answers,
5
+ * calls the clarification agent, returns questions with suggested answers.
6
+ *
7
+ * Claude's context window is the session state — each call is self-contained.
8
+ */
9
+ import { callClarificationAgent, SpmApiError } from '../client/spm-api.js';
10
+ export const clarifyDefinition = {
11
+ name: 'spm_clarify',
12
+ description: 'Get clarification questions for gaps identified in an SPM analysis. ' +
13
+ 'Pass the original document, the expert analysis result (from spm_analyze), ' +
14
+ 'and optionally previous Q&A answers. Returns targeted questions with suggested answers ' +
15
+ 'that help close gaps in the document. ' +
16
+ 'After the user answers, call spm_clarify again with the accumulated answers to get more questions, ' +
17
+ 'or call spm_analyze again with the document + answers appended to get updated scores.',
18
+ inputSchema: {
19
+ type: 'object',
20
+ properties: {
21
+ document: {
22
+ type: 'string',
23
+ description: 'The original product document text',
24
+ },
25
+ nano_app_id: {
26
+ type: 'string',
27
+ description: 'The nano app template key used in the analysis',
28
+ },
29
+ main_expectation: {
30
+ type: 'string',
31
+ description: 'The main expectation name/text from the analysis result',
32
+ },
33
+ all_sub_expectations: {
34
+ type: 'string',
35
+ description: 'JSON string of all sub-expectations from the analysis. ' +
36
+ 'Pass the not_met_expectations array from spm_analyze result.',
37
+ },
38
+ target_sub_expectation: {
39
+ type: 'string',
40
+ description: 'The specific sub-expectation (gap) to ask about. ' +
41
+ 'Use the title or description of the gap you want to clarify.',
42
+ },
43
+ previous_answers: {
44
+ type: 'array',
45
+ items: {
46
+ type: 'object',
47
+ properties: {
48
+ question: { type: 'string' },
49
+ answer: { type: 'string' },
50
+ },
51
+ },
52
+ description: 'Array of previous Q&A pairs from this clarification session. ' +
53
+ 'Pass as [{question, answer}, ...]. Empty array for first question.',
54
+ },
55
+ },
56
+ required: ['document', 'nano_app_id', 'main_expectation', 'all_sub_expectations', 'target_sub_expectation'],
57
+ },
58
+ };
59
+ export async function handleClarify(input, apiKey) {
60
+ const { document, nano_app_id, main_expectation, all_sub_expectations, target_sub_expectation, previous_answers = [], } = input;
61
+ if (!document?.trim()) {
62
+ throw new SpmApiError('Document text is required', 'INVALID_INPUT');
63
+ }
64
+ if (!nano_app_id?.trim()) {
65
+ throw new SpmApiError('nano_app_id is required', 'INVALID_INPUT');
66
+ }
67
+ if (!target_sub_expectation?.trim()) {
68
+ throw new SpmApiError('target_sub_expectation is required', 'INVALID_INPUT');
69
+ }
70
+ // Format nudgeHistory as the extension does
71
+ const nudgeHistory = previous_answers.map(qa => ({
72
+ question: qa.question,
73
+ answer: qa.answer,
74
+ }));
75
+ // Format prior_clarifications as condensed strings
76
+ const prior_clarifications = previous_answers.map(qa => `Q: ${qa.question}\nA: ${qa.answer}`);
77
+ // userId is a label for logging — real identity comes from X-SPM-API-Key
78
+ // resolved by verifyAuthToken on the Cloud Function side.
79
+ const payload = {
80
+ userId: 'mcp-api-key-auth',
81
+ role: 'clarification',
82
+ main_expectation,
83
+ requirement_document: document,
84
+ all_sub_expectations,
85
+ target_sub_expectation,
86
+ nudgeHistory,
87
+ prior_clarifications,
88
+ recent_recommendation: '',
89
+ latest_review: '',
90
+ gap_coverage: '',
91
+ nano_app_id,
92
+ };
93
+ const result = await callClarificationAgent(payload, apiKey);
94
+ return {
95
+ nano_app_id,
96
+ target_sub_expectation,
97
+ clarification: result,
98
+ round: previous_answers.length + 1,
99
+ };
100
+ }
101
+ //# sourceMappingURL=clarify.js.map
@@ -0,0 +1,15 @@
1
+ /**
2
+ * spm_create_custom_nano_app — Create a custom nano app.
3
+ * Routes through the createCustomNanoApp Cloud Function.
4
+ */
5
+ export interface CreateCustomNanoAppInput {
6
+ description: string;
7
+ name?: string;
8
+ preferences?: {
9
+ domain_lens?: string;
10
+ good_bad_signals?: string;
11
+ clarification_style?: string;
12
+ improvement_format?: string;
13
+ };
14
+ }
15
+ export declare function handleCreateCustomNanoApp(input: CreateCustomNanoAppInput, apiKey?: string): Promise<unknown>;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * spm_create_custom_nano_app — Create a custom nano app.
3
+ * Routes through the createCustomNanoApp Cloud Function.
4
+ */
5
+ import { callJsonEndpoint } from '../client/cloud-functions.js';
6
+ export async function handleCreateCustomNanoApp(input, apiKey) {
7
+ return callJsonEndpoint('createCustomNanoApp', input, apiKey);
8
+ }
9
+ //# sourceMappingURL=create-custom-nano-app.js.map
@@ -0,0 +1,21 @@
1
+ /**
2
+ * spm_evaluate — Gap evaluator tool.
3
+ *
4
+ * Stateless: takes the document + expectations + clarification answers,
5
+ * calls the gap evaluator agent, returns updated scores per sub-expectation.
6
+ *
7
+ * Use after 3 rounds of spm_clarify to check which gaps are now covered.
8
+ * The evaluator reads both the original document AND the clarification answers
9
+ * to produce updated scores.
10
+ */
11
+ export interface EvaluateInput {
12
+ document: string;
13
+ nano_app_id: string;
14
+ main_expectation: string;
15
+ all_sub_expectations: string;
16
+ prior_clarifications: Array<{
17
+ question: string;
18
+ answer: string;
19
+ }>;
20
+ }
21
+ export declare function handleEvaluate(input: EvaluateInput, apiKey?: string): Promise<unknown>;
@@ -0,0 +1,64 @@
1
+ /**
2
+ * spm_evaluate — Gap evaluator tool.
3
+ *
4
+ * Stateless: takes the document + expectations + clarification answers,
5
+ * calls the gap evaluator agent, returns updated scores per sub-expectation.
6
+ *
7
+ * Use after 3 rounds of spm_clarify to check which gaps are now covered.
8
+ * The evaluator reads both the original document AND the clarification answers
9
+ * to produce updated scores.
10
+ */
11
+ import { callEndpoint, SpmApiError } from '../client/spm-api.js';
12
+ export async function handleEvaluate(input, apiKey) {
13
+ const { document, nano_app_id, main_expectation, all_sub_expectations, prior_clarifications = [], } = input;
14
+ if (!document?.trim()) {
15
+ throw new SpmApiError('Document text is required', 'INVALID_INPUT');
16
+ }
17
+ if (!nano_app_id?.trim()) {
18
+ throw new SpmApiError('nano_app_id is required', 'INVALID_INPUT');
19
+ }
20
+ if (!main_expectation?.trim()) {
21
+ throw new SpmApiError('main_expectation is required', 'INVALID_INPUT');
22
+ }
23
+ if (!all_sub_expectations?.trim()) {
24
+ throw new SpmApiError('all_sub_expectations is required (JSON string)', 'INVALID_INPUT');
25
+ }
26
+ // Parse all_sub_expectations — the evaluator expects an object keyed by sub-exp ID
27
+ let parsedSubExps;
28
+ try {
29
+ const raw = JSON.parse(all_sub_expectations);
30
+ // If it's an array (from spm_analyze output), convert to object keyed by id
31
+ if (Array.isArray(raw)) {
32
+ parsedSubExps = {};
33
+ for (const sub of raw) {
34
+ const id = sub.id || `sub_${Object.keys(parsedSubExps).length + 1}`;
35
+ parsedSubExps[id] = sub;
36
+ }
37
+ }
38
+ else {
39
+ parsedSubExps = raw;
40
+ }
41
+ }
42
+ catch {
43
+ throw new SpmApiError('all_sub_expectations must be valid JSON', 'INVALID_INPUT');
44
+ }
45
+ // Format prior_clarifications as condensed strings (same as extension)
46
+ const formattedClarifications = prior_clarifications.map(qa => `Q: ${qa.question}\nA: ${qa.answer}`);
47
+ const payload = {
48
+ userId: 'mcp-api-key-auth',
49
+ role: 'gapEvaluator',
50
+ main_expectation,
51
+ all_sub_expectations: parsedSubExps,
52
+ requirement_document: document,
53
+ prior_clarifications: formattedClarifications,
54
+ nano_app_id,
55
+ };
56
+ const result = await callEndpoint('extensionGapEvaluatorV3', payload, apiKey);
57
+ return {
58
+ nano_app_id,
59
+ main_expectation,
60
+ evaluation: result,
61
+ clarifications_evaluated: prior_clarifications.length,
62
+ };
63
+ }
64
+ //# sourceMappingURL=evaluate.js.map
@@ -0,0 +1,23 @@
1
+ /**
2
+ * spm_improve — Improviser tool.
3
+ *
4
+ * Stateless: takes the document + expectations + clarification answers,
5
+ * calls the improviser agent, returns improved document content.
6
+ *
7
+ * Use after spm_evaluate confirms gaps are sufficiently covered.
8
+ * The improviser generates paste-ready content for each gap, grounded in
9
+ * the original document and the PM's clarification answers.
10
+ */
11
+ export interface ImproveInput {
12
+ document: string;
13
+ nano_app_id: string;
14
+ main_expectation: string;
15
+ sub_expectation: string;
16
+ all_sub_expectations?: string;
17
+ prior_clarifications: Array<{
18
+ question: string;
19
+ answer: string;
20
+ }>;
21
+ current_content?: string;
22
+ }
23
+ export declare function handleImprove(input: ImproveInput, apiKey?: string): Promise<unknown>;