@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.
Files changed (40) hide show
  1. package/LICENSE +21 -0
  2. package/dist/bin/harness-mcp.d.ts +2 -0
  3. package/dist/bin/harness-mcp.js +6 -0
  4. package/dist/src/index.d.ts +3 -0
  5. package/dist/src/index.js +3 -0
  6. package/dist/src/resources/learnings.d.ts +1 -0
  7. package/dist/src/resources/learnings.js +14 -0
  8. package/dist/src/resources/project.d.ts +1 -0
  9. package/dist/src/resources/project.js +9 -0
  10. package/dist/src/resources/rules.d.ts +1 -0
  11. package/dist/src/resources/rules.js +36 -0
  12. package/dist/src/resources/skills.d.ts +1 -0
  13. package/dist/src/resources/skills.js +33 -0
  14. package/dist/src/server.d.ts +17 -0
  15. package/dist/src/server.js +125 -0
  16. package/dist/src/tools/agent.d.ts +79 -0
  17. package/dist/src/tools/agent.js +96 -0
  18. package/dist/src/tools/architecture.d.ts +17 -0
  19. package/dist/src/tools/architecture.js +45 -0
  20. package/dist/src/tools/docs.d.ts +39 -0
  21. package/dist/src/tools/docs.js +65 -0
  22. package/dist/src/tools/entropy.d.ts +39 -0
  23. package/dist/src/tools/entropy.js +81 -0
  24. package/dist/src/tools/init.d.ts +33 -0
  25. package/dist/src/tools/init.js +55 -0
  26. package/dist/src/tools/linter.d.ts +63 -0
  27. package/dist/src/tools/linter.js +65 -0
  28. package/dist/src/tools/persona.d.ts +59 -0
  29. package/dist/src/tools/persona.js +106 -0
  30. package/dist/src/tools/skill.d.ts +38 -0
  31. package/dist/src/tools/skill.js +46 -0
  32. package/dist/src/tools/validate.d.ts +22 -0
  33. package/dist/src/tools/validate.js +85 -0
  34. package/dist/src/utils/config-resolver.d.ts +7 -0
  35. package/dist/src/utils/config-resolver.js +18 -0
  36. package/dist/src/utils/paths.d.ts +3 -0
  37. package/dist/src/utils/paths.js +33 -0
  38. package/dist/src/utils/result-adapter.d.ts +11 -0
  39. package/dist/src/utils/result-adapter.js +11 -0
  40. 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,7 @@
1
+ import type { Result } from '@harness-engineering/core';
2
+ export interface ProjectConfig {
3
+ name?: string;
4
+ version: number;
5
+ [key: string]: unknown;
6
+ }
7
+ export declare function resolveProjectConfig(projectPath: string): Result<ProjectConfig, Error>;
@@ -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,3 @@
1
+ export declare function resolveTemplatesDir(): string;
2
+ export declare function resolvePersonasDir(): string;
3
+ export declare function resolveSkillsDir(): string;
@@ -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;
@@ -0,0 +1,11 @@
1
+ export function resultToMcpResponse(result) {
2
+ if (result.ok) {
3
+ return {
4
+ content: [{ type: 'text', text: JSON.stringify(result.value) }],
5
+ };
6
+ }
7
+ return {
8
+ content: [{ type: 'text', text: result.error.message }],
9
+ isError: true,
10
+ };
11
+ }
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
+ }