@schilling.mark.a/software-methodology 1.0.0 → 1.0.1

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 (69) hide show
  1. package/.github/copilot-instructions.md +159 -0
  2. package/README.md +172 -6
  3. package/docs/story-map/backbone.md +141 -0
  4. package/docs/story-map/releases/r1-walking-skeleton.md +152 -0
  5. package/docs/story-map/user-tasks/ACT-001-task-001.md +45 -0
  6. package/docs/story-map/user-tasks/ACT-001-task-002.md +48 -0
  7. package/docs/story-map/user-tasks/ACT-002-task-001.md +47 -0
  8. package/docs/story-map/user-tasks/ACT-002-task-002.md +47 -0
  9. package/docs/story-map/user-tasks/ACT-002-task-003.md +46 -0
  10. package/docs/story-map/user-tasks/ACT-003-task-001.md +47 -0
  11. package/docs/story-map/user-tasks/ACT-003-task-002.md +46 -0
  12. package/docs/story-map/user-tasks/ACT-003-task-003.md +49 -0
  13. package/docs/story-map/user-tasks/ACT-003-task-004.md +47 -0
  14. package/docs/story-map/user-tasks/ACT-004-task-001.md +48 -0
  15. package/docs/story-map/user-tasks/ACT-004-task-002.md +49 -0
  16. package/docs/story-map/user-tasks/ACT-004-task-003.md +47 -0
  17. package/docs/story-map/user-tasks/ACT-005-task-001.md +47 -0
  18. package/docs/story-map/user-tasks/ACT-005-task-002.md +48 -0
  19. package/docs/story-map/user-tasks/ACT-005-task-003.md +48 -0
  20. package/docs/story-map/user-tasks/ACT-005-task-004.md +48 -0
  21. package/docs/story-map/user-tasks/ACT-006-task-001.md +47 -0
  22. package/docs/story-map/user-tasks/ACT-006-task-002.md +46 -0
  23. package/docs/story-map/user-tasks/ACT-006-task-003.md +47 -0
  24. package/docs/story-map/user-tasks/ACT-006-task-004.md +46 -0
  25. package/docs/story-map/user-tasks/ACT-007-task-001.md +48 -0
  26. package/docs/story-map/user-tasks/ACT-007-task-002.md +47 -0
  27. package/docs/story-map/user-tasks/ACT-007-task-003.md +47 -0
  28. package/docs/story-map/user-tasks/ACT-007-task-004.md +48 -0
  29. package/docs/story-map/user-tasks/ACT-008-task-001.md +48 -0
  30. package/docs/story-map/user-tasks/ACT-008-task-002.md +48 -0
  31. package/docs/story-map/user-tasks/ACT-008-task-003.md +47 -0
  32. package/docs/story-map/user-tasks/ACT-008-task-004.md +48 -0
  33. package/docs/story-map/walking-skeleton.md +95 -0
  34. package/docs/value-proposition-canvas.md +171 -0
  35. package/features/mcp-server/query-vpc.feature +48 -0
  36. package/features/mcp-server/read-reference.feature +41 -0
  37. package/features/mcp-server/read-skill.feature +33 -0
  38. package/features/mcp-server/search-guidance.feature +42 -0
  39. package/features/mcp-server/suggest-next-step.feature +61 -0
  40. package/features/mcp-server/validate-gherkin.feature +54 -0
  41. package/mcp-server/QUICKSTART.md +172 -0
  42. package/mcp-server/README.md +171 -0
  43. package/mcp-server/dist/index.d.ts +12 -0
  44. package/mcp-server/dist/index.js +296 -0
  45. package/mcp-server/dist/repository.d.ts +59 -0
  46. package/mcp-server/dist/repository.js +211 -0
  47. package/mcp-server/dist/tools/gherkin-validator.d.ts +16 -0
  48. package/mcp-server/dist/tools/gherkin-validator.js +152 -0
  49. package/mcp-server/dist/tools/guidance-searcher.d.ts +11 -0
  50. package/mcp-server/dist/tools/guidance-searcher.js +34 -0
  51. package/mcp-server/dist/tools/next-step-suggester.d.ts +16 -0
  52. package/mcp-server/dist/tools/next-step-suggester.js +210 -0
  53. package/mcp-server/dist/tools/reference-reader.d.ts +17 -0
  54. package/mcp-server/dist/tools/reference-reader.js +57 -0
  55. package/mcp-server/dist/tools/skill-reader.d.ts +17 -0
  56. package/mcp-server/dist/tools/skill-reader.js +38 -0
  57. package/mcp-server/dist/tools/vpc-querier.d.ts +37 -0
  58. package/mcp-server/dist/tools/vpc-querier.js +158 -0
  59. package/mcp-server/package.json +42 -0
  60. package/mcp-server/src/index.ts +331 -0
  61. package/mcp-server/src/repository.ts +254 -0
  62. package/mcp-server/src/tools/gherkin-validator.ts +206 -0
  63. package/mcp-server/src/tools/guidance-searcher.ts +42 -0
  64. package/mcp-server/src/tools/next-step-suggester.ts +243 -0
  65. package/mcp-server/src/tools/reference-reader.ts +71 -0
  66. package/mcp-server/src/tools/skill-reader.ts +47 -0
  67. package/mcp-server/src/tools/vpc-querier.ts +201 -0
  68. package/mcp-server/tsconfig.json +17 -0
  69. package/package.json +8 -2
@@ -0,0 +1,57 @@
1
+ export class ReferenceReader {
2
+ repository;
3
+ constructor(repository) {
4
+ this.repository = repository;
5
+ }
6
+ async readReference(referencePath) {
7
+ // Parse path like "clean-code/solid.md"
8
+ const parts = referencePath.split("/");
9
+ if (parts.length !== 2) {
10
+ throw new Error("Reference path must be in format 'skill-name/reference-name.md'");
11
+ }
12
+ const [skillName, referenceName] = parts;
13
+ try {
14
+ const content = await this.repository.readReference(skillName, referenceName);
15
+ return {
16
+ content: [
17
+ {
18
+ type: "text",
19
+ text: content,
20
+ },
21
+ ],
22
+ };
23
+ }
24
+ catch (error) {
25
+ const references = await this.repository.listReferences(skillName);
26
+ if (references.length === 0) {
27
+ throw new Error(`Skill '${skillName}' has no reference documents or doesn't exist`);
28
+ }
29
+ const availableRefs = references.map((r) => r.name).join(", ");
30
+ throw new Error(`Reference '${referenceName}' not found in skill '${skillName}'. Available references: ${availableRefs}`);
31
+ }
32
+ }
33
+ async listReferences(skillName) {
34
+ const references = await this.repository.listReferences(skillName);
35
+ if (references.length === 0) {
36
+ return {
37
+ content: [
38
+ {
39
+ type: "text",
40
+ text: `Skill '${skillName}' has no reference documents.`,
41
+ },
42
+ ],
43
+ };
44
+ }
45
+ const text = references
46
+ .map((ref) => `- ${ref.name}`)
47
+ .join("\n");
48
+ return {
49
+ content: [
50
+ {
51
+ type: "text",
52
+ text: `# References for ${skillName} (${references.length})\n\n${text}`,
53
+ },
54
+ ],
55
+ };
56
+ }
57
+ }
@@ -0,0 +1,17 @@
1
+ import { MethodologyRepository } from "../repository.js";
2
+ export declare class SkillReader {
3
+ private repository;
4
+ constructor(repository: MethodologyRepository);
5
+ readSkill(skillName: string): Promise<{
6
+ content: {
7
+ type: "text";
8
+ text: string;
9
+ }[];
10
+ }>;
11
+ listSkills(): Promise<{
12
+ content: {
13
+ type: "text";
14
+ text: string;
15
+ }[];
16
+ }>;
17
+ }
@@ -0,0 +1,38 @@
1
+ export class SkillReader {
2
+ repository;
3
+ constructor(repository) {
4
+ this.repository = repository;
5
+ }
6
+ async readSkill(skillName) {
7
+ try {
8
+ const content = await this.repository.readSkill(skillName);
9
+ return {
10
+ content: [
11
+ {
12
+ type: "text",
13
+ text: content,
14
+ },
15
+ ],
16
+ };
17
+ }
18
+ catch (error) {
19
+ const skills = await this.repository.listSkills();
20
+ const availableSkills = skills.map((s) => s.name).join(", ");
21
+ throw new Error(`Skill '${skillName}' not found. Available skills: ${availableSkills}`);
22
+ }
23
+ }
24
+ async listSkills() {
25
+ const skills = await this.repository.listSkills();
26
+ const text = skills
27
+ .map((skill) => `**${skill.displayName}** (${skill.name}):\n ${skill.description}`)
28
+ .join("\n\n");
29
+ return {
30
+ content: [
31
+ {
32
+ type: "text",
33
+ text: `# Available Skills (${skills.length})\n\n${text}`,
34
+ },
35
+ ],
36
+ };
37
+ }
38
+ }
@@ -0,0 +1,37 @@
1
+ import { MethodologyRepository } from "../repository.js";
2
+ export declare class VPCQuerier {
3
+ private repository;
4
+ constructor(repository: MethodologyRepository);
5
+ getSegments(): Promise<{
6
+ content: {
7
+ type: "text";
8
+ text: string;
9
+ }[];
10
+ }>;
11
+ getCustomerJobs(segmentName: string): Promise<{
12
+ content: {
13
+ type: "text";
14
+ text: string;
15
+ }[];
16
+ }>;
17
+ getPains(segmentName: string): Promise<{
18
+ content: {
19
+ type: "text";
20
+ text: string;
21
+ }[];
22
+ }>;
23
+ getGains(segmentName: string): Promise<{
24
+ content: {
25
+ type: "text";
26
+ text: string;
27
+ }[];
28
+ }>;
29
+ search(query: string): Promise<{
30
+ content: {
31
+ type: "text";
32
+ text: string;
33
+ }[];
34
+ }>;
35
+ private parseSegments;
36
+ private extractList;
37
+ }
@@ -0,0 +1,158 @@
1
+ export class VPCQuerier {
2
+ repository;
3
+ constructor(repository) {
4
+ this.repository = repository;
5
+ }
6
+ async getSegments() {
7
+ const vpc = await this.repository.readVPC();
8
+ const segments = this.parseSegments(vpc);
9
+ const text = segments.map((s) => `- ${s.name}`).join("\n");
10
+ return {
11
+ content: [
12
+ {
13
+ type: "text",
14
+ text: `# Customer Segments (${segments.length})\n\n${text}`,
15
+ },
16
+ ],
17
+ };
18
+ }
19
+ async getCustomerJobs(segmentName) {
20
+ const vpc = await this.repository.readVPC();
21
+ const segments = this.parseSegments(vpc);
22
+ const segment = segments.find((s) => s.name === segmentName);
23
+ if (!segment) {
24
+ const available = segments.map((s) => s.name).join(", ");
25
+ throw new Error(`Segment '${segmentName}' not found. Available segments: ${available}`);
26
+ }
27
+ const text = segment.customerJobs
28
+ .map((job, i) => `${i + 1}. ${job}`)
29
+ .join("\n");
30
+ return {
31
+ content: [
32
+ {
33
+ type: "text",
34
+ text: `# Customer Jobs for ${segmentName}\n\n${text}`,
35
+ },
36
+ ],
37
+ };
38
+ }
39
+ async getPains(segmentName) {
40
+ const vpc = await this.repository.readVPC();
41
+ const segments = this.parseSegments(vpc);
42
+ const segment = segments.find((s) => s.name === segmentName);
43
+ if (!segment) {
44
+ const available = segments.map((s) => s.name).join(", ");
45
+ throw new Error(`Segment '${segmentName}' not found. Available segments: ${available}`);
46
+ }
47
+ const text = segment.pains
48
+ .map((pain, i) => `${i + 1}. ${pain}`)
49
+ .join("\n");
50
+ return {
51
+ content: [
52
+ {
53
+ type: "text",
54
+ text: `# Pains for ${segmentName}\n\n${text}`,
55
+ },
56
+ ],
57
+ };
58
+ }
59
+ async getGains(segmentName) {
60
+ const vpc = await this.repository.readVPC();
61
+ const segments = this.parseSegments(vpc);
62
+ const segment = segments.find((s) => s.name === segmentName);
63
+ if (!segment) {
64
+ const available = segments.map((s) => s.name).join(", ");
65
+ throw new Error(`Segment '${segmentName}' not found. Available segments: ${available}`);
66
+ }
67
+ const text = segment.gains
68
+ .map((gain, i) => `${i + 1}. ${gain}`)
69
+ .join("\n");
70
+ return {
71
+ content: [
72
+ {
73
+ type: "text",
74
+ text: `# Gains for ${segmentName}\n\n${text}`,
75
+ },
76
+ ],
77
+ };
78
+ }
79
+ async search(query) {
80
+ const vpc = await this.repository.readVPC();
81
+ const segments = this.parseSegments(vpc);
82
+ const results = [];
83
+ const queryLower = query.toLowerCase();
84
+ for (const segment of segments) {
85
+ for (const pain of segment.pains) {
86
+ if (pain.toLowerCase().includes(queryLower)) {
87
+ results.push({ segment: segment.name, type: "pain", text: pain });
88
+ }
89
+ }
90
+ for (const gain of segment.gains) {
91
+ if (gain.toLowerCase().includes(queryLower)) {
92
+ results.push({ segment: segment.name, type: "gain", text: gain });
93
+ }
94
+ }
95
+ }
96
+ if (results.length === 0) {
97
+ return {
98
+ content: [
99
+ {
100
+ type: "text",
101
+ text: `No results found for '${query}' in Value Proposition Canvas.`,
102
+ },
103
+ ],
104
+ };
105
+ }
106
+ const text = results
107
+ .map((r) => `**[${r.segment}] ${r.type.toUpperCase()}:** ${r.text}`)
108
+ .join("\n\n");
109
+ return {
110
+ content: [
111
+ {
112
+ type: "text",
113
+ text: `# VPC Search Results for '${query}'\n\n${text}`,
114
+ },
115
+ ],
116
+ };
117
+ }
118
+ parseSegments(vpcContent) {
119
+ const segments = [];
120
+ const segmentRegex = /## Segment: (.+?)\n/g;
121
+ let match;
122
+ const segmentStarts = [];
123
+ while ((match = segmentRegex.exec(vpcContent)) !== null) {
124
+ segmentStarts.push({ name: match[1], index: match.index });
125
+ }
126
+ for (let i = 0; i < segmentStarts.length; i++) {
127
+ const start = segmentStarts[i];
128
+ const end = i + 1 < segmentStarts.length ? segmentStarts[i + 1].index : vpcContent.length;
129
+ const segmentContent = vpcContent.slice(start.index, end);
130
+ segments.push({
131
+ name: start.name,
132
+ customerJobs: this.extractList(segmentContent, "#### Customer Jobs"),
133
+ pains: this.extractList(segmentContent, "#### Pains"),
134
+ gains: this.extractList(segmentContent, "#### Gains"),
135
+ });
136
+ }
137
+ return segments;
138
+ }
139
+ extractList(content, heading) {
140
+ const headingIndex = content.indexOf(heading);
141
+ if (headingIndex === -1)
142
+ return [];
143
+ const afterHeading = content.slice(headingIndex + heading.length);
144
+ const nextHeadingIndex = afterHeading.search(/####|###|##/);
145
+ const section = nextHeadingIndex === -1
146
+ ? afterHeading
147
+ : afterHeading.slice(0, nextHeadingIndex);
148
+ const lines = section.split("\n");
149
+ const items = [];
150
+ for (const line of lines) {
151
+ const match = line.match(/^\d+\.\s+\*\*(.+?)\*\*\s*—\s*(.+)/);
152
+ if (match) {
153
+ items.push(`${match[1]} — ${match[2]}`);
154
+ }
155
+ }
156
+ return items;
157
+ }
158
+ }
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@schilling.mark.a/software-methodology-mcp",
3
+ "version": "0.1.1",
4
+ "type": "module",
5
+ "description": "MCP server providing structured access to software methodology skills, references, and guidance",
6
+ "bin": {
7
+ "software-methodology-mcp": "./dist/index.js"
8
+ },
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "watch": "tsc --watch",
12
+ "prepare": "npm run build",
13
+ "test": "jest",
14
+ "test:watch": "jest --watch"
15
+ },
16
+ "keywords": [
17
+ "mcp",
18
+ "mcp-server",
19
+ "methodology",
20
+ "software-development",
21
+ "bdd",
22
+ "atdd",
23
+ "clean-code"
24
+ ],
25
+ "author": "Mark Schilling",
26
+ "license": "MIT",
27
+ "dependencies": {
28
+ "@modelcontextprotocol/sdk": "^0.5.0"
29
+ },
30
+ "devDependencies": {
31
+ "@types/node": "^20.11.0",
32
+ "typescript": "^5.3.3",
33
+ "jest": "^29.7.0",
34
+ "@types/jest": "^29.5.11"
35
+ },
36
+ "files": [
37
+ "dist/",
38
+ "../docs/**/*.md",
39
+ "../**/SKILL.md",
40
+ "../**/references/**/*.md"
41
+ ]
42
+ }
@@ -0,0 +1,331 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Software Methodology MCP Server
5
+ *
6
+ * Provides structured access to software methodology documentation:
7
+ * - 11 skills (product-strategy through continuous-improvement)
8
+ * - Reference documents for each skill
9
+ * - Value Proposition Canvas queries
10
+ * - Guidance search across all content
11
+ * - Next-step suggestions based on current context
12
+ */
13
+
14
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
15
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
16
+ import {
17
+ CallToolRequestSchema,
18
+ ListToolsRequestSchema,
19
+ ListResourcesRequestSchema,
20
+ ReadResourceRequestSchema,
21
+ } from "@modelcontextprotocol/sdk/types.js";
22
+ import { MethodologyRepository } from "./repository.js";
23
+ import { SkillReader } from "./tools/skill-reader.js";
24
+ import { ReferenceReader } from "./tools/reference-reader.js";
25
+ import { VPCQuerier } from "./tools/vpc-querier.js";
26
+ import { GuidanceSearcher } from "./tools/guidance-searcher.js";
27
+ import { NextStepSuggester } from "./tools/next-step-suggester.js";
28
+ import { GherkinValidator } from "./tools/gherkin-validator.js";
29
+
30
+ const server = new Server(
31
+ {
32
+ name: "software-methodology",
33
+ version: "0.1.0",
34
+ },
35
+ {
36
+ capabilities: {
37
+ tools: {},
38
+ resources: {},
39
+ },
40
+ }
41
+ );
42
+
43
+ // Initialize repository
44
+ const repository = new MethodologyRepository();
45
+
46
+ // Initialize tools
47
+ const skillReader = new SkillReader(repository);
48
+ const referenceReader = new ReferenceReader(repository);
49
+ const vpcQuerier = new VPCQuerier(repository);
50
+ const guidanceSearcher = new GuidanceSearcher(repository);
51
+ const nextStepSuggester = new NextStepSuggester(repository);
52
+ const gherkinValidator = new GherkinValidator(repository);
53
+
54
+ /**
55
+ * List all available tools
56
+ */
57
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
58
+ return {
59
+ tools: [
60
+ {
61
+ name: "read_skill",
62
+ description: "Read main SKILL.md documentation for a specific skill",
63
+ inputSchema: {
64
+ type: "object",
65
+ properties: {
66
+ skill_name: {
67
+ type: "string",
68
+ description: "Name of the skill (e.g., 'atdd-workflow', 'clean-code', 'product-strategy')",
69
+ },
70
+ },
71
+ required: ["skill_name"],
72
+ },
73
+ },
74
+ {
75
+ name: "list_skills",
76
+ description: "List all available skills in the methodology",
77
+ inputSchema: {
78
+ type: "object",
79
+ properties: {},
80
+ },
81
+ },
82
+ {
83
+ name: "read_reference",
84
+ description: "Read a specific reference document from a skill",
85
+ inputSchema: {
86
+ type: "object",
87
+ properties: {
88
+ reference_path: {
89
+ type: "string",
90
+ description: "Path to reference (e.g., 'clean-code/solid.md', 'atdd-workflow/red-phase.md')",
91
+ },
92
+ },
93
+ required: ["reference_path"],
94
+ },
95
+ },
96
+ {
97
+ name: "list_references",
98
+ description: "List all reference documents for a specific skill",
99
+ inputSchema: {
100
+ type: "object",
101
+ properties: {
102
+ skill_name: {
103
+ type: "string",
104
+ description: "Name of the skill",
105
+ },
106
+ },
107
+ required: ["skill_name"],
108
+ },
109
+ },
110
+ {
111
+ name: "query_vpc_segments",
112
+ description: "Get all customer segments from Value Proposition Canvas",
113
+ inputSchema: {
114
+ type: "object",
115
+ properties: {},
116
+ },
117
+ },
118
+ {
119
+ name: "query_vpc_customer_jobs",
120
+ description: "Get customer jobs for a specific segment",
121
+ inputSchema: {
122
+ type: "object",
123
+ properties: {
124
+ segment: {
125
+ type: "string",
126
+ description: "Customer segment name (e.g., 'Individual Developer')",
127
+ },
128
+ },
129
+ required: ["segment"],
130
+ },
131
+ },
132
+ {
133
+ name: "query_vpc_pains",
134
+ description: "Get pains for a specific segment",
135
+ inputSchema: {
136
+ type: "object",
137
+ properties: {
138
+ segment: {
139
+ type: "string",
140
+ description: "Customer segment name",
141
+ },
142
+ },
143
+ required: ["segment"],
144
+ },
145
+ },
146
+ {
147
+ name: "query_vpc_gains",
148
+ description: "Get gains for a specific segment",
149
+ inputSchema: {
150
+ type: "object",
151
+ properties: {
152
+ segment: {
153
+ type: "string",
154
+ description: "Customer segment name",
155
+ },
156
+ },
157
+ required: ["segment"],
158
+ },
159
+ },
160
+ {
161
+ name: "search_vpc",
162
+ description: "Search for specific terms across VPC pains and gains",
163
+ inputSchema: {
164
+ type: "object",
165
+ properties: {
166
+ query: {
167
+ type: "string",
168
+ description: "Search term",
169
+ },
170
+ },
171
+ required: ["query"],
172
+ },
173
+ },
174
+ {
175
+ name: "search_guidance",
176
+ description: "Search across all skills and references for relevant guidance",
177
+ inputSchema: {
178
+ type: "object",
179
+ properties: {
180
+ query: {
181
+ type: "string",
182
+ description: "What you're looking for (e.g., 'test before code', 'many parameters', 'hard to test')",
183
+ },
184
+ },
185
+ required: ["query"],
186
+ },
187
+ },
188
+ {
189
+ name: "suggest_next_step",
190
+ description: "Get suggestion for next methodology step based on current project state",
191
+ inputSchema: {
192
+ type: "object",
193
+ properties: {
194
+ project_path: {
195
+ type: "string",
196
+ description: "Path to project directory (optional, defaults to current directory)",
197
+ },
198
+ },
199
+ },
200
+ },
201
+ {
202
+ name: "validate_gherkin",
203
+ description: "Validate a Gherkin feature file against methodology best practices",
204
+ inputSchema: {
205
+ type: "object",
206
+ properties: {
207
+ content: {
208
+ type: "string",
209
+ description: "Gherkin feature file content to validate",
210
+ },
211
+ },
212
+ required: ["content"],
213
+ },
214
+ },
215
+ ],
216
+ };
217
+ });
218
+
219
+ /**
220
+ * Handle tool execution
221
+ */
222
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
223
+ const { name, arguments: args } = request.params;
224
+
225
+ try {
226
+ switch (name) {
227
+ case "read_skill":
228
+ return await skillReader.readSkill(args?.skill_name as string);
229
+
230
+ case "list_skills":
231
+ return await skillReader.listSkills();
232
+
233
+ case "read_reference":
234
+ return await referenceReader.readReference(args?.reference_path as string);
235
+
236
+ case "list_references":
237
+ return await referenceReader.listReferences(args?.skill_name as string);
238
+
239
+ case "query_vpc_segments":
240
+ return await vpcQuerier.getSegments();
241
+
242
+ case "query_vpc_customer_jobs":
243
+ return await vpcQuerier.getCustomerJobs(args?.segment as string);
244
+
245
+ case "query_vpc_pains":
246
+ return await vpcQuerier.getPains(args?.segment as string);
247
+
248
+ case "query_vpc_gains":
249
+ return await vpcQuerier.getGains(args?.segment as string);
250
+
251
+ case "search_vpc":
252
+ return await vpcQuerier.search(args?.query as string);
253
+
254
+ case "search_guidance":
255
+ return await guidanceSearcher.search(args?.query as string);
256
+
257
+ case "suggest_next_step":
258
+ return await nextStepSuggester.suggest(args?.project_path as string | undefined);
259
+
260
+ case "validate_gherkin":
261
+ return await gherkinValidator.validate(args?.content as string);
262
+
263
+ default:
264
+ throw new Error(`Unknown tool: ${name}`);
265
+ }
266
+ } catch (error) {
267
+ return {
268
+ content: [
269
+ {
270
+ type: "text",
271
+ text: `Error: ${error instanceof Error ? error.message : String(error)}`,
272
+ },
273
+ ],
274
+ isError: true,
275
+ };
276
+ }
277
+ });
278
+
279
+ /**
280
+ * List resources (alternative access pattern to tools)
281
+ */
282
+ server.setRequestHandler(ListResourcesRequestSchema, async () => {
283
+ const skills = await repository.listSkills();
284
+
285
+ return {
286
+ resources: skills.map((skill) => ({
287
+ uri: `methodology://skill/${skill.name}`,
288
+ mimeType: "text/markdown",
289
+ name: skill.displayName,
290
+ description: skill.description,
291
+ })),
292
+ };
293
+ });
294
+
295
+ /**
296
+ * Read resource
297
+ */
298
+ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
299
+ const uri = request.params.uri;
300
+
301
+ if (uri.startsWith("methodology://skill/")) {
302
+ const skillName = uri.replace("methodology://skill/", "");
303
+ const result = await skillReader.readSkill(skillName);
304
+
305
+ return {
306
+ contents: [
307
+ {
308
+ uri,
309
+ mimeType: "text/markdown",
310
+ text: result.content[0].text,
311
+ },
312
+ ],
313
+ };
314
+ }
315
+
316
+ throw new Error(`Unknown resource URI: ${uri}`);
317
+ });
318
+
319
+ /**
320
+ * Start the server
321
+ */
322
+ async function main() {
323
+ const transport = new StdioServerTransport();
324
+ await server.connect(transport);
325
+ console.error("Software Methodology MCP server running on stdio");
326
+ }
327
+
328
+ main().catch((error) => {
329
+ console.error("Fatal error in main():", error);
330
+ process.exit(1);
331
+ });