@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.
- package/.github/ISSUE_TEMPLATE/bug_report.md +40 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +31 -0
- package/.github/pull_request_template.md +48 -0
- package/.github/workflows/deploy-docs.yml +59 -0
- package/.github/workflows/npm-publish.yml +48 -0
- package/.github/workflows/test.yml +48 -0
- package/CHANGELOG.md +92 -0
- package/CONTRIBUTING.md +438 -0
- package/LICENSE +190 -0
- package/PROJECT_SUMMARY.md +318 -0
- package/README.md +444 -0
- package/RELEASE_CHECKLIST.md +182 -0
- package/dist/application.js +166 -0
- package/dist/application.js.map +1 -0
- package/dist/commands/release.js +326 -0
- package/dist/commands/release.js.map +1 -0
- package/dist/constants.js +122 -0
- package/dist/constants.js.map +1 -0
- package/dist/logging.js +176 -0
- package/dist/logging.js.map +1 -0
- package/dist/main.js +24 -0
- package/dist/main.js.map +1 -0
- package/dist/mcp-server.js +17467 -0
- package/dist/mcp-server.js.map +7 -0
- package/dist/utils/config.js +89 -0
- package/dist/utils/config.js.map +1 -0
- package/docs/AI_GUIDE.md +618 -0
- package/eslint.config.mjs +85 -0
- package/guide/architecture.md +776 -0
- package/guide/commands.md +580 -0
- package/guide/configuration.md +779 -0
- package/guide/mcp-integration.md +708 -0
- package/guide/overview.md +225 -0
- package/package.json +91 -0
- package/scripts/build-mcp.js +115 -0
- package/scripts/test-mcp-compliance.js +254 -0
- package/src/application.ts +246 -0
- package/src/commands/release.ts +450 -0
- package/src/constants.ts +162 -0
- package/src/logging.ts +210 -0
- package/src/main.ts +25 -0
- package/src/mcp/prompts/index.ts +98 -0
- package/src/mcp/resources.ts +121 -0
- package/src/mcp/server.ts +195 -0
- package/src/mcp/tools.ts +219 -0
- package/src/types.ts +131 -0
- package/src/utils/config.ts +181 -0
- package/tests/application.test.ts +114 -0
- package/tests/commands/commit.test.ts +248 -0
- package/tests/commands/release.test.ts +325 -0
- package/tests/constants.test.ts +118 -0
- package/tests/logging.test.ts +142 -0
- package/tests/mcp/prompts/index.test.ts +202 -0
- package/tests/mcp/resources.test.ts +166 -0
- package/tests/mcp/tools.test.ts +211 -0
- package/tests/utils/config.test.ts +212 -0
- package/tsconfig.json +32 -0
- package/vite.config.ts +107 -0
- package/vitest.config.ts +40 -0
- package/website/index.html +14 -0
- package/website/src/App.css +142 -0
- package/website/src/App.tsx +34 -0
- package/website/src/components/Commands.tsx +182 -0
- package/website/src/components/Configuration.tsx +214 -0
- package/website/src/components/Examples.tsx +234 -0
- package/website/src/components/Footer.css +99 -0
- package/website/src/components/Footer.tsx +93 -0
- package/website/src/components/GettingStarted.tsx +94 -0
- package/website/src/components/Hero.css +95 -0
- package/website/src/components/Hero.tsx +50 -0
- package/website/src/components/Navigation.css +102 -0
- package/website/src/components/Navigation.tsx +57 -0
- package/website/src/index.css +36 -0
- package/website/src/main.tsx +10 -0
- 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();
|