atabey-mcp 0.0.4

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 (73) hide show
  1. package/dist/constants.js +64 -0
  2. package/dist/index.js +119 -0
  3. package/dist/tools/control_plane/locking.js +82 -0
  4. package/dist/tools/control_plane/registry.js +34 -0
  5. package/dist/tools/definitions.js +290 -0
  6. package/dist/tools/file_system/batch_surgical_edit.js +59 -0
  7. package/dist/tools/file_system/patch_file.js +29 -0
  8. package/dist/tools/file_system/read_file.js +51 -0
  9. package/dist/tools/file_system/replace_text.js +45 -0
  10. package/dist/tools/file_system/write_file.js +38 -0
  11. package/dist/tools/framework/audit_deps.js +41 -0
  12. package/dist/tools/framework/get_status.js +5 -0
  13. package/dist/tools/framework/orchestrate.js +5 -0
  14. package/dist/tools/framework/run_tests.js +27 -0
  15. package/dist/tools/framework/update_contract_hash.js +5 -0
  16. package/dist/tools/framework/update_memory.js +8 -0
  17. package/dist/tools/index.js +60 -0
  18. package/dist/tools/memory/get_insights.js +34 -0
  19. package/dist/tools/memory/read_memory.js +28 -0
  20. package/dist/tools/messaging/log_action.js +22 -0
  21. package/dist/tools/messaging/send_message.js +94 -0
  22. package/dist/tools/observability/check_ports.js +26 -0
  23. package/dist/tools/observability/get_health.js +19 -0
  24. package/dist/tools/quality/check_lint.js +30 -0
  25. package/dist/tools/search/get_gaps.js +48 -0
  26. package/dist/tools/search/get_map.js +43 -0
  27. package/dist/tools/search/grep_search.js +75 -0
  28. package/dist/tools/search/list_dir.js +28 -0
  29. package/dist/tools/shell/run_command.js +56 -0
  30. package/dist/tools/types.js +1 -0
  31. package/dist/utils/cli.js +59 -0
  32. package/dist/utils/compliance.js +78 -0
  33. package/dist/utils/fs.js +44 -0
  34. package/dist/utils/metrics.js +56 -0
  35. package/dist/utils/security.js +60 -0
  36. package/package.json +26 -0
  37. package/src/constants.ts +78 -0
  38. package/src/declarations.d.ts +17 -0
  39. package/src/index.ts +144 -0
  40. package/src/tools/control_plane/locking.ts +89 -0
  41. package/src/tools/control_plane/registry.ts +38 -0
  42. package/src/tools/definitions.ts +292 -0
  43. package/src/tools/file_system/batch_surgical_edit.ts +79 -0
  44. package/src/tools/file_system/patch_file.ts +39 -0
  45. package/src/tools/file_system/read_file.ts +58 -0
  46. package/src/tools/file_system/replace_text.ts +54 -0
  47. package/src/tools/file_system/write_file.ts +45 -0
  48. package/src/tools/framework/audit_deps.ts +49 -0
  49. package/src/tools/framework/get_status.ts +7 -0
  50. package/src/tools/framework/orchestrate.ts +7 -0
  51. package/src/tools/framework/run_tests.ts +30 -0
  52. package/src/tools/framework/update_contract_hash.ts +7 -0
  53. package/src/tools/framework/update_memory.ts +10 -0
  54. package/src/tools/index.ts +64 -0
  55. package/src/tools/memory/get_insights.ts +41 -0
  56. package/src/tools/memory/read_memory.ts +31 -0
  57. package/src/tools/messaging/log_action.ts +28 -0
  58. package/src/tools/messaging/send_message.ts +97 -0
  59. package/src/tools/observability/check_ports.ts +30 -0
  60. package/src/tools/observability/get_health.ts +24 -0
  61. package/src/tools/quality/check_lint.ts +36 -0
  62. package/src/tools/search/get_gaps.ts +54 -0
  63. package/src/tools/search/get_map.ts +48 -0
  64. package/src/tools/search/grep_search.ts +75 -0
  65. package/src/tools/search/list_dir.ts +34 -0
  66. package/src/tools/shell/run_command.ts +66 -0
  67. package/src/tools/types.ts +89 -0
  68. package/src/utils/cli.ts +53 -0
  69. package/src/utils/compliance.ts +95 -0
  70. package/src/utils/fs.ts +45 -0
  71. package/src/utils/metrics.ts +73 -0
  72. package/src/utils/security.ts +66 -0
  73. package/tsconfig.json +14 -0
@@ -0,0 +1,43 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ /**
4
+ * Generates a tree-view map of the project structure.
5
+ * Helps agents visualize the entire project layout quickly.
6
+ */
7
+ export function handleGetProjectMap(projectRoot, args) {
8
+ const maxDepth = args.maxDepth || 3;
9
+ const includeFiles = args.includeFiles !== false;
10
+ const buildTree = (dir, depth) => {
11
+ if (depth > maxDepth)
12
+ return [];
13
+ const results = [];
14
+ const files = fs.readdirSync(dir);
15
+ files.forEach(file => {
16
+ if (file === "node_modules" || file === ".git" || file === "dist" || file.startsWith("."))
17
+ return;
18
+ const fullPath = path.join(dir, file);
19
+ const stat = fs.statSync(fullPath);
20
+ const indent = " ".repeat(depth);
21
+ if (stat.isDirectory()) {
22
+ results.push(`${indent}📁 ${file}/`);
23
+ results.push(...buildTree(fullPath, depth + 1));
24
+ }
25
+ else if (includeFiles) {
26
+ results.push(`${indent}📄 ${file}`);
27
+ }
28
+ });
29
+ return results;
30
+ };
31
+ try {
32
+ const tree = buildTree(projectRoot, 0);
33
+ return {
34
+ content: [{
35
+ type: "text",
36
+ text: `🗺️ **Project Map (Depth: ${maxDepth})**\n\n${tree.join("\n")}`
37
+ }]
38
+ };
39
+ }
40
+ catch (e) {
41
+ return { isError: true, content: [{ type: "text", text: `Failed to map project: ${String(e)}` }] };
42
+ }
43
+ }
@@ -0,0 +1,75 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { Metrics } from "../../utils/metrics.js";
4
+ /**
5
+ * Searches for a regex pattern within files in the project.
6
+ */
7
+ export function handleGrepSearch(projectRoot, args) {
8
+ const pattern = args.pattern;
9
+ const includePattern = args.includePattern || ""; // e.g., ".ts"
10
+ const excludePattern = args.excludePattern || "node_modules";
11
+ if (!pattern) {
12
+ const err = "Search pattern is required.";
13
+ Metrics.logError(projectRoot, "@mcp", "grep_search", err);
14
+ return { isError: true, content: [{ type: "text", text: `❌ ${err}` }] };
15
+ }
16
+ const results = [];
17
+ try {
18
+ new RegExp(pattern);
19
+ }
20
+ catch (e) {
21
+ const err = `Invalid regex pattern: ${String(e)}`;
22
+ Metrics.logError(projectRoot, "@mcp", "grep_search", err);
23
+ return { isError: true, content: [{ type: "text", text: `❌ ${err}` }] };
24
+ }
25
+ const walk = (dir) => {
26
+ if (results.length > 100)
27
+ return;
28
+ try {
29
+ const files = fs.readdirSync(dir);
30
+ for (const file of files) {
31
+ if (results.length > 100)
32
+ return;
33
+ const filePath = path.join(dir, file);
34
+ if (excludePattern && filePath.includes(excludePattern)) {
35
+ continue;
36
+ }
37
+ const stat = fs.statSync(filePath);
38
+ if (stat.isDirectory()) {
39
+ walk(filePath);
40
+ }
41
+ else if (stat.isFile()) {
42
+ if (includePattern && !filePath.endsWith(includePattern)) {
43
+ continue;
44
+ }
45
+ const content = fs.readFileSync(filePath, "utf8");
46
+ // Create a new regex object for each line to avoid state issues with /g
47
+ if (new RegExp(pattern).test(content)) {
48
+ if (results.length < 100) {
49
+ results.push(filePath);
50
+ }
51
+ }
52
+ }
53
+ }
54
+ }
55
+ catch {
56
+ // Ignore directories that cannot be read
57
+ }
58
+ };
59
+ try {
60
+ walk(projectRoot);
61
+ }
62
+ catch (e) {
63
+ const err = `Search failed: ${String(e)}`;
64
+ Metrics.logError(projectRoot, "@mcp", "grep_search", err);
65
+ return { isError: true, content: [{ type: "text", text: `❌ ${err}` }] };
66
+ }
67
+ return {
68
+ content: [{
69
+ type: "text",
70
+ text: results.length > 0
71
+ ? `Found ${results.length} matches:\n\n${results.join("\n")}`
72
+ : "No matches found."
73
+ }]
74
+ };
75
+ }
@@ -0,0 +1,28 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { safePath } from "../../utils/security.js";
4
+ /**
5
+ * Lists the contents of a directory.
6
+ */
7
+ export function handleListDir(projectRoot, args) {
8
+ const dirPath = safePath(projectRoot, args.path || ".");
9
+ if (!fs.existsSync(dirPath)) {
10
+ throw new Error(`Directory not found: ${args.path}`);
11
+ }
12
+ const stats = fs.statSync(dirPath);
13
+ if (!stats.isDirectory()) {
14
+ throw new Error(`Path is not a directory: ${args.path}`);
15
+ }
16
+ const files = fs.readdirSync(dirPath);
17
+ const results = files.map(file => {
18
+ const fullPath = path.join(dirPath, file);
19
+ const isDir = fs.statSync(fullPath).isDirectory();
20
+ return `${isDir ? "[DIR] " : " "}${file}`;
21
+ });
22
+ return {
23
+ content: [{
24
+ type: "text",
25
+ text: `Directory listing for ${args.path || "."}:\n\n${results.join("\n")}`
26
+ }]
27
+ };
28
+ }
@@ -0,0 +1,56 @@
1
+ import { exec } from "child_process";
2
+ import { Metrics } from "../../utils/metrics.js";
3
+ const COMMAND_ALLOW_LIST = [
4
+ "npm test",
5
+ "npm run lint",
6
+ "npm run build",
7
+ "git status",
8
+ "git diff",
9
+ "npx vitest run",
10
+ "go test",
11
+ "go fmt",
12
+ "go build",
13
+ "pytest",
14
+ "ruff check",
15
+ "dotnet test",
16
+ "dotnet format",
17
+ "dotnet build",
18
+ "./gradlew",
19
+ "mvn",
20
+ ];
21
+ const TIMEOUT = 30000; // 30 seconds
22
+ export function handleRunCommand(projectRoot, args) {
23
+ const command = args.command;
24
+ const isAllowed = COMMAND_ALLOW_LIST.some(allowedCmd => command.startsWith(allowedCmd));
25
+ if (!isAllowed) {
26
+ const errorMsg = `Command not allowed: "${command}". Only commands starting with the following are allowed: ${COMMAND_ALLOW_LIST.join(", ")}`;
27
+ Metrics.logError(projectRoot, "@mcp", `run_shell_command: ${command} (denied)`, errorMsg);
28
+ return Promise.resolve({
29
+ content: [{ type: "text", text: `ERROR: ${errorMsg}` }],
30
+ isError: true,
31
+ });
32
+ }
33
+ return new Promise((resolve) => {
34
+ exec(command, { cwd: projectRoot, timeout: TIMEOUT }, (error, stdout, stderr) => {
35
+ const output = stdout + stderr;
36
+ const tokens = Metrics.estimateTokens(output);
37
+ Metrics.logUsage(projectRoot, "@mcp", `run_shell_command: ${command}`, tokens);
38
+ if (error) {
39
+ const errorMsg = `Command failed with exit code ${error.code}: ${error.message}.`;
40
+ Metrics.logError(projectRoot, "@mcp", `run_shell_command: ${command}`, errorMsg);
41
+ resolve({
42
+ content: [{ type: "text", text: `ERROR: ${errorMsg}. Output: ${output}` }],
43
+ isError: true,
44
+ });
45
+ return;
46
+ }
47
+ // Truncate long outputs
48
+ const MAX_OUTPUT_LENGTH = 5000;
49
+ let truncatedOutput = output;
50
+ if (output.length > MAX_OUTPUT_LENGTH) {
51
+ truncatedOutput = output.substring(0, MAX_OUTPUT_LENGTH) + "... [TRUNCATED] ..."; // Simplified
52
+ }
53
+ resolve({ content: [{ type: "text", text: truncatedOutput }] });
54
+ });
55
+ });
56
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,59 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { execFileSync } from "child_process";
4
+ /**
5
+ * Executes a command safely and returns the output.
6
+ */
7
+ export function safeExec(cmd, args, cwd, timeout = 30000) {
8
+ try {
9
+ return execFileSync(cmd, args, { cwd, timeout, encoding: "utf8", stdio: "pipe" });
10
+ }
11
+ catch (err) {
12
+ const error = err;
13
+ return error.stdout?.toString() || error.stderr?.toString() || error.message || String(err);
14
+ }
15
+ }
16
+ /**
17
+ * Detects the backend language from the framework configuration.
18
+ */
19
+ export function getBackendLanguage(projectRoot) {
20
+ try {
21
+ const configPath = path.join(projectRoot, ".atabey", "config.json");
22
+ if (fs.existsSync(configPath)) {
23
+ const config = JSON.parse(fs.readFileSync(configPath, "utf8"));
24
+ return config.backendLanguage || "Node.js (TypeScript)";
25
+ }
26
+ }
27
+ catch {
28
+ // Fallback to default
29
+ }
30
+ return "Node.js (TypeScript)";
31
+ }
32
+ /**
33
+ * Returns the default lint command for the given language.
34
+ */
35
+ export function getDefaultLintCommand(language) {
36
+ if (language.includes("Go"))
37
+ return "go fmt ./...";
38
+ if (language.includes("Java"))
39
+ return "./gradlew check"; // or mvn check
40
+ if (language.includes("Python"))
41
+ return "ruff check .";
42
+ if (language.includes(".NET"))
43
+ return "dotnet format";
44
+ return "npm run lint";
45
+ }
46
+ /**
47
+ * Returns the default test command for the given language.
48
+ */
49
+ export function getDefaultTestCommand(language) {
50
+ if (language.includes("Go"))
51
+ return "go test ./...";
52
+ if (language.includes("Java"))
53
+ return "./gradlew test"; // or mvn test
54
+ if (language.includes("Python"))
55
+ return "pytest";
56
+ if (language.includes(".NET"))
57
+ return "dotnet test";
58
+ return "npm test";
59
+ }
@@ -0,0 +1,78 @@
1
+ import ts from "typescript";
2
+ /**
3
+ * Enterprise Compliance Guardrail
4
+ * Checks content against corporate standards using AST analysis before allowing file mutations.
5
+ */
6
+ export function verifyCorporateCompliance(content, filePath) {
7
+ // Skip compliance checks for non-source files or specific ignored files
8
+ if (filePath.endsWith(".json") || filePath.endsWith(".md") || filePath.endsWith(".env.example")) {
9
+ return;
10
+ }
11
+ const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);
12
+ const errors = [];
13
+ /**
14
+ * Recursive AST Visitor
15
+ */
16
+ function visit(node) {
17
+ // 1. Zero Console Policy
18
+ if (ts.isPropertyAccessExpression(node)) {
19
+ const expression = node.expression;
20
+ const name = node.name.text;
21
+ if (ts.isIdentifier(expression) && expression.text === "console") {
22
+ if (["log", "warn", "error"].includes(name)) {
23
+ // Check if file is exempt
24
+ if (!filePath.includes("logger.ts") && !filePath.includes("check.ts") && !filePath.includes("cli.ts")) {
25
+ errors.push(`❌ Corporate Compliance Breach: 'console.${name}' usage is forbidden at line ${sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1}.`);
26
+ }
27
+ }
28
+ }
29
+ }
30
+ // 2. No Explicit Any Policy
31
+ if (ts.isTypeReferenceNode(node)) {
32
+ if (ts.isIdentifier(node.typeName) && node.typeName.text === "any") {
33
+ if (!filePath.includes("definitions.ts") && !filePath.includes("types.ts")) {
34
+ errors.push(`❌ Corporate Compliance Breach: 'any' type is forbidden at line ${sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1}.`);
35
+ }
36
+ }
37
+ }
38
+ // 3. Zero UI Library Policy (No @chakra-ui, mui, @shadcn)
39
+ if (ts.isImportDeclaration(node)) {
40
+ const moduleSpecifier = node.moduleSpecifier;
41
+ if (ts.isStringLiteral(moduleSpecifier)) {
42
+ const forbiddenLibs = ["@chakra-ui", "mui", "@shadcn", "antd", "bootstrap"];
43
+ const lib = forbiddenLibs.find(l => moduleSpecifier.text.includes(l));
44
+ if (lib) {
45
+ errors.push(`❌ Corporate Compliance Breach: External UI library '${lib}' usage is FORBIDDEN at line ${sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1}. Build atomic components manually instead.`);
46
+ }
47
+ }
48
+ }
49
+ // Handle 'any' as a keyword type (e.g., parameter: any)
50
+ if (node.kind === ts.SyntaxKind.AnyKeyword) {
51
+ if (!filePath.includes("definitions.ts") && !filePath.includes("types.ts")) {
52
+ errors.push(`❌ Corporate Compliance Breach: 'any' keyword is forbidden at line ${sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1}.`);
53
+ }
54
+ }
55
+ ts.forEachChild(node, visit);
56
+ }
57
+ visit(sourceFile);
58
+ // 3. Hardcoded Secrets & PII Guard
59
+ const piiKeywords = [
60
+ { regex: /API_KEY\s*=\s*['"][^'"]+['"]/i, msg: "Hardcoded API Key" },
61
+ { regex: /SECRET\s*=\s*['"][^'"]+['"]/i, msg: "Hardcoded Secret" },
62
+ { regex: /PASSWORD\s*=\s*['"][^'"]+['"]/i, msg: "Hardcoded Password" },
63
+ { regex: /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/, msg: "PII Detected: Email Address" },
64
+ { regex: /\b\d{4}[- ]?\d{4}[- ]?\d{4}[- ]?\d{4}\b/, msg: "PII Detected: Credit Card Pattern" }
65
+ ];
66
+ for (const { regex, msg } of piiKeywords) {
67
+ if (regex.test(content)) {
68
+ // Allow emails in specific files like README or package.json
69
+ if (msg.includes("Email") && (filePath.endsWith("README.md") || filePath.endsWith("package.json") || filePath.includes("CONTRIBUTING"))) {
70
+ continue;
71
+ }
72
+ errors.push(`❌ Corporate Compliance Breach: ${msg} detected.`);
73
+ }
74
+ }
75
+ if (errors.length > 0) {
76
+ throw new Error(errors.join("\n"));
77
+ }
78
+ }
@@ -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: ATABEY_TEST_DIR (env) -> package.json `atabey.frameworkDir` -> `.atabey` -> 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 atabeyConfig = pkg["atabey"];
32
+ if (atabeyConfig && typeof atabeyConfig["frameworkDir"] === "string") {
33
+ // Ensure the path is relative if it's within the project, otherwise use as-is.
34
+ const resolvedDir = path.resolve(projectRoot, atabeyConfig["frameworkDir"]);
35
+ if (resolvedDir.startsWith(path.resolve(projectRoot))) {
36
+ return path.relative(projectRoot, resolvedDir);
37
+ }
38
+ return atabeyConfig["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, // .atabey
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
+ }
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "atabey-mcp",
3
+ "version": "0.0.4",
4
+ "description": "Agent Atabey Model Context Protocol (MCP) Server",
5
+ "author": "Yusuf BEKAR",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/ysf-bkr/atabey.git"
10
+ },
11
+ "homepage": "https://github.com/ysf-bkr/atabey#readme",
12
+ "type": "module",
13
+ "scripts": {
14
+ "build": "npx tsc",
15
+ "start": "node dist/index.js",
16
+ "dev": "tsx src/index.ts"
17
+ },
18
+ "dependencies": {
19
+ "@modelcontextprotocol/sdk": "^1.26.0",
20
+ "zod": "^3.24.2"
21
+ },
22
+ "devDependencies": {
23
+ "@types/node": "^25.9.1",
24
+ "typescript": "^5.9.3"
25
+ }
26
+ }
@@ -0,0 +1,78 @@
1
+ import path from "path";
2
+
3
+ /**
4
+ * Agent Atabey — 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 Atabey",
12
+ CORE_DIR: ".atabey",
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: "ATABEY_PROJECT_ROOT",
42
+ // Environment variable for test mode
43
+ TEST_DIR_ENV: "ATABEY_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
+ }