agent-enderun 1.10.4 → 1.11.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 (210) hide show
  1. package/README.md +34 -45
  2. package/dist/framework-mcp/tests/tools/quality/check_lint.test.js +1 -1
  3. package/dist/framework-mcp/tests/tools/quality/check_lint.test.js.map +1 -1
  4. package/dist/src/cli/adapters/core.d.ts +1 -1
  5. package/dist/src/cli/adapters/core.js +0 -1
  6. package/dist/src/cli/adapters/core.js.map +1 -1
  7. package/dist/src/cli/adapters/index.d.ts +1 -0
  8. package/dist/src/cli/adapters/index.js +1 -0
  9. package/dist/src/cli/adapters/index.js.map +1 -1
  10. package/dist/src/cli/adapters/scaffold.d.ts +1 -1
  11. package/dist/src/cli/adapters/scaffold.js +13 -7
  12. package/dist/src/cli/adapters/scaffold.js.map +1 -1
  13. package/dist/src/cli/commands/init/scaffold-core.d.ts +5 -2
  14. package/dist/src/cli/commands/init/scaffold-core.js +28 -6
  15. package/dist/src/cli/commands/init/scaffold-core.js.map +1 -1
  16. package/dist/src/cli/commands/init.js +31 -4
  17. package/dist/src/cli/commands/init.js.map +1 -1
  18. package/dist/src/cli/commands/orchestrate.d.ts +9 -7
  19. package/dist/src/cli/commands/orchestrate.js +40 -4
  20. package/dist/src/cli/commands/orchestrate.js.map +1 -1
  21. package/dist/src/cli/commands/plan.d.ts +3 -1
  22. package/dist/src/cli/commands/plan.js +79 -22
  23. package/dist/src/cli/commands/plan.js.map +1 -1
  24. package/dist/src/cli/index.js +11 -15
  25. package/dist/src/cli/index.js.map +1 -1
  26. package/dist/src/cli/utils/compliance.js +0 -1
  27. package/dist/src/cli/utils/compliance.js.map +1 -1
  28. package/dist/src/cli/utils/config-schema.d.ts +7 -7
  29. package/dist/src/cli/utils/pkg.d.ts +19 -0
  30. package/dist/src/cli/utils/pkg.js +29 -5
  31. package/dist/src/cli/utils/pkg.js.map +1 -1
  32. package/dist/src/modules/adapters/definitions.js +1 -1
  33. package/dist/src/modules/adapters/definitions.js.map +1 -1
  34. package/dist/src/modules/adapters/shared.js +7 -4
  35. package/dist/src/modules/adapters/shared.js.map +1 -1
  36. package/dist/src/modules/agents/definitions.d.ts +1 -1
  37. package/dist/src/modules/agents/definitions.js +129 -69
  38. package/dist/src/modules/agents/definitions.js.map +1 -1
  39. package/dist/src/modules/agents/registry/analyst.js +5 -1
  40. package/dist/src/modules/agents/registry/analyst.js.map +1 -1
  41. package/dist/src/modules/agents/registry/backend.js +5 -4
  42. package/dist/src/modules/agents/registry/backend.js.map +1 -1
  43. package/dist/src/modules/agents/registry/database.js +2 -1
  44. package/dist/src/modules/agents/registry/database.js.map +1 -1
  45. package/dist/src/modules/agents/registry/devops.js +2 -0
  46. package/dist/src/modules/agents/registry/devops.js.map +1 -1
  47. package/dist/src/modules/agents/registry/explorer.js +1 -0
  48. package/dist/src/modules/agents/registry/explorer.js.map +1 -1
  49. package/dist/src/modules/agents/registry/git.js +3 -1
  50. package/dist/src/modules/agents/registry/git.js.map +1 -1
  51. package/dist/src/modules/agents/registry/manager.js +8 -2
  52. package/dist/src/modules/agents/registry/manager.js.map +1 -1
  53. package/dist/src/modules/agents/registry/native.js +5 -1
  54. package/dist/src/modules/agents/registry/native.js.map +1 -1
  55. package/dist/src/modules/agents/registry/security.js +1 -0
  56. package/dist/src/modules/agents/registry/security.js.map +1 -1
  57. package/dist/src/shared/constants.d.ts +1 -1
  58. package/dist/src/shared/constants.js +10 -9
  59. package/dist/src/shared/constants.js.map +1 -1
  60. package/dist/tests/approve.test.js +4 -9
  61. package/dist/tests/approve.test.js.map +1 -1
  62. package/dist/tests/integration/agent_flow.test.js +2 -2
  63. package/dist/tests/integration/agent_flow.test.js.map +1 -1
  64. package/dist/tests/orchestrate.test.js +2 -7
  65. package/dist/tests/orchestrate.test.js.map +1 -1
  66. package/framework-mcp/dist/constants.js +64 -0
  67. package/framework-mcp/dist/index.js +109 -0
  68. package/framework-mcp/dist/tools/control_plane/locking.js +64 -0
  69. package/framework-mcp/dist/tools/control_plane/registry.js +34 -0
  70. package/framework-mcp/dist/tools/dashboard/start_dashboard.js +29 -0
  71. package/framework-mcp/dist/tools/definitions.js +300 -0
  72. package/framework-mcp/dist/tools/file_system/batch_surgical_edit.js +59 -0
  73. package/framework-mcp/dist/tools/file_system/patch_file.js +25 -0
  74. package/framework-mcp/dist/tools/file_system/read_file.js +51 -0
  75. package/framework-mcp/dist/tools/file_system/replace_text.js +43 -0
  76. package/framework-mcp/dist/tools/file_system/write_file.js +38 -0
  77. package/framework-mcp/dist/tools/framework/audit_deps.js +41 -0
  78. package/framework-mcp/dist/tools/framework/get_status.js +5 -0
  79. package/framework-mcp/dist/tools/framework/orchestrate.js +5 -0
  80. package/framework-mcp/dist/tools/framework/run_tests.js +25 -0
  81. package/framework-mcp/dist/tools/framework/update_contract_hash.js +5 -0
  82. package/framework-mcp/dist/tools/framework/update_memory.js +8 -0
  83. package/framework-mcp/dist/tools/index.js +62 -0
  84. package/framework-mcp/dist/tools/memory/get_insights.js +34 -0
  85. package/framework-mcp/dist/tools/memory/read_memory.js +28 -0
  86. package/framework-mcp/dist/tools/messaging/log_action.js +22 -0
  87. package/framework-mcp/dist/tools/messaging/send_message.js +87 -0
  88. package/framework-mcp/dist/tools/observability/check_ports.js +26 -0
  89. package/framework-mcp/dist/tools/observability/get_health.js +19 -0
  90. package/framework-mcp/dist/tools/quality/check_lint.js +28 -0
  91. package/framework-mcp/dist/tools/search/get_gaps.js +48 -0
  92. package/framework-mcp/dist/tools/search/get_map.js +43 -0
  93. package/framework-mcp/dist/tools/search/grep_search.js +76 -0
  94. package/framework-mcp/dist/tools/search/list_dir.js +28 -0
  95. package/framework-mcp/dist/tools/shell/run_command.js +46 -0
  96. package/framework-mcp/dist/tools/types.js +1 -0
  97. package/framework-mcp/dist/utils/cli.js +20 -0
  98. package/framework-mcp/dist/utils/compliance.js +29 -0
  99. package/framework-mcp/dist/utils/fs.js +44 -0
  100. package/framework-mcp/dist/utils/metrics.js +56 -0
  101. package/framework-mcp/dist/utils/security.js +60 -0
  102. package/framework-mcp/package.json +19 -0
  103. package/framework-mcp/src/constants.ts +78 -0
  104. package/framework-mcp/src/declarations.d.ts +17 -0
  105. package/framework-mcp/src/index.ts +132 -0
  106. package/framework-mcp/src/tools/control_plane/locking.ts +71 -0
  107. package/framework-mcp/src/tools/control_plane/registry.ts +38 -0
  108. package/framework-mcp/src/tools/dashboard/start_dashboard.ts +33 -0
  109. package/framework-mcp/src/tools/definitions.ts +302 -0
  110. package/framework-mcp/src/tools/file_system/batch_surgical_edit.ts +79 -0
  111. package/framework-mcp/src/tools/file_system/patch_file.ts +33 -0
  112. package/framework-mcp/src/tools/file_system/read_file.ts +58 -0
  113. package/framework-mcp/src/tools/file_system/replace_text.ts +52 -0
  114. package/framework-mcp/src/tools/file_system/write_file.ts +45 -0
  115. package/framework-mcp/src/tools/framework/audit_deps.ts +49 -0
  116. package/framework-mcp/src/tools/framework/get_status.ts +7 -0
  117. package/framework-mcp/src/tools/framework/orchestrate.ts +7 -0
  118. package/framework-mcp/src/tools/framework/run_tests.ts +28 -0
  119. package/framework-mcp/src/tools/framework/update_contract_hash.ts +7 -0
  120. package/framework-mcp/src/tools/framework/update_memory.ts +10 -0
  121. package/framework-mcp/src/tools/index.ts +66 -0
  122. package/framework-mcp/src/tools/memory/get_insights.ts +41 -0
  123. package/framework-mcp/src/tools/memory/read_memory.ts +31 -0
  124. package/framework-mcp/src/tools/messaging/log_action.ts +28 -0
  125. package/framework-mcp/src/tools/messaging/send_message.ts +89 -0
  126. package/framework-mcp/src/tools/observability/check_ports.ts +30 -0
  127. package/framework-mcp/src/tools/observability/get_health.ts +24 -0
  128. package/framework-mcp/src/tools/quality/check_lint.ts +33 -0
  129. package/framework-mcp/src/tools/search/get_gaps.ts +54 -0
  130. package/framework-mcp/src/tools/search/get_map.ts +48 -0
  131. package/framework-mcp/src/tools/search/grep_search.ts +76 -0
  132. package/framework-mcp/src/tools/search/list_dir.ts +34 -0
  133. package/framework-mcp/src/tools/shell/run_command.ts +56 -0
  134. package/framework-mcp/src/tools/types.ts +89 -0
  135. package/framework-mcp/src/utils/cli.ts +20 -0
  136. package/framework-mcp/src/utils/compliance.ts +37 -0
  137. package/framework-mcp/src/utils/fs.ts +45 -0
  138. package/framework-mcp/src/utils/metrics.ts +73 -0
  139. package/framework-mcp/src/utils/security.ts +66 -0
  140. package/framework-mcp/tests/tools/file_system/file_system_tools.test.ts +212 -0
  141. package/framework-mcp/tests/tools/messaging/send_message.test.ts +136 -0
  142. package/framework-mcp/tests/tools/quality/check_lint.test.ts +46 -0
  143. package/framework-mcp/tests/tools/shell/run_command.test.ts +55 -0
  144. package/framework-mcp/tsconfig.json +14 -0
  145. package/package.json +5 -3
  146. package/src/cli/adapters/core.ts +2 -3
  147. package/src/cli/adapters/index.ts +1 -0
  148. package/src/cli/adapters/scaffold.ts +20 -7
  149. package/src/cli/commands/init/scaffold-core.ts +45 -6
  150. package/src/cli/commands/init.ts +31 -2
  151. package/src/cli/commands/orchestrate.ts +41 -4
  152. package/src/cli/commands/plan.ts +89 -23
  153. package/src/cli/index.ts +14 -19
  154. package/src/cli/utils/compliance.ts +8 -9
  155. package/src/cli/utils/pkg.ts +42 -13
  156. package/src/modules/adapters/definitions.ts +1 -1
  157. package/src/modules/adapters/shared.ts +7 -4
  158. package/src/modules/agents/definitions.ts +140 -73
  159. package/src/modules/agents/registry/analyst.ts +5 -1
  160. package/src/modules/agents/registry/backend.ts +5 -4
  161. package/src/modules/agents/registry/database.ts +2 -1
  162. package/src/modules/agents/registry/devops.ts +2 -0
  163. package/src/modules/agents/registry/explorer.ts +1 -0
  164. package/src/modules/agents/registry/git.ts +3 -1
  165. package/src/modules/agents/registry/manager.ts +8 -2
  166. package/src/modules/agents/registry/native.ts +5 -1
  167. package/src/modules/agents/registry/security.ts +1 -0
  168. package/src/shared/constants.ts +14 -13
  169. package/templates/prompts/bug-fix-recipe.md +20 -0
  170. package/templates/prompts/contract-design-recipe.md +21 -0
  171. package/templates/prompts/db-management-recipe.md +25 -0
  172. package/templates/prompts/deployment-recipe.md +23 -0
  173. package/templates/prompts/new-feature-recipe.md +19 -0
  174. package/templates/prompts/performance-optimization-recipe.md +23 -0
  175. package/templates/prompts/pull-request-template.md +21 -0
  176. package/templates/prompts/refactoring-recipe.md +21 -0
  177. package/templates/prompts/security-audit-recipe.md +20 -0
  178. package/templates/standards/architecture-standards.md +23 -0
  179. package/templates/standards/auth-standards.md +125 -0
  180. package/templates/standards/crud-governance.md +21 -0
  181. package/templates/standards/deployment-standards.md +21 -0
  182. package/templates/standards/frontend-standards.md +37 -0
  183. package/templates/standards/github-actions-standards.md +43 -0
  184. package/templates/standards/i18n-standards.md +17 -0
  185. package/templates/standards/kysely-standards.md +47 -0
  186. package/templates/standards/llm-governance.md +15 -0
  187. package/templates/standards/logging-and-secrets.md +34 -0
  188. package/templates/standards/mobile-standards.md +23 -0
  189. package/templates/standards/observability-standards.md +15 -0
  190. package/templates/standards/performance-standards.md +15 -0
  191. package/templates/standards/pino-standards.md +46 -0
  192. package/templates/standards/playwright-standards.md +54 -0
  193. package/templates/standards/quality-standards.md +31 -0
  194. package/templates/standards/react-query-standards.md +72 -0
  195. package/templates/standards/react-router-standards.md +62 -0
  196. package/templates/standards/security-audit-standards.md +16 -0
  197. package/templates/standards/security-standards.md +21 -0
  198. package/templates/standards/swagger-standards.md +50 -0
  199. package/templates/standards/tailwind-standards.md +20 -0
  200. package/templates/standards/testing-standards.md +31 -0
  201. package/templates/standards/typeorm-standards.md +49 -0
  202. package/templates/standards/vitest-standards.md +110 -0
  203. package/src/cli/commands/app.ts +0 -56
  204. package/src/cli/utils/app-backend.ts +0 -257
  205. package/src/cli/utils/app-docs.ts +0 -83
  206. package/src/cli/utils/app-frontend.ts +0 -263
  207. package/src/cli/utils/app-inferrer.ts +0 -63
  208. package/src/cli/utils/app-mobile.ts +0 -113
  209. package/src/cli/utils/app-types.ts +0 -248
  210. package/src/cli/utils/app.ts +0 -6
@@ -0,0 +1,44 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ /**
4
+ * Ensures directory existence.
5
+ */
6
+ export function ensureDir(dirPath) {
7
+ if (!fs.existsSync(dirPath)) {
8
+ fs.mkdirSync(dirPath, { recursive: true });
9
+ }
10
+ }
11
+ /**
12
+ * Atomically writes a text file.
13
+ */
14
+ export function writeTextFileAtomic(filePath, content) {
15
+ const dir = path.dirname(filePath);
16
+ ensureDir(dir);
17
+ const tempPath = `${filePath}.${Math.random().toString(36).slice(2, 9)}.tmp`;
18
+ const finalContent = content.endsWith("\n") ? content : `${content}\n`;
19
+ try {
20
+ fs.writeFileSync(tempPath, finalContent, "utf8");
21
+ fs.renameSync(tempPath, filePath);
22
+ }
23
+ catch (err) {
24
+ if (fs.existsSync(tempPath)) {
25
+ try {
26
+ fs.unlinkSync(tempPath);
27
+ }
28
+ catch { /* ignore */ }
29
+ }
30
+ throw err;
31
+ }
32
+ }
33
+ /**
34
+ * Atomically appends to a file (if supported by OS) or simulates it.
35
+ * Note: Real atomic append on POSIX is a single write() call with O_APPEND.
36
+ * For simplicity and robustness across platforms, we use a simple append here
37
+ * as the risk of corruption is lower than a full rewrite, but for logs
38
+ * it's acceptable.
39
+ */
40
+ export function appendFileSafe(filePath, content) {
41
+ const dir = path.dirname(filePath);
42
+ ensureDir(dir);
43
+ fs.appendFileSync(filePath, content, "utf8");
44
+ }
@@ -0,0 +1,56 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { resolveFrameworkDir } from "./security.js";
4
+ export const Metrics = {
5
+ /**
6
+ * Estimates tokens based on character count (rough heuristic: 1 token ~= 4 chars).
7
+ */
8
+ estimateTokens: (text) => {
9
+ return Math.ceil(text.length / 4);
10
+ },
11
+ /**
12
+ * Logs the token usage and action to the observability metrics file.
13
+ */
14
+ logUsage: (projectRoot, agent, action, tokens) => {
15
+ Metrics.saveMetric(projectRoot, {
16
+ timestamp: new Date().toISOString(),
17
+ agent,
18
+ action,
19
+ estimatedTokens: tokens
20
+ });
21
+ },
22
+ /**
23
+ * Logs an error occurrence to the observability metrics file.
24
+ */
25
+ logError: (projectRoot, agent, action, error) => {
26
+ Metrics.saveMetric(projectRoot, {
27
+ timestamp: new Date().toISOString(),
28
+ agent,
29
+ action: `ERROR: ${action}`,
30
+ estimatedTokens: 0,
31
+ error
32
+ });
33
+ },
34
+ /**
35
+ * Internal helper to save metric entries.
36
+ */
37
+ saveMetric: (projectRoot, entry) => {
38
+ const frameworkDir = resolveFrameworkDir(projectRoot);
39
+ const metricsPath = path.join(projectRoot, frameworkDir, "observability/metrics.json");
40
+ try {
41
+ const metricsDir = path.dirname(metricsPath);
42
+ if (!fs.existsSync(metricsDir))
43
+ fs.mkdirSync(metricsDir, { recursive: true });
44
+ let currentMetrics = [];
45
+ if (fs.existsSync(metricsPath)) {
46
+ currentMetrics = JSON.parse(fs.readFileSync(metricsPath, "utf8"));
47
+ }
48
+ currentMetrics.push(entry);
49
+ // Keep only last 100 entries to save space
50
+ if (currentMetrics.length > 100)
51
+ currentMetrics.shift();
52
+ fs.writeFileSync(metricsPath, JSON.stringify(currentMetrics, null, 2));
53
+ }
54
+ catch { /* ignore: metrics should not block the main process */ }
55
+ }
56
+ };
@@ -0,0 +1,60 @@
1
+ import path from "path";
2
+ import fs from "fs";
3
+ import { FRAMEWORK, MCP, UNIFIED_HUB_DIR } from "../constants.js"; // New import
4
+ import os from "os"; // Need os.homedir()
5
+ /**
6
+ * Validates and resolves a user-provided path to prevent path traversal attacks.
7
+ * Ensures the resolved path stays within the project root boundary.
8
+ */
9
+ export function safePath(projectRoot, userPath) {
10
+ const resolved = path.resolve(projectRoot, userPath);
11
+ const normalizedRoot = path.resolve(projectRoot);
12
+ if (!resolved.startsWith(normalizedRoot + path.sep) && resolved !== normalizedRoot) {
13
+ throw new Error(`Access denied: path "${userPath}" escapes project root.`);
14
+ }
15
+ return resolved;
16
+ }
17
+ /**
18
+ * Resolves the active framework directory.
19
+ * Priority: ENDERUN_TEST_DIR (env) -> package.json `enderun.frameworkDir` -> `.enderun` -> other adapter dirs -> global HOME.
20
+ */
21
+ export function resolveFrameworkDir(projectRoot) {
22
+ // For test environments, use the explicitly set test directory.
23
+ const testDir = process.env[MCP.TEST_DIR_ENV];
24
+ if (testDir)
25
+ return testDir;
26
+ // 1. Authoritative source: read from package.json if present
27
+ try {
28
+ const pkgPath = path.join(projectRoot, "package.json");
29
+ if (fs.existsSync(pkgPath)) {
30
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
31
+ const enderunConfig = pkg["enderun"];
32
+ if (enderunConfig && typeof enderunConfig["frameworkDir"] === "string") {
33
+ // Ensure the path is relative if it's within the project, otherwise use as-is.
34
+ const resolvedDir = path.resolve(projectRoot, enderunConfig["frameworkDir"]);
35
+ if (resolvedDir.startsWith(path.resolve(projectRoot))) {
36
+ return path.relative(projectRoot, resolvedDir);
37
+ }
38
+ return enderunConfig["frameworkDir"];
39
+ }
40
+ }
41
+ }
42
+ catch {
43
+ // ignore — fall through to filesystem scan
44
+ }
45
+ // 2. Filesystem scan in projectRoot for common framework directories
46
+ const localCandidates = [
47
+ FRAMEWORK.CORE_DIR, // .enderun
48
+ UNIFIED_HUB_DIR, // .agents
49
+ // Add other adapter specific directories if needed, or remove if unified is strictly enforced
50
+ ];
51
+ for (const candidate of localCandidates) {
52
+ const candidatePath = path.join(projectRoot, candidate);
53
+ if (fs.existsSync(candidatePath)) {
54
+ return candidate;
55
+ }
56
+ }
57
+ // 3. Fallback to global home directory.
58
+ const homeDir = os.homedir();
59
+ return path.join(homeDir, FRAMEWORK.CORE_DIR);
60
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "@agent-enderun/mcp",
3
+ "version": "1.10.5",
4
+ "description": "Agent Enderun Model Context Protocol (MCP) Server",
5
+ "type": "module",
6
+ "scripts": {
7
+ "build": "npx tsc",
8
+ "start": "node dist/index.js",
9
+ "dev": "tsx src/index.ts"
10
+ },
11
+ "dependencies": {
12
+ "@modelcontextprotocol/sdk": "^1.26.0",
13
+ "zod": "^3.24.2"
14
+ },
15
+ "devDependencies": {
16
+ "@types/node": "^25.9.1",
17
+ "typescript": "^5.0.0"
18
+ }
19
+ }
@@ -0,0 +1,78 @@
1
+ import path from "path";
2
+
3
+ /**
4
+ * Agent Enderun — Single Source of Truth for framework constants.
5
+ * Import from here instead of hardcoding paths, phases, or directory names.
6
+ */
7
+
8
+ // ─── Framework identity ───────────────────────────────────────────────────
9
+
10
+ export const FRAMEWORK = {
11
+ NAME: "Agent Enderun",
12
+ CORE_DIR: ".enderun",
13
+ // This is the hub for unified adapter layouts (e.g. .agents/gemini, .agents/claude)
14
+ UNIFIED_HUB_DIR: ".agents",
15
+ // This is the default directory to scaffold new apps into
16
+ APPS_DIR: "apps",
17
+ // This is where all skills are stored
18
+ SKILLS_DIR: "skills",
19
+ };
20
+
21
+ export const FRAMEWORK_SUBDIRS = {
22
+ AGENTS: "agents",
23
+ SKILLS: "skills",
24
+ KNOWLEDGE: "knowledge",
25
+ MESSAGES: "messages",
26
+ MEMORY: "memory",
27
+ MEMORY_GRAPH: "memory-graph",
28
+ LOGS: "logs",
29
+ CONFIG: "config",
30
+ };
31
+
32
+ export const ROOT_CONFIG_FILES = {
33
+ MCP: "mcp.json",
34
+ NATIVE_MODULES: "native-modules.json",
35
+ TSCONFIG: "tsconfig.json",
36
+ ESLINT: "eslint.config.js",
37
+ };
38
+
39
+ export const MCP = {
40
+ // Environment variable used by MCP to identify project root
41
+ PROJECT_ROOT_ENV: "ENDERUN_PROJECT_ROOT",
42
+ // Environment variable for test mode
43
+ TEST_DIR_ENV: "ENDERUN_TEST_DIR",
44
+ };
45
+
46
+ export const MEMORY_FILES = {
47
+ STATE: "state.json",
48
+ SHARED_FACTS: "shared_facts.json",
49
+ };
50
+
51
+ export const NATIVE_AGENT_PATHS = {
52
+ gemini: ".gemini/agents",
53
+ claude: ".claude/agents",
54
+ cursor: ".cursor/rules",
55
+ codex: ".agents/instructions",
56
+ grok: ".grok",
57
+ "antigravity-cli": ".antigravity/agents",
58
+ };
59
+
60
+ // ─── Backward-compatible aliases ──────────────────────────────────────────
61
+
62
+ export const CORE_FRAMEWORK_DIR = FRAMEWORK.CORE_DIR;
63
+ export const UNIFIED_HUB_DIR = FRAMEWORK.UNIFIED_HUB_DIR;
64
+ export const SKILLS_HUB_PATH = pathJoin(UNIFIED_HUB_DIR, FRAMEWORK_SUBDIRS.SKILLS);
65
+
66
+ // ─── Path Helpers ─────────────────────────────────────────────────────────
67
+
68
+ function pathJoin(...args: string[]): string {
69
+ return path.join(...args);
70
+ }
71
+
72
+ function corePath(subdir: string, filename: string): string {
73
+ return pathJoin(FRAMEWORK.CORE_DIR, subdir, filename);
74
+ }
75
+
76
+ export function knowledgePath(filename: string): string {
77
+ return corePath(FRAMEWORK_SUBDIRS.KNOWLEDGE, filename);
78
+ }
@@ -0,0 +1,17 @@
1
+ declare module "@modelcontextprotocol/sdk/server/index.js" {
2
+ export class Server {
3
+ constructor(info: { name: string; version: string }, options: { capabilities: { tools: Record<string, never> } });
4
+ setRequestHandler(schema: unknown, handler: (request: unknown) => Promise<unknown>): void;
5
+ connect(transport: unknown): Promise<void>;
6
+ close(): Promise<void>;
7
+ }
8
+ }
9
+
10
+ declare module "@modelcontextprotocol/sdk/server/stdio.js" {
11
+ export class StdioServerTransport {}
12
+ }
13
+
14
+ declare module "@modelcontextprotocol/sdk/types.js" {
15
+ export const ListToolsRequestSchema: unknown;
16
+ export const CallToolRequestSchema: unknown;
17
+ }
@@ -0,0 +1,132 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { fileURLToPath } from "url";
4
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
5
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
+ import {
7
+ CallToolRequestSchema,
8
+ ListToolsRequestSchema,
9
+ } from "@modelcontextprotocol/sdk/types.js";
10
+
11
+
12
+ import { TOOLS, toolHandlers } from "./tools/index.js";
13
+
14
+
15
+ // ─── Server Setup ─────────────────────────────────────────────────
16
+
17
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
18
+
19
+ // Robustly find package.json by walking up from __dirname
20
+ function findPackageJson(startDir: string): string {
21
+ let currentDir = startDir;
22
+ while (currentDir !== path.parse(currentDir).root) {
23
+ const pkgPath = path.join(currentDir, "package.json");
24
+ if (fs.existsSync(pkgPath)) return pkgPath;
25
+ currentDir = path.dirname(currentDir);
26
+ }
27
+ throw new Error("Could not find package.json for @agent-enderun/mcp");
28
+ }
29
+
30
+ const pkgPath = findPackageJson(__dirname);
31
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
32
+ const serverVersion = pkg.version;
33
+
34
+ const server = new Server(
35
+ {
36
+ name: "@agent-enderun/mcp",
37
+ version: serverVersion,
38
+ },
39
+ {
40
+ capabilities: {
41
+ tools: {},
42
+ },
43
+ }
44
+ );
45
+
46
+ // Basic Schema Validator for Required Fields
47
+ function validateArgs(toolName: string, args: Record<string, unknown>): string | null {
48
+ const definition = TOOLS.find(t => t.name === toolName);
49
+ if (!definition) return `Unknown tool: ${toolName}`;
50
+
51
+ const required = definition.inputSchema.required || [];
52
+ for (const field of required) {
53
+ if (args[field] === undefined || args[field] === null || args[field] === "") {
54
+ return `Missing required argument: '${field}' for tool '${toolName}'`;
55
+ }
56
+ }
57
+ return null;
58
+ }
59
+
60
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
61
+ return { tools: TOOLS };
62
+ });
63
+
64
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
65
+ const req = request as { params: { name: string, arguments?: Record<string, unknown> } };
66
+ const { name, arguments: args } = req.params;
67
+ const projectRoot = process.env.ENDERUN_PROJECT_ROOT || process.cwd();
68
+
69
+ try {
70
+ const handler = toolHandlers[name];
71
+ if (!handler) {
72
+ return {
73
+ isError: true,
74
+ content: [{ type: "text" as const, text: `❌ Unknown tool: ${name}` }],
75
+ };
76
+ }
77
+
78
+ // 🛡️ Runtime Validation
79
+ const validationError = validateArgs(name, args || {});
80
+ if (validationError) {
81
+ return {
82
+ isError: true,
83
+ content: [{ type: "text" as const, text: `❌ Validation Error: ${validationError}` }],
84
+ };
85
+ }
86
+
87
+ return await handler(projectRoot, args || {});
88
+ } catch (error: unknown) {
89
+ const message = error instanceof Error ? error.message : "Unknown error occurred";
90
+ return {
91
+ isError: true,
92
+ content: [{ type: "text" as const, text: `❌ Execution failed: ${message}` }],
93
+ };
94
+ }
95
+ });
96
+
97
+ // ─── Graceful Startup & Shutdown ──────────────────────────────────
98
+
99
+ async function run() {
100
+ const transport = new StdioServerTransport();
101
+
102
+ // Prevent unhandled errors from crashing the MCP stream
103
+ process.on("uncaughtException", (error: Error) => {
104
+ process.stderr.write(`[agent-enderun-mcp] Uncaught exception: ${error.message}
105
+ `);
106
+ });
107
+ process.on("unhandledRejection", (reason: unknown) => {
108
+ const message = reason instanceof Error ? reason.message : String(reason);
109
+ process.stderr.write(`[agent-enderun-mcp] Unhandled rejection: ${message}
110
+ `);
111
+ });
112
+
113
+ // Graceful shutdown on SIGINT/SIGTERM
114
+ const shutdown = async () => {
115
+ try {
116
+ await server.close();
117
+ } catch {
118
+ // Already closed or failed — safe to ignore
119
+ }
120
+ process.exit(0);
121
+ };
122
+ process.on("SIGINT", shutdown);
123
+ process.on("SIGTERM", shutdown);
124
+
125
+ await server.connect(transport);
126
+ }
127
+
128
+ run().catch((error: Error) => {
129
+ process.stderr.write(`[agent-enderun-mcp] Fatal startup error: ${error.message}
130
+ `);
131
+ process.exit(1);
132
+ });
@@ -0,0 +1,71 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { ToolResult, AcquireLockArgs, ReleaseLockArgs } from "../types.js";
4
+ import { resolveFrameworkDir } from "../../utils/security.js";
5
+
6
+ /**
7
+ * Handles acquiring a stateful lock on a resource.
8
+ */
9
+ export async function handleAcquireLock(projectRoot: string, args: AcquireLockArgs): Promise<ToolResult> {
10
+ const { resource, agent, ttl = 60 } = args;
11
+ const frameworkDir = resolveFrameworkDir(projectRoot);
12
+ const lockDir = path.join(projectRoot, frameworkDir, "locks");
13
+ const lockPath = path.join(lockDir, `${resource}.lock`);
14
+
15
+ try {
16
+ if (!fs.existsSync(lockDir)) fs.mkdirSync(lockDir, { recursive: true });
17
+
18
+ if (fs.existsSync(lockPath)) {
19
+ const stat = fs.statSync(lockPath);
20
+ const now = new Date().getTime();
21
+ const age = (now - stat.mtimeMs) / 1000;
22
+
23
+ if (age < ttl) {
24
+ return {
25
+ isError: true,
26
+ content: [{ type: "text", text: `Resource '${resource}' is currently locked by another agent.` }]
27
+ };
28
+ }
29
+ // Lock expired, safe to override
30
+ fs.unlinkSync(lockPath);
31
+ }
32
+
33
+ fs.writeFileSync(lockPath, JSON.stringify({ agent, timestamp: new Date().toISOString() }));
34
+ return {
35
+ content: [{ type: "text", text: `✅ Lock acquired for resource '${resource}' by ${agent}.` }]
36
+ };
37
+ } catch (e) {
38
+ return {
39
+ isError: true,
40
+ content: [{ type: "text", text: `Failed to acquire lock: ${String(e)}` }]
41
+ };
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Handles releasing a lock.
47
+ */
48
+ export async function handleReleaseLock(projectRoot: string, args: ReleaseLockArgs): Promise<ToolResult> {
49
+ const { resource, agent } = args;
50
+ const frameworkDir = resolveFrameworkDir(projectRoot);
51
+ const lockPath = path.join(projectRoot, frameworkDir, "locks", `${resource}.lock`);
52
+
53
+ try {
54
+ if (!fs.existsSync(lockPath)) {
55
+ return { content: [{ type: "text", text: `ℹ️ No lock found for resource '${resource}'.` }] };
56
+ }
57
+
58
+ const lockData = JSON.parse(fs.readFileSync(lockPath, "utf8"));
59
+ if (lockData.agent !== agent) {
60
+ return {
61
+ isError: true,
62
+ content: [{ type: "text", text: `❌ Denied: You do not own the lock for '${resource}'. Owned by ${lockData.agent}.` }]
63
+ };
64
+ }
65
+
66
+ fs.unlinkSync(lockPath);
67
+ return { content: [{ type: "text", text: `✅ Lock released for resource '${resource}' by ${agent}.` }] };
68
+ } catch (e) {
69
+ return { isError: true, content: [{ type: "text", text: `Failed to release lock: ${String(e)}` }] };
70
+ }
71
+ }
@@ -0,0 +1,38 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { ToolResult, RegisterAgentArgs } from "../types.js";
4
+ import { resolveFrameworkDir } from "../../utils/security.js";
5
+
6
+ /**
7
+ * Handles agent registration with the Control Plane.
8
+ * This can be used to validate permissions and active status.
9
+ */
10
+ export async function handleRegisterAgent(projectRoot: string, args: RegisterAgentArgs): Promise<ToolResult> {
11
+ const { agent, role, capability = 5 } = args;
12
+ const frameworkDir = resolveFrameworkDir(projectRoot);
13
+ const registryDir = path.join(projectRoot, frameworkDir, "registry");
14
+ const agentFile = path.join(registryDir, `${agent.replace("@", "")}_active.json`);
15
+
16
+ try {
17
+ if (!fs.existsSync(registryDir)) fs.mkdirSync(registryDir, { recursive: true });
18
+
19
+ const agentData = {
20
+ agent,
21
+ role,
22
+ capability,
23
+ last_seen: new Date().toISOString(),
24
+ status: "ACTIVE"
25
+ };
26
+
27
+ fs.writeFileSync(agentFile, JSON.stringify(agentData, null, 2));
28
+
29
+ return {
30
+ content: [{ type: "text", text: `🎖️ Agent ${agent} (${role}) registered successfully in the Enderun Control Plane.` }]
31
+ };
32
+ } catch (e) {
33
+ return {
34
+ isError: true,
35
+ content: [{ type: "text", text: `Failed to register agent: ${String(e)}` }]
36
+ };
37
+ }
38
+ }
@@ -0,0 +1,33 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { exec } from "child_process";
4
+ import { StartDashboardArgs, ToolResult } from "../types.js";
5
+
6
+ /**
7
+ * Starts the Enderun Web Dashboard server.
8
+ */
9
+ export function handleStartDashboard(projectRoot: string, args: StartDashboardArgs): ToolResult {
10
+ const port = args.port || 5858;
11
+ const dashboardScript = path.join(projectRoot, "bin/enderun-dashboard.js");
12
+
13
+ if (!fs.existsSync(dashboardScript)) {
14
+ throw new Error("Dashboard server script not found in bin/enderun-dashboard.js");
15
+ }
16
+
17
+ try {
18
+ // Start the dashboard as a background process
19
+ exec(`node "${dashboardScript}"`, {
20
+ cwd: projectRoot,
21
+ env: { ...process.env, PORT: String(port) }
22
+ });
23
+
24
+ return {
25
+ content: [{
26
+ type: "text",
27
+ text: `🖥️ **Enderun Web Dashboard started!**\n\nAccess it at: http://localhost:${port}\n\nThis interface will show real-time agent status, system health, and orchestration logs.`
28
+ }]
29
+ };
30
+ } catch (e) {
31
+ return { isError: true, content: [{ type: "text", text: `Failed to start dashboard: ${String(e)}` }] };
32
+ }
33
+ }