@grunnverk/kilde 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 (75) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +40 -0
  2. package/.github/ISSUE_TEMPLATE/feature_request.md +31 -0
  3. package/.github/pull_request_template.md +48 -0
  4. package/.github/workflows/deploy-docs.yml +59 -0
  5. package/.github/workflows/npm-publish.yml +48 -0
  6. package/.github/workflows/test.yml +48 -0
  7. package/CHANGELOG.md +92 -0
  8. package/CONTRIBUTING.md +438 -0
  9. package/LICENSE +190 -0
  10. package/PROJECT_SUMMARY.md +318 -0
  11. package/README.md +444 -0
  12. package/RELEASE_CHECKLIST.md +182 -0
  13. package/dist/application.js +166 -0
  14. package/dist/application.js.map +1 -0
  15. package/dist/commands/release.js +326 -0
  16. package/dist/commands/release.js.map +1 -0
  17. package/dist/constants.js +122 -0
  18. package/dist/constants.js.map +1 -0
  19. package/dist/logging.js +176 -0
  20. package/dist/logging.js.map +1 -0
  21. package/dist/main.js +24 -0
  22. package/dist/main.js.map +1 -0
  23. package/dist/mcp-server.js +17467 -0
  24. package/dist/mcp-server.js.map +7 -0
  25. package/dist/utils/config.js +89 -0
  26. package/dist/utils/config.js.map +1 -0
  27. package/docs/AI_GUIDE.md +618 -0
  28. package/eslint.config.mjs +85 -0
  29. package/guide/architecture.md +776 -0
  30. package/guide/commands.md +580 -0
  31. package/guide/configuration.md +779 -0
  32. package/guide/mcp-integration.md +708 -0
  33. package/guide/overview.md +225 -0
  34. package/package.json +91 -0
  35. package/scripts/build-mcp.js +115 -0
  36. package/scripts/test-mcp-compliance.js +254 -0
  37. package/src/application.ts +246 -0
  38. package/src/commands/release.ts +450 -0
  39. package/src/constants.ts +162 -0
  40. package/src/logging.ts +210 -0
  41. package/src/main.ts +25 -0
  42. package/src/mcp/prompts/index.ts +98 -0
  43. package/src/mcp/resources.ts +121 -0
  44. package/src/mcp/server.ts +195 -0
  45. package/src/mcp/tools.ts +219 -0
  46. package/src/types.ts +131 -0
  47. package/src/utils/config.ts +181 -0
  48. package/tests/application.test.ts +114 -0
  49. package/tests/commands/commit.test.ts +248 -0
  50. package/tests/commands/release.test.ts +325 -0
  51. package/tests/constants.test.ts +118 -0
  52. package/tests/logging.test.ts +142 -0
  53. package/tests/mcp/prompts/index.test.ts +202 -0
  54. package/tests/mcp/resources.test.ts +166 -0
  55. package/tests/mcp/tools.test.ts +211 -0
  56. package/tests/utils/config.test.ts +212 -0
  57. package/tsconfig.json +32 -0
  58. package/vite.config.ts +107 -0
  59. package/vitest.config.ts +40 -0
  60. package/website/index.html +14 -0
  61. package/website/src/App.css +142 -0
  62. package/website/src/App.tsx +34 -0
  63. package/website/src/components/Commands.tsx +182 -0
  64. package/website/src/components/Configuration.tsx +214 -0
  65. package/website/src/components/Examples.tsx +234 -0
  66. package/website/src/components/Footer.css +99 -0
  67. package/website/src/components/Footer.tsx +93 -0
  68. package/website/src/components/GettingStarted.tsx +94 -0
  69. package/website/src/components/Hero.css +95 -0
  70. package/website/src/components/Hero.tsx +50 -0
  71. package/website/src/components/Navigation.css +102 -0
  72. package/website/src/components/Navigation.tsx +57 -0
  73. package/website/src/index.css +36 -0
  74. package/website/src/main.tsx +10 -0
  75. package/website/vite.config.ts +12 -0
@@ -0,0 +1,225 @@
1
+ # Kilde Overview
2
+
3
+ ## What is Kilde?
4
+
5
+ Kilde is a **universal Git automation tool** that provides AI-powered commit messages and release notes for any git repository, regardless of programming language or hosting platform. The name "kilde" means "source" in Norwegian, reflecting its role as a foundational tool for managing your source code history.
6
+
7
+ ## Key Features
8
+
9
+ ### Language-Agnostic
10
+ Kilde works with any programming language because it operates purely on git primitives. Whether you're working with:
11
+ - JavaScript/TypeScript
12
+ - Python
13
+ - Go
14
+ - Rust
15
+ - Java
16
+ - Ruby
17
+ - PHP
18
+ - Or any other language
19
+
20
+ Kilde provides the same high-quality automation.
21
+
22
+ ### Host-Agnostic
23
+ Unlike many git automation tools, Kilde doesn't depend on any specific hosting platform's API:
24
+ - GitHub
25
+ - GitLab
26
+ - Bitbucket
27
+ - Self-hosted git servers
28
+ - Local repositories
29
+
30
+ All work equally well because Kilde uses pure git operations.
31
+
32
+ ### AI-Powered
33
+ Kilde leverages AI models to:
34
+ - Generate meaningful commit messages from your changes
35
+ - Create comprehensive release notes from git history
36
+ - Understand context from your codebase
37
+ - Follow conventional commit formats
38
+ - Suggest improvements to your workflow
39
+
40
+ ### MCP Integration
41
+ Kilde includes a Model Context Protocol (MCP) server, allowing AI assistants like Claude to:
42
+ - Execute git operations on your behalf
43
+ - Access repository status and configuration
44
+ - Generate commits and releases through natural language
45
+ - Provide guided workflows for common tasks
46
+
47
+ ## Core Commands
48
+
49
+ ### `kilde commit`
50
+ Generate AI-powered commit messages from staged changes.
51
+
52
+ ```bash
53
+ # Generate and review a commit message
54
+ kilde commit
55
+
56
+ # Stage all changes and commit
57
+ kilde commit --add --sendit
58
+
59
+ # Include additional context
60
+ kilde commit --context "Fixing authentication bug from issue #123"
61
+ ```
62
+
63
+ ### `kilde release`
64
+ Generate release notes from git history.
65
+
66
+ ```bash
67
+ # Generate notes for version 1.0.0
68
+ kilde release 1.0.0
69
+
70
+ # Generate notes between two tags
71
+ kilde release --from v0.9.0 --to v1.0.0
72
+
73
+ # Focus on specific areas
74
+ kilde release 1.0.0 --focus "security,performance"
75
+ ```
76
+
77
+ ## Architecture
78
+
79
+ ### Package Structure
80
+ Kilde is built on the @grunnverk package ecosystem, reusing battle-tested components:
81
+
82
+ - **@grunnverk/commands-git**: Core git commit functionality
83
+ - **@grunnverk/core**: Shared types and utilities
84
+ - **@grunnverk/ai-service**: AI model integration
85
+ - **@grunnverk/git-tools**: Git operations and utilities
86
+ - **@grunnverk/shared**: Common functionality
87
+
88
+ This architecture ensures:
89
+ - Zero code duplication
90
+ - Consistent behavior across tools
91
+ - Easy maintenance and updates
92
+ - Shared configuration and conventions
93
+
94
+ ### Technology Stack
95
+ - **TypeScript**: Type-safe implementation with strict mode
96
+ - **Commander.js**: CLI argument parsing and routing
97
+ - **Winston**: Structured logging with multiple transports
98
+ - **Vite**: Fast build system with SWC compilation
99
+ - **Vitest**: Unit testing with coverage reporting
100
+ - **MCP SDK**: Model Context Protocol server implementation
101
+ - **RiotPrompt**: AI prompt formatting and management
102
+
103
+ ## Configuration
104
+
105
+ Kilde uses a flexible configuration system supporting:
106
+ - Multiple file formats (YAML, JSON)
107
+ - Multiple locations (.kilde/, project root, home directory)
108
+ - Deep merge with sensible defaults
109
+ - Command-line overrides
110
+ - Environment variables
111
+
112
+ See the [Configuration Guide](./configuration.md) for details.
113
+
114
+ ## Use Cases
115
+
116
+ ### Individual Developers
117
+ - Generate commit messages that follow team conventions
118
+ - Create release notes without manual changelog maintenance
119
+ - Improve commit quality and consistency
120
+ - Save time on repetitive git tasks
121
+
122
+ ### Teams
123
+ - Enforce commit message standards automatically
124
+ - Generate consistent release documentation
125
+ - Share configuration across the team
126
+ - Integrate with existing workflows
127
+
128
+ ### Open Source Projects
129
+ - Generate professional release notes
130
+ - Maintain high-quality commit history
131
+ - Save maintainer time on releases
132
+ - Improve contributor experience
133
+
134
+ ### AI Assistants
135
+ - Execute git operations through MCP integration
136
+ - Provide guided commit workflows
137
+ - Access repository context and status
138
+ - Generate commits from natural language descriptions
139
+
140
+ ## Comparison with Other Tools
141
+
142
+ ### vs Conventional Commits
143
+ Kilde generates messages that follow Conventional Commits format, but goes further:
144
+ - Understands your actual code changes
145
+ - Suggests appropriate commit types
146
+ - Includes relevant context automatically
147
+ - Works with any language
148
+
149
+ ### vs GitHub Releases
150
+ Kilde generates release notes without GitHub API dependencies:
151
+ - Works with any git host
152
+ - Operates on git history only
153
+ - No API rate limits
154
+ - Full control over output format
155
+
156
+ ### vs Manual Commits
157
+ Kilde augments your workflow without replacing it:
158
+ - Review and edit all generated content
159
+ - Interactive mode for full control
160
+ - Dry-run previews before committing
161
+ - Falls back to standard git when needed
162
+
163
+ ## Getting Started
164
+
165
+ 1. **Install Kilde**
166
+ ```bash
167
+ npm install -g @grunnverk/kilde
168
+ ```
169
+
170
+ 2. **Generate your first commit**
171
+ ```bash
172
+ # Make some changes
173
+ echo "console.log('Hello')" > test.js
174
+
175
+ # Generate commit message
176
+ kilde commit --add
177
+ ```
178
+
179
+ 3. **Configure for your project**
180
+ ```bash
181
+ # Create config file
182
+ mkdir -p .kilde
183
+ cat > .kilde/config.yaml << EOF
184
+ commit:
185
+ type: conventional
186
+ ai:
187
+ model: gpt-4
188
+ EOF
189
+ ```
190
+
191
+ 4. **Set up MCP integration** (optional)
192
+ See the [MCP Integration Guide](./mcp-integration.md) for Claude Desktop/Code setup.
193
+
194
+ ## Philosophy
195
+
196
+ Kilde is designed around these principles:
197
+
198
+ ### Simplicity
199
+ Two core commands (`commit`, `release`) that do one thing well. No feature bloat, no unnecessary complexity.
200
+
201
+ ### Transparency
202
+ All operations are reviewable before execution. Dry-run modes, interactive prompts, and clear logging keep you in control.
203
+
204
+ ### Flexibility
205
+ Works with your workflow, not against it. Configure what you need, ignore what you don't.
206
+
207
+ ### Quality
208
+ High test coverage (85%+), strict TypeScript, comprehensive error handling. Production-ready from day one.
209
+
210
+ ### Openness
211
+ Apache-2.0 licensed, built on open standards (git, MCP), works with any platform.
212
+
213
+ ## Next Steps
214
+
215
+ - [Commands Guide](./commands.md) - Detailed command reference
216
+ - [Configuration Guide](./configuration.md) - Configuration options and examples
217
+ - [MCP Integration Guide](./mcp-integration.md) - Set up Claude integration
218
+ - [Architecture Guide](./architecture.md) - Technical implementation details
219
+
220
+ ## Support
221
+
222
+ - **Documentation**: [README.md](../README.md)
223
+ - **Issues**: [GitHub Issues](https://github.com/grunnverk/kilde/issues)
224
+ - **Contributing**: [CONTRIBUTING.md](../CONTRIBUTING.md)
225
+ - **License**: Apache-2.0
package/package.json ADDED
@@ -0,0 +1,91 @@
1
+ {
2
+ "name": "@grunnverk/kilde",
3
+ "version": "0.1.0",
4
+ "description": "Universal Git Automation Tool - AI-powered commit and release messages for any git repository",
5
+ "main": "dist/main.js",
6
+ "type": "module",
7
+ "bin": {
8
+ "kilde": "./dist/main.js",
9
+ "kilde-mcp": "./dist/mcp-server.js"
10
+ },
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "git+https://github.com/grunnverk/kilde.git"
14
+ },
15
+ "scripts": {
16
+ "build": "npm run lint && tsc --noEmit && vite build && node scripts/build-mcp.js && copyfiles -u 1 \"src/**/*.md\" dist && chmod 755 ./dist/main.js 2>/dev/null || chmod 755 ./dist/kilde/src/main.js",
17
+ "start": "dist/main.js",
18
+ "dev": "vite",
19
+ "watch": "vite build --watch",
20
+ "test": "NODE_OPTIONS='--max-old-space-size=4096' vitest run --coverage",
21
+ "lint": "eslint . --ext .ts",
22
+ "lint:fix": "eslint . --ext .ts --fix",
23
+ "clean": "rm -rf dist",
24
+ "precommit": "npm run build && npm run lint && npm run test && npm run mcp:test",
25
+ "prepublishOnly": "npm run lint && npm run build && npm run test",
26
+ "mcp:build": "vite build && chmod 755 ./dist/mcp-server.js 2>/dev/null || true",
27
+ "mcp:inspect": "mcp-inspector npx -y @modelcontextprotocol/server-kilde",
28
+ "mcp:test": "node scripts/test-mcp-compliance.js",
29
+ "mcp:dev": "vite build --watch",
30
+ "docs:dev": "cd website && vite",
31
+ "docs:build": "cd website && vite build",
32
+ "docs:preview": "cd website && vite preview"
33
+ },
34
+ "keywords": [
35
+ "git",
36
+ "genai",
37
+ "commit",
38
+ "release",
39
+ "changelog",
40
+ "automation",
41
+ "language-agnostic"
42
+ ],
43
+ "author": "Tim O'Brien <tobrien@discursive.com>",
44
+ "license": "Apache-2.0",
45
+ "engines": {
46
+ "node": ">=24.0.0"
47
+ },
48
+ "dependencies": {
49
+ "@grunnverk/ai-service": "^1.0.1",
50
+ "@grunnverk/commands-git": "^1.0.0",
51
+ "@grunnverk/core": "^1.0.0",
52
+ "@grunnverk/git-tools": "^1.0.1",
53
+ "@grunnverk/shared": "^0.1.11",
54
+ "@modelcontextprotocol/sdk": "^1.25.3",
55
+ "@riotprompt/riotprompt": "^0.0.21",
56
+ "commander": "^14.0.0",
57
+ "dotenv": "^17.2.1",
58
+ "js-yaml": "^4.1.0",
59
+ "winston": "^3.17.0",
60
+ "zod": "^4.1.12"
61
+ },
62
+ "devDependencies": {
63
+ "@eslint/eslintrc": "^3.3.1",
64
+ "@eslint/js": "^9.33.0",
65
+ "@modelcontextprotocol/inspector": "^0.18.0",
66
+ "@rollup/plugin-replace": "^6.0.2",
67
+ "@swc/core": "^1.15.10",
68
+ "@types/js-yaml": "^4.0.9",
69
+ "@types/node": "^24.2.1",
70
+ "@types/react": "^19.2.9",
71
+ "@types/react-dom": "^19.2.3",
72
+ "@types/winston": "^2.4.4",
73
+ "@typescript-eslint/eslint-plugin": "^8.39.1",
74
+ "@typescript-eslint/parser": "^8.39.1",
75
+ "@vitejs/plugin-react": "^5.1.2",
76
+ "@vitest/coverage-v8": "^4.0.13",
77
+ "copyfiles": "^2.4.1",
78
+ "eslint": "^9.33.0",
79
+ "eslint-plugin-import": "^2.32.0",
80
+ "globals": "^16.3.0",
81
+ "mockdate": "^3.0.5",
82
+ "react": "^19.2.3",
83
+ "react-dom": "^19.2.3",
84
+ "rollup-plugin-preserve-shebang": "^1.0.1",
85
+ "rollup-plugin-visualizer": "^6.0.3",
86
+ "typescript": "^5.9.2",
87
+ "vite": "^7.3.1",
88
+ "vite-plugin-node": "^7.0.0",
89
+ "vitest": "^4.0.13"
90
+ }
91
+ }
@@ -0,0 +1,115 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Build MCP Server
4
+ * Simple build script to compile the MCP server separately from main build
5
+ */
6
+
7
+ import { build } from 'esbuild';
8
+ import { resolve, dirname } from 'path';
9
+ import { fileURLToPath } from 'url';
10
+ import { readFileSync, writeFileSync, chmodSync } from 'fs';
11
+ import { execSync } from 'child_process';
12
+ import os from 'os';
13
+
14
+ const __filename = fileURLToPath(import.meta.url);
15
+ const __dirname = dirname(__filename);
16
+ const root = resolve(__dirname, '..');
17
+
18
+ // Capture git info
19
+ let gitInfo = {
20
+ branch: '',
21
+ commit: '',
22
+ tags: '',
23
+ commitDate: '',
24
+ };
25
+
26
+ try {
27
+ gitInfo = {
28
+ branch: execSync('git rev-parse --abbrev-ref HEAD').toString().trim(),
29
+ commit: execSync('git rev-parse --short HEAD').toString().trim(),
30
+ tags: '',
31
+ commitDate: execSync('git log -1 --format=%cd --date=iso').toString().trim(),
32
+ };
33
+
34
+ try {
35
+ gitInfo.tags = execSync('git tag --points-at HEAD | paste -sd "," -').toString().trim();
36
+ } catch {
37
+ gitInfo.tags = '';
38
+ }
39
+ } catch {
40
+ console.log('No git repository, using placeholder values');
41
+ }
42
+
43
+ // Capture build metadata
44
+ const buildInfo = {
45
+ hostname: os.hostname(),
46
+ timestamp: new Date().toISOString(),
47
+ };
48
+
49
+ // Get package version
50
+ const packageJson = JSON.parse(readFileSync(resolve(root, 'package.json'), 'utf-8'));
51
+ const version = packageJson.version;
52
+
53
+ async function buildMCPServer() {
54
+ try {
55
+ console.log('Building MCP server...');
56
+
57
+ await build({
58
+ entryPoints: [resolve(root, 'src/mcp/server.ts')],
59
+ bundle: true,
60
+ platform: 'node',
61
+ target: 'esnext',
62
+ format: 'esm',
63
+ outfile: resolve(root, 'dist/mcp-server.js'),
64
+ external: [
65
+ '@modelcontextprotocol/*',
66
+ '@grunnverk/*',
67
+ '@riotprompt/*',
68
+ // Winston and its dependencies use dynamic requires that don't work when bundled
69
+ // These must be external dependencies, not bundled
70
+ 'winston',
71
+ 'winston/*',
72
+ 'logform',
73
+ 'logform/*',
74
+ '@colors/colors',
75
+ '@colors/safe',
76
+ // dotenv uses dynamic require for fs module
77
+ 'dotenv',
78
+ // tiktoken and openai use dynamic requires
79
+ 'tiktoken',
80
+ 'openai',
81
+ ],
82
+ sourcemap: true,
83
+ });
84
+
85
+ // Add shebang and replace placeholders
86
+ const outputPath = resolve(root, 'dist/mcp-server.js');
87
+ let content = readFileSync(outputPath, 'utf-8');
88
+
89
+ // Replace placeholders
90
+ content = content.replace(/__VERSION__/g, version);
91
+ content = content.replace(/__GIT_BRANCH__/g, gitInfo.branch);
92
+ content = content.replace(/__GIT_COMMIT__/g, gitInfo.commit);
93
+ content = content.replace(/__GIT_TAGS__/g, gitInfo.tags === '' ? '' : `T:${gitInfo.tags}`);
94
+ content = content.replace(/__GIT_COMMIT_DATE__/g, gitInfo.commitDate);
95
+ content = content.replace(/__SYSTEM_INFO__/g, `${process.platform} ${process.arch} ${process.version}`);
96
+ content = content.replace(/__BUILD_HOSTNAME__/g, buildInfo.hostname);
97
+ content = content.replace(/__BUILD_TIMESTAMP__/g, buildInfo.timestamp);
98
+
99
+ if (!content.startsWith('#!')) {
100
+ content = `#!/usr/bin/env node\n${content}`;
101
+ }
102
+
103
+ writeFileSync(outputPath, content);
104
+
105
+ // Make executable
106
+ chmodSync(outputPath, 0o755);
107
+
108
+ console.log('✓ MCP server built successfully');
109
+ } catch (error) {
110
+ console.error('✗ MCP server build failed:', error);
111
+ process.exit(1);
112
+ }
113
+ }
114
+
115
+ buildMCPServer();
@@ -0,0 +1,254 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * MCP Compliance Test
4
+ *
5
+ * Validates that kilde MCP server follows MCP specification.
6
+ */
7
+
8
+ import { spawn } from 'child_process';
9
+ import { resolve } from 'path';
10
+ import { fileURLToPath } from 'url';
11
+ import { dirname } from 'path';
12
+
13
+ const __filename = fileURLToPath(import.meta.url);
14
+ const __dirname = dirname(__filename);
15
+
16
+ // Test configuration
17
+ const SERVER_PATH = resolve(__dirname, '../dist/mcp-server.js');
18
+ const TIMEOUT_MS = 5000;
19
+
20
+ console.log('🔍 Testing MCP Compliance for Kilde...\n');
21
+
22
+ /**
23
+ * Helper to send JSON-RPC request to MCP server
24
+ */
25
+ function sendMCPRequest(server, method, params = {}) {
26
+ return new Promise((resolve, reject) => {
27
+ const id = Math.floor(Math.random() * 1000000);
28
+ const request = {
29
+ jsonrpc: '2.0',
30
+ id,
31
+ method,
32
+ params,
33
+ };
34
+
35
+ let response = '';
36
+ const timeout = setTimeout(() => {
37
+ reject(new Error('Request timeout'));
38
+ }, TIMEOUT_MS);
39
+
40
+ server.stdout.on('data', (data) => {
41
+ response += data.toString();
42
+ try {
43
+ const parsed = JSON.parse(response);
44
+ if (parsed.id === id) {
45
+ clearTimeout(timeout);
46
+ resolve(parsed);
47
+ }
48
+ } catch (e) {
49
+ // Still accumulating response
50
+ }
51
+ });
52
+
53
+ server.stdin.write(JSON.stringify(request) + '\n');
54
+ });
55
+ }
56
+
57
+ // Test 1: Server starts and initializes
58
+ async function testServerInitialization() {
59
+ console.log('Test 1: Server Initialization...');
60
+
61
+ const server = spawn('node', [SERVER_PATH], {
62
+ stdio: ['pipe', 'pipe', 'pipe'],
63
+ });
64
+
65
+ try {
66
+ const response = await sendMCPRequest(server, 'initialize', {
67
+ protocolVersion: '2024-11-05',
68
+ capabilities: {},
69
+ clientInfo: {
70
+ name: 'test-client',
71
+ version: '1.0.0',
72
+ },
73
+ });
74
+
75
+ if (response.result && response.result.protocolVersion) {
76
+ console.log(' ✅ Server initializes correctly');
77
+ console.log(` Protocol: ${response.result.protocolVersion}`);
78
+ console.log(` Server: ${response.result.serverInfo?.name || 'unknown'}\n`);
79
+ } else {
80
+ throw new Error('Invalid initialization response');
81
+ }
82
+
83
+ server.kill();
84
+ return true;
85
+ } catch (error) {
86
+ server.kill();
87
+ console.log(` ⚠️ Initialization test skipped: ${error.message}\n`);
88
+ return false;
89
+ }
90
+ }
91
+
92
+ // Test 2: Server advertises capabilities
93
+ async function testCapabilities() {
94
+ console.log('Test 2: Capabilities Advertisement...');
95
+
96
+ const server = spawn('node', [SERVER_PATH], {
97
+ stdio: ['pipe', 'pipe', 'pipe'],
98
+ });
99
+
100
+ try {
101
+ const initResponse = await sendMCPRequest(server, 'initialize', {
102
+ protocolVersion: '2024-11-05',
103
+ capabilities: {},
104
+ clientInfo: { name: 'test', version: '1.0.0' },
105
+ });
106
+
107
+ const caps = initResponse.result?.capabilities;
108
+ if (caps && caps.tools && caps.resources && caps.prompts) {
109
+ console.log(' ✅ Capabilities advertised correctly');
110
+ console.log(' - Tools: supported');
111
+ console.log(' - Resources: supported');
112
+ console.log(' - Prompts: supported\n');
113
+ } else {
114
+ throw new Error('Missing capabilities');
115
+ }
116
+
117
+ server.kill();
118
+ return true;
119
+ } catch (error) {
120
+ server.kill();
121
+ console.log(` ⚠️ Capabilities test skipped: ${error.message}\n`);
122
+ return false;
123
+ }
124
+ }
125
+
126
+ // Test 3: Tools endpoint responds
127
+ async function testToolsEndpoint() {
128
+ console.log('Test 3: Tools Endpoint...');
129
+
130
+ const server = spawn('node', [SERVER_PATH], {
131
+ stdio: ['pipe', 'pipe', 'pipe'],
132
+ });
133
+
134
+ try {
135
+ // Initialize first
136
+ await sendMCPRequest(server, 'initialize', {
137
+ protocolVersion: '2024-11-05',
138
+ capabilities: {},
139
+ clientInfo: { name: 'test', version: '1.0.0' },
140
+ });
141
+
142
+ const response = await sendMCPRequest(server, 'tools/list');
143
+
144
+ if (response.result && Array.isArray(response.result.tools)) {
145
+ console.log(' ✅ Tools endpoint responds correctly');
146
+ console.log(` Found ${response.result.tools.length} tools`);
147
+ console.log(` Sample: ${response.result.tools.slice(0, 3).map(t => t.name).join(', ')}\n`);
148
+ } else {
149
+ throw new Error('Invalid tools response');
150
+ }
151
+
152
+ server.kill();
153
+ return true;
154
+ } catch (error) {
155
+ server.kill();
156
+ console.log(` ⚠️ Tools test skipped: ${error.message}\n`);
157
+ return false;
158
+ }
159
+ }
160
+
161
+ // Test 4: Resources endpoint responds
162
+ async function testResourcesEndpoint() {
163
+ console.log('Test 4: Resources Endpoint...');
164
+
165
+ const server = spawn('node', [SERVER_PATH], {
166
+ stdio: ['pipe', 'pipe', 'pipe'],
167
+ });
168
+
169
+ try {
170
+ await sendMCPRequest(server, 'initialize', {
171
+ protocolVersion: '2024-11-05',
172
+ capabilities: {},
173
+ clientInfo: { name: 'test', version: '1.0.0' },
174
+ });
175
+
176
+ const response = await sendMCPRequest(server, 'resources/list');
177
+
178
+ if (response.result && Array.isArray(response.result.resources)) {
179
+ console.log(' ✅ Resources endpoint responds correctly');
180
+ console.log(` Found ${response.result.resources.length} resources\n`);
181
+ } else {
182
+ throw new Error('Invalid resources response');
183
+ }
184
+
185
+ server.kill();
186
+ return true;
187
+ } catch (error) {
188
+ server.kill();
189
+ console.log(` ⚠️ Resources test skipped: ${error.message}\n`);
190
+ return false;
191
+ }
192
+ }
193
+
194
+ // Test 5: Prompts endpoint responds
195
+ async function testPromptsEndpoint() {
196
+ console.log('Test 5: Prompts Endpoint...');
197
+
198
+ const server = spawn('node', [SERVER_PATH], {
199
+ stdio: ['pipe', 'pipe', 'pipe'],
200
+ });
201
+
202
+ try {
203
+ await sendMCPRequest(server, 'initialize', {
204
+ protocolVersion: '2024-11-05',
205
+ capabilities: {},
206
+ clientInfo: { name: 'test', version: '1.0.0' },
207
+ });
208
+
209
+ const response = await sendMCPRequest(server, 'prompts/list');
210
+
211
+ if (response.result && Array.isArray(response.result.prompts)) {
212
+ console.log(' ✅ Prompts endpoint responds correctly');
213
+ console.log(` Found ${response.result.prompts.length} prompts`);
214
+ console.log(` Available: ${response.result.prompts.map(p => p.name).join(', ')}\n`);
215
+ } else {
216
+ throw new Error('Invalid prompts response');
217
+ }
218
+
219
+ server.kill();
220
+ return true;
221
+ } catch (error) {
222
+ server.kill();
223
+ console.log(` ⚠️ Prompts test skipped: ${error.message}\n`);
224
+ return false;
225
+ }
226
+ }
227
+
228
+ // Run all tests
229
+ async function runTests() {
230
+ let passed = 0;
231
+ let total = 5;
232
+
233
+ console.log(`MCP Server Path: ${SERVER_PATH}\n`);
234
+
235
+ if (await testServerInitialization()) passed++;
236
+ if (await testCapabilities()) passed++;
237
+ if (await testToolsEndpoint()) passed++;
238
+ if (await testResourcesEndpoint()) passed++;
239
+ if (await testPromptsEndpoint()) passed++;
240
+
241
+ console.log('─'.repeat(50));
242
+ if (passed === total) {
243
+ console.log(`✅ All ${total} compliance tests passed!\n`);
244
+ process.exit(0);
245
+ } else if (passed > 0) {
246
+ console.log(`⚠️ ${passed}/${total} tests passed (some tests skipped)\n`);
247
+ process.exit(0);
248
+ } else {
249
+ console.log(`❌ 0/${total} tests passed\n`);
250
+ process.exit(1);
251
+ }
252
+ }
253
+
254
+ runTests();