@harness-engineering/mcp-server 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/LICENSE +21 -0
- package/dist/bin/harness-mcp.d.ts +2 -0
- package/dist/bin/harness-mcp.js +6 -0
- package/dist/src/index.d.ts +3 -0
- package/dist/src/index.js +3 -0
- package/dist/src/resources/learnings.d.ts +1 -0
- package/dist/src/resources/learnings.js +14 -0
- package/dist/src/resources/project.d.ts +1 -0
- package/dist/src/resources/project.js +9 -0
- package/dist/src/resources/rules.d.ts +1 -0
- package/dist/src/resources/rules.js +36 -0
- package/dist/src/resources/skills.d.ts +1 -0
- package/dist/src/resources/skills.js +33 -0
- package/dist/src/server.d.ts +17 -0
- package/dist/src/server.js +125 -0
- package/dist/src/tools/agent.d.ts +79 -0
- package/dist/src/tools/agent.js +96 -0
- package/dist/src/tools/architecture.d.ts +17 -0
- package/dist/src/tools/architecture.js +45 -0
- package/dist/src/tools/docs.d.ts +39 -0
- package/dist/src/tools/docs.js +65 -0
- package/dist/src/tools/entropy.d.ts +39 -0
- package/dist/src/tools/entropy.js +81 -0
- package/dist/src/tools/init.d.ts +33 -0
- package/dist/src/tools/init.js +55 -0
- package/dist/src/tools/linter.d.ts +63 -0
- package/dist/src/tools/linter.js +65 -0
- package/dist/src/tools/persona.d.ts +59 -0
- package/dist/src/tools/persona.js +106 -0
- package/dist/src/tools/skill.d.ts +38 -0
- package/dist/src/tools/skill.js +46 -0
- package/dist/src/tools/validate.d.ts +22 -0
- package/dist/src/tools/validate.js +85 -0
- package/dist/src/utils/config-resolver.d.ts +7 -0
- package/dist/src/utils/config-resolver.js +18 -0
- package/dist/src/utils/paths.d.ts +3 -0
- package/dist/src/utils/paths.js +33 -0
- package/dist/src/utils/result-adapter.d.ts +11 -0
- package/dist/src/utils/result-adapter.js +11 -0
- package/package.json +56 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import * as path from 'path';
|
|
2
|
+
import { resolveProjectConfig } from '../utils/config-resolver.js';
|
|
3
|
+
export const validateToolDefinition = {
|
|
4
|
+
name: 'validate_project',
|
|
5
|
+
description: 'Run all validation checks on a harness engineering project',
|
|
6
|
+
inputSchema: {
|
|
7
|
+
type: 'object',
|
|
8
|
+
properties: {
|
|
9
|
+
path: { type: 'string', description: 'Path to project root directory' },
|
|
10
|
+
},
|
|
11
|
+
required: ['path'],
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
export async function handleValidateProject(input) {
|
|
15
|
+
const projectPath = path.resolve(input.path);
|
|
16
|
+
const errors = [];
|
|
17
|
+
const checks = {
|
|
18
|
+
config: 'fail',
|
|
19
|
+
structure: 'skipped',
|
|
20
|
+
agentsMap: 'skipped',
|
|
21
|
+
};
|
|
22
|
+
// 1. Load config
|
|
23
|
+
const configResult = resolveProjectConfig(projectPath);
|
|
24
|
+
if (!configResult.ok) {
|
|
25
|
+
errors.push(`Config: ${configResult.error.message}`);
|
|
26
|
+
return {
|
|
27
|
+
content: [{ type: 'text', text: JSON.stringify({ valid: false, checks, errors }) }],
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
checks.config = 'pass';
|
|
31
|
+
const config = configResult.value;
|
|
32
|
+
// 2. Run validateFileStructure if conventions are available
|
|
33
|
+
try {
|
|
34
|
+
const core = await import('@harness-engineering/core');
|
|
35
|
+
if (typeof core.validateFileStructure === 'function' &&
|
|
36
|
+
Array.isArray(config.conventions)) {
|
|
37
|
+
const structureResult = await core.validateFileStructure(projectPath, config.conventions);
|
|
38
|
+
if (structureResult.ok) {
|
|
39
|
+
checks.structure = structureResult.value.valid ? 'pass' : 'fail';
|
|
40
|
+
if (!structureResult.value.valid) {
|
|
41
|
+
for (const missing of structureResult.value.missing) {
|
|
42
|
+
errors.push(`Missing required file: ${missing}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
checks.structure = 'fail';
|
|
48
|
+
errors.push(`Structure validation error: ${structureResult.error.message}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
// core not available, skip
|
|
54
|
+
}
|
|
55
|
+
// 3. Run validateAgentsMap
|
|
56
|
+
try {
|
|
57
|
+
const core = await import('@harness-engineering/core');
|
|
58
|
+
if (typeof core.validateAgentsMap === 'function') {
|
|
59
|
+
const agentsMapPath = path.join(projectPath, 'AGENTS.md');
|
|
60
|
+
const agentsResult = await core.validateAgentsMap(agentsMapPath);
|
|
61
|
+
if (agentsResult.ok) {
|
|
62
|
+
checks.agentsMap = agentsResult.value.valid ? 'pass' : 'fail';
|
|
63
|
+
if (!agentsResult.value.valid) {
|
|
64
|
+
if (agentsResult.value.missingSections.length > 0) {
|
|
65
|
+
errors.push(`AGENTS.md missing sections: ${agentsResult.value.missingSections.join(', ')}`);
|
|
66
|
+
}
|
|
67
|
+
if (agentsResult.value.brokenLinks.length > 0) {
|
|
68
|
+
errors.push(`AGENTS.md has ${agentsResult.value.brokenLinks.length} broken link(s)`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
checks.agentsMap = 'fail';
|
|
74
|
+
errors.push(`AGENTS.md validation error: ${agentsResult.error.message}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// core not available, skip
|
|
80
|
+
}
|
|
81
|
+
const valid = errors.length === 0;
|
|
82
|
+
return {
|
|
83
|
+
content: [{ type: 'text', text: JSON.stringify({ valid, checks, errors }) }],
|
|
84
|
+
};
|
|
85
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// packages/mcp-server/src/utils/config-resolver.ts
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import { Ok, Err } from '@harness-engineering/core';
|
|
5
|
+
export function resolveProjectConfig(projectPath) {
|
|
6
|
+
const configPath = path.join(projectPath, 'harness.config.json');
|
|
7
|
+
if (!fs.existsSync(configPath)) {
|
|
8
|
+
return Err(new Error(`No harness.config.json found in ${projectPath}`));
|
|
9
|
+
}
|
|
10
|
+
try {
|
|
11
|
+
const raw = fs.readFileSync(configPath, 'utf-8');
|
|
12
|
+
const config = JSON.parse(raw);
|
|
13
|
+
return Ok(config);
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
return Err(new Error(`Failed to parse config: ${error instanceof Error ? error.message : String(error)}`));
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
5
|
+
const __dirname = path.dirname(__filename);
|
|
6
|
+
function findUpDir(targetName, marker, maxLevels = 8) {
|
|
7
|
+
let dir = __dirname;
|
|
8
|
+
for (let i = 0; i < maxLevels; i++) {
|
|
9
|
+
const candidate = path.join(dir, targetName);
|
|
10
|
+
if (fs.existsSync(candidate) && fs.statSync(candidate).isDirectory()) {
|
|
11
|
+
if (fs.existsSync(path.join(candidate, marker))) {
|
|
12
|
+
return candidate;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
dir = path.dirname(dir);
|
|
16
|
+
}
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
export function resolveTemplatesDir() {
|
|
20
|
+
return (findUpDir('templates', 'base') ?? path.resolve(__dirname, '..', '..', '..', '..', 'templates'));
|
|
21
|
+
}
|
|
22
|
+
export function resolvePersonasDir() {
|
|
23
|
+
const agentsDir = findUpDir('agents', 'personas');
|
|
24
|
+
if (agentsDir)
|
|
25
|
+
return path.join(agentsDir, 'personas');
|
|
26
|
+
return path.resolve(__dirname, '..', '..', '..', '..', 'agents', 'personas');
|
|
27
|
+
}
|
|
28
|
+
export function resolveSkillsDir() {
|
|
29
|
+
const agentsDir = findUpDir('agents', 'skills');
|
|
30
|
+
if (agentsDir)
|
|
31
|
+
return path.join(agentsDir, 'skills', 'claude-code');
|
|
32
|
+
return path.resolve(__dirname, '..', '..', '..', '..', 'agents', 'skills', 'claude-code');
|
|
33
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Result } from '@harness-engineering/core';
|
|
2
|
+
export interface McpToolResponse {
|
|
3
|
+
content: Array<{
|
|
4
|
+
type: 'text';
|
|
5
|
+
text: string;
|
|
6
|
+
}>;
|
|
7
|
+
isError?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare function resultToMcpResponse(result: Result<unknown, {
|
|
10
|
+
message: string;
|
|
11
|
+
}>): McpToolResponse;
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@harness-engineering/mcp-server",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "MCP server for Harness Engineering toolkit",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"bin": {
|
|
15
|
+
"harness-mcp": "./dist/bin/harness-mcp.js"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"README.md"
|
|
20
|
+
],
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
23
|
+
"zod": "^3.22.0",
|
|
24
|
+
"yaml": "^2.3.0",
|
|
25
|
+
"handlebars": "^4.7.0",
|
|
26
|
+
"@harness-engineering/types": "0.0.0",
|
|
27
|
+
"@harness-engineering/cli": "1.0.0",
|
|
28
|
+
"@harness-engineering/core": "0.5.0",
|
|
29
|
+
"@harness-engineering/linter-gen": "0.1.0"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/node": "^20.0.0",
|
|
33
|
+
"typescript": "^5.0.0",
|
|
34
|
+
"vitest": "^2.0.0"
|
|
35
|
+
},
|
|
36
|
+
"license": "MIT",
|
|
37
|
+
"publishConfig": {
|
|
38
|
+
"access": "public"
|
|
39
|
+
},
|
|
40
|
+
"repository": {
|
|
41
|
+
"type": "git",
|
|
42
|
+
"url": "https://github.com/Intense-Visions/harness-engineering.git",
|
|
43
|
+
"directory": "packages/mcp-server"
|
|
44
|
+
},
|
|
45
|
+
"bugs": {
|
|
46
|
+
"url": "https://github.com/Intense-Visions/harness-engineering/issues"
|
|
47
|
+
},
|
|
48
|
+
"homepage": "https://github.com/Intense-Visions/harness-engineering/tree/main/packages/mcp-server#readme",
|
|
49
|
+
"scripts": {
|
|
50
|
+
"build": "tsc",
|
|
51
|
+
"test": "vitest run",
|
|
52
|
+
"test:watch": "vitest",
|
|
53
|
+
"typecheck": "tsc --noEmit",
|
|
54
|
+
"clean": "rm -rf dist"
|
|
55
|
+
}
|
|
56
|
+
}
|