atabey-mcp 0.0.7 → 0.0.9
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/{src/constants.ts → dist/constants.js} +3 -17
- package/dist/framework-mcp/src/resources/index.js +2 -2
- package/dist/framework-mcp/src/utils/errors.js +68 -0
- package/dist/framework-mcp/src/utils/memory.js +74 -0
- package/dist/framework-mcp/src/utils/storage.js +207 -0
- package/dist/framework-mcp/src/utils/types.js +12 -0
- package/{src/index.ts → dist/index.js} +31 -59
- package/{src/resources/index.ts → dist/resources/index.js} +4 -11
- package/dist/src/cli/adapters/core.js +12 -19
- package/dist/src/shared/constants.js +1 -0
- package/{src/tools/control_plane/locking.ts → dist/tools/control_plane/locking.js} +11 -18
- package/{src/tools/control_plane/registry.ts → dist/tools/control_plane/registry.js} +5 -9
- package/{src/tools/definitions.ts → dist/tools/definitions.js} +2 -4
- package/{src/tools/file_system/batch_surgical_edit.ts → dist/tools/file_system/batch_surgical_edit.js} +9 -32
- package/{src/tools/file_system/patch_file.ts → dist/tools/file_system/patch_file.js} +2 -14
- package/{src/tools/file_system/read_file.ts → dist/tools/file_system/read_file.js} +6 -13
- package/{src/tools/file_system/replace_text.ts → dist/tools/file_system/replace_text.js} +6 -17
- package/{src/tools/file_system/write_file.ts → dist/tools/file_system/write_file.js} +5 -14
- package/{src/tools/framework/audit_deps.ts → dist/tools/framework/audit_deps.js} +8 -16
- package/{src/tools/framework/get_status.ts → dist/tools/framework/get_status.js} +1 -3
- package/{src/tools/framework/orchestrate.ts → dist/tools/framework/orchestrate.js} +1 -3
- package/{src/tools/framework/run_tests.ts → dist/tools/framework/run_tests.js} +8 -11
- package/{src/tools/framework/submit_plan.ts → dist/tools/framework/submit_plan.js} +1 -14
- package/{src/tools/framework/update_contract_hash.ts → dist/tools/framework/update_contract_hash.js} +1 -3
- package/{src/tools/framework/update_memory.ts → dist/tools/framework/update_memory.js} +1 -3
- package/{src/tools/index.ts → dist/tools/index.js} +3 -7
- package/{src/tools/memory/get_insights.ts → dist/tools/memory/get_insights.js} +3 -10
- package/{src/tools/memory/read_memory.ts → dist/tools/memory/read_memory.js} +3 -6
- package/{src/tools/messaging/log_action.ts → dist/tools/messaging/log_action.js} +1 -7
- package/{src/tools/messaging/send_message.ts → dist/tools/messaging/send_message.js} +14 -17
- package/{src/tools/observability/check_ports.ts → dist/tools/observability/check_ports.js} +6 -10
- package/{src/tools/observability/get_health.ts → dist/tools/observability/get_health.js} +1 -6
- package/{src/tools/quality/check_lint.ts → dist/tools/quality/check_lint.js} +1 -7
- package/{src/tools/search/get_gaps.ts → dist/tools/search/get_gaps.js} +12 -18
- package/{src/tools/search/get_map.ts → dist/tools/search/get_map.js} +14 -19
- package/{src/tools/search/grep_search.ts → dist/tools/search/grep_search.js} +23 -23
- package/{src/tools/search/list_dir.ts → dist/tools/search/list_dir.js} +4 -10
- package/{src/tools/shell/run_command.ts → dist/tools/shell/run_command.js} +1 -11
- package/dist/tools/types.js +1 -0
- package/{src/utils/cli.ts → dist/utils/cli.js} +25 -19
- package/{src/utils/compliance.ts → dist/utils/compliance.js} +34 -74
- package/{src/utils/fs.ts → dist/utils/fs.js} +9 -10
- package/{src/utils/metrics.ts → dist/utils/metrics.js} +11 -28
- package/{src/utils/permissions.ts → dist/utils/permissions.js} +11 -28
- package/{src/utils/security.ts → dist/utils/security.js} +9 -15
- package/package.json +9 -1
- package/src/declarations.d.ts +0 -19
- package/src/tools/types.ts +0 -89
- package/tsconfig.json +0 -13
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
import path from "path";
|
|
2
|
-
|
|
3
2
|
/**
|
|
4
3
|
* Agent Atabey — Single Source of Truth for framework constants.
|
|
5
4
|
* Import from here instead of hardcoding paths, phases, or directory names.
|
|
6
5
|
*/
|
|
7
|
-
|
|
8
6
|
// ─── Framework identity ───────────────────────────────────────────────────
|
|
9
|
-
|
|
10
7
|
export const FRAMEWORK = {
|
|
11
8
|
NAME: "Agent Atabey",
|
|
12
9
|
CORE_DIR: ".atabey",
|
|
@@ -17,7 +14,6 @@ export const FRAMEWORK = {
|
|
|
17
14
|
// This is where all skills are stored
|
|
18
15
|
SKILLS_DIR: "skills",
|
|
19
16
|
};
|
|
20
|
-
|
|
21
17
|
export const FRAMEWORK_SUBDIRS = {
|
|
22
18
|
AGENTS: "agents",
|
|
23
19
|
SKILLS: "skills",
|
|
@@ -28,26 +24,22 @@ export const FRAMEWORK_SUBDIRS = {
|
|
|
28
24
|
LOGS: "logs",
|
|
29
25
|
CONFIG: "config",
|
|
30
26
|
};
|
|
31
|
-
|
|
32
27
|
export const ROOT_CONFIG_FILES = {
|
|
33
28
|
MCP: "mcp.json",
|
|
34
29
|
NATIVE_MODULES: "native-modules.json",
|
|
35
30
|
TSCONFIG: "tsconfig.json",
|
|
36
31
|
ESLINT: "eslint.config.js",
|
|
37
32
|
};
|
|
38
|
-
|
|
39
33
|
export const MCP = {
|
|
40
34
|
// Environment variable used by MCP to identify project root
|
|
41
35
|
PROJECT_ROOT_ENV: "ATABEY_PROJECT_ROOT",
|
|
42
36
|
// Environment variable for test mode
|
|
43
37
|
TEST_DIR_ENV: "ATABEY_TEST_DIR",
|
|
44
38
|
};
|
|
45
|
-
|
|
46
39
|
export const MEMORY_FILES = {
|
|
47
40
|
STATE: "state.json",
|
|
48
41
|
SHARED_FACTS: "shared_facts.json",
|
|
49
42
|
};
|
|
50
|
-
|
|
51
43
|
export const NATIVE_AGENT_PATHS = {
|
|
52
44
|
gemini: ".gemini/agents",
|
|
53
45
|
claude: ".claude/agents",
|
|
@@ -56,23 +48,17 @@ export const NATIVE_AGENT_PATHS = {
|
|
|
56
48
|
grok: ".grok",
|
|
57
49
|
"antigravity-cli": ".agents/agents",
|
|
58
50
|
};
|
|
59
|
-
|
|
60
51
|
// ─── Backward-compatible aliases ──────────────────────────────────────────
|
|
61
|
-
|
|
62
52
|
export const CORE_FRAMEWORK_DIR = FRAMEWORK.CORE_DIR;
|
|
63
53
|
export const UNIFIED_HUB_DIR = FRAMEWORK.UNIFIED_HUB_DIR;
|
|
64
54
|
export const SKILLS_HUB_PATH = pathJoin(UNIFIED_HUB_DIR, FRAMEWORK_SUBDIRS.SKILLS);
|
|
65
|
-
|
|
66
55
|
// ─── Path Helpers ─────────────────────────────────────────────────────────
|
|
67
|
-
|
|
68
|
-
function pathJoin(...args: string[]): string {
|
|
56
|
+
function pathJoin(...args) {
|
|
69
57
|
return path.join(...args);
|
|
70
58
|
}
|
|
71
|
-
|
|
72
|
-
function corePath(subdir: string, filename: string): string {
|
|
59
|
+
function corePath(subdir, filename) {
|
|
73
60
|
return pathJoin(FRAMEWORK.CORE_DIR, subdir, filename);
|
|
74
61
|
}
|
|
75
|
-
|
|
76
|
-
export function knowledgePath(filename: string): string {
|
|
62
|
+
export function knowledgePath(filename) {
|
|
77
63
|
return corePath(FRAMEWORK_SUBDIRS.KNOWLEDGE, filename);
|
|
78
64
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Storage } from "
|
|
1
|
+
import { Storage } from "../utils/storage.js";
|
|
2
2
|
/**
|
|
3
3
|
* [DATA] MCP Resource Definitions
|
|
4
4
|
*/
|
|
@@ -43,7 +43,7 @@ export async function handleReadResource(uri) {
|
|
|
43
43
|
if (uri === "atabey://memory/project") {
|
|
44
44
|
const fs = await import("fs");
|
|
45
45
|
const path = await import("path");
|
|
46
|
-
const { getFrameworkDir } = await import("
|
|
46
|
+
const { getFrameworkDir } = await import("../utils/memory.js");
|
|
47
47
|
const projectRoot = process.env.ATABEY_PROJECT_ROOT || process.cwd();
|
|
48
48
|
const fwDir = getFrameworkDir();
|
|
49
49
|
const p = path.isAbsolute(fwDir)
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enterprise Base Error class for the Agent Atabey Framework.
|
|
3
|
+
*/
|
|
4
|
+
export class AtabeyBaseError extends Error {
|
|
5
|
+
code;
|
|
6
|
+
timestamp;
|
|
7
|
+
details;
|
|
8
|
+
solution;
|
|
9
|
+
constructor(message, code = "ATABEY_INTERNAL_ERROR", details, solution) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = this.constructor.name;
|
|
12
|
+
this.code = code;
|
|
13
|
+
this.timestamp = new Date();
|
|
14
|
+
this.details = details;
|
|
15
|
+
this.solution = solution;
|
|
16
|
+
// Ensure proper prototype chain and capture stack trace
|
|
17
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
18
|
+
if (Error.captureStackTrace) {
|
|
19
|
+
Error.captureStackTrace(this, this.constructor);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Converts the error into a structured JSON log format.
|
|
24
|
+
*/
|
|
25
|
+
toJSON() {
|
|
26
|
+
return {
|
|
27
|
+
name: this.name,
|
|
28
|
+
message: this.message,
|
|
29
|
+
code: this.code,
|
|
30
|
+
timestamp: this.timestamp.toISOString(),
|
|
31
|
+
details: this.details,
|
|
32
|
+
solution: this.solution,
|
|
33
|
+
stack: this.stack,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Thrown when configuration loading or validation fails.
|
|
39
|
+
*/
|
|
40
|
+
export class ConfigurationError extends AtabeyBaseError {
|
|
41
|
+
constructor(message, details, solution) {
|
|
42
|
+
super(message, "CONFIGURATION_ERROR", details, solution);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Thrown when data schema or payload validation fails.
|
|
47
|
+
*/
|
|
48
|
+
export class ValidationError extends AtabeyBaseError {
|
|
49
|
+
constructor(message, details, solution) {
|
|
50
|
+
super(message, "VALIDATION_ERROR", details, solution);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Thrown when adapter initialization or execution fails.
|
|
55
|
+
*/
|
|
56
|
+
export class AdapterError extends AtabeyBaseError {
|
|
57
|
+
constructor(message, adapterId, details, solution) {
|
|
58
|
+
super(`Adapter '${adapterId}' failure: ${message}`, "ADAPTER_ERROR", details, solution);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Thrown when orchestration or agent communication fails.
|
|
63
|
+
*/
|
|
64
|
+
export class OrchestrationError extends AtabeyBaseError {
|
|
65
|
+
constructor(message, details, solution) {
|
|
66
|
+
super(message, "ORCHESTRATION_ERROR", details, solution);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import os from "os";
|
|
3
|
+
import path from "path";
|
|
4
|
+
const CWD = process.cwd();
|
|
5
|
+
const HOME = os.homedir();
|
|
6
|
+
const FRAMEWORK = {
|
|
7
|
+
CORE_DIR: ".atabey",
|
|
8
|
+
};
|
|
9
|
+
const FRAMEWORK_DIR_CANDIDATES = [
|
|
10
|
+
".atabey",
|
|
11
|
+
".agents",
|
|
12
|
+
".claude",
|
|
13
|
+
".gemini",
|
|
14
|
+
".grok",
|
|
15
|
+
".cursor",
|
|
16
|
+
];
|
|
17
|
+
const MCP = {
|
|
18
|
+
TEST_DIR_ENV: "ATABEY_TEST_DIR",
|
|
19
|
+
};
|
|
20
|
+
function findFrameworkDir(basePath) {
|
|
21
|
+
try {
|
|
22
|
+
const pkgPath = path.join(basePath, "package.json");
|
|
23
|
+
if (fs.existsSync(pkgPath)) {
|
|
24
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
25
|
+
if (pkg.atabey && typeof pkg.atabey.frameworkDir === "string") {
|
|
26
|
+
const customDir = path.join(basePath, pkg.atabey.frameworkDir);
|
|
27
|
+
if (fs.existsSync(customDir))
|
|
28
|
+
return customDir;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
// ignore
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
export function isFrameworkDevelopmentRepo() {
|
|
38
|
+
try {
|
|
39
|
+
const pkgPath = path.join(CWD, "package.json");
|
|
40
|
+
if (fs.existsSync(pkgPath)) {
|
|
41
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
42
|
+
if (pkg.name === "atabey") {
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
// ignore
|
|
49
|
+
}
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
export function getConfigDir() {
|
|
53
|
+
const localDir = findFrameworkDir(CWD);
|
|
54
|
+
if (localDir)
|
|
55
|
+
return localDir;
|
|
56
|
+
// Check for standard local directories if package.json doesn't specify
|
|
57
|
+
const localCandidates = [...FRAMEWORK_DIR_CANDIDATES, ".agent"];
|
|
58
|
+
for (const cand of localCandidates) {
|
|
59
|
+
const p = path.join(CWD, cand);
|
|
60
|
+
if (fs.existsSync(p))
|
|
61
|
+
return p;
|
|
62
|
+
}
|
|
63
|
+
// In dev repo, don't fall back to global dir. Point to local default.
|
|
64
|
+
if (isFrameworkDevelopmentRepo()) {
|
|
65
|
+
return path.join(CWD, FRAMEWORK.CORE_DIR);
|
|
66
|
+
}
|
|
67
|
+
return path.join(HOME, FRAMEWORK.CORE_DIR);
|
|
68
|
+
}
|
|
69
|
+
export function getFrameworkDir() {
|
|
70
|
+
const testDir = process.env[MCP.TEST_DIR_ENV];
|
|
71
|
+
if (testDir)
|
|
72
|
+
return testDir;
|
|
73
|
+
return getConfigDir();
|
|
74
|
+
}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import Database from "better-sqlite3";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import { getFrameworkDir } from "./memory.js";
|
|
5
|
+
/**
|
|
6
|
+
* [DB] Atabey Storage Engine
|
|
7
|
+
* Central SQLite database for enterprise-scale agent state management.
|
|
8
|
+
*/
|
|
9
|
+
export class Storage {
|
|
10
|
+
static db = null;
|
|
11
|
+
static getDB() {
|
|
12
|
+
if (!this.db) {
|
|
13
|
+
const frameworkDir = getFrameworkDir();
|
|
14
|
+
if (!fs.existsSync(frameworkDir)) {
|
|
15
|
+
fs.mkdirSync(frameworkDir, { recursive: true });
|
|
16
|
+
}
|
|
17
|
+
const dbPath = path.join(frameworkDir, "atabey.db");
|
|
18
|
+
this.db = new Database(dbPath);
|
|
19
|
+
this.initializeSchema();
|
|
20
|
+
}
|
|
21
|
+
return this.db;
|
|
22
|
+
}
|
|
23
|
+
static initializeSchema() {
|
|
24
|
+
const db = this.db;
|
|
25
|
+
// Agents Table
|
|
26
|
+
db.exec(`
|
|
27
|
+
CREATE TABLE IF NOT EXISTS agents (
|
|
28
|
+
name TEXT PRIMARY KEY,
|
|
29
|
+
state TEXT DEFAULT 'READY',
|
|
30
|
+
task TEXT DEFAULT 'Idle',
|
|
31
|
+
last_updated DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
32
|
+
)
|
|
33
|
+
`);
|
|
34
|
+
// Messages (Hermes) Table
|
|
35
|
+
db.exec(`
|
|
36
|
+
CREATE TABLE IF NOT EXISTS messages (
|
|
37
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
38
|
+
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
39
|
+
sender TEXT,
|
|
40
|
+
receiver TEXT,
|
|
41
|
+
category TEXT,
|
|
42
|
+
content TEXT,
|
|
43
|
+
trace_id TEXT,
|
|
44
|
+
parent_id TEXT,
|
|
45
|
+
status TEXT DEFAULT 'PENDING',
|
|
46
|
+
priority TEXT DEFAULT 'NORMAL',
|
|
47
|
+
requires_approval BOOLEAN DEFAULT 0
|
|
48
|
+
)
|
|
49
|
+
`);
|
|
50
|
+
// Tasks Table
|
|
51
|
+
db.exec(`
|
|
52
|
+
CREATE TABLE IF NOT EXISTS tasks (
|
|
53
|
+
id TEXT PRIMARY KEY,
|
|
54
|
+
trace_id TEXT,
|
|
55
|
+
description TEXT,
|
|
56
|
+
agent TEXT,
|
|
57
|
+
status TEXT DEFAULT 'PENDING',
|
|
58
|
+
priority TEXT DEFAULT 'NORMAL',
|
|
59
|
+
dependencies TEXT, -- JSON Array
|
|
60
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
61
|
+
)
|
|
62
|
+
`);
|
|
63
|
+
// Logs Table
|
|
64
|
+
db.exec(`
|
|
65
|
+
CREATE TABLE IF NOT EXISTS logs (
|
|
66
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
67
|
+
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
68
|
+
agent TEXT,
|
|
69
|
+
action TEXT,
|
|
70
|
+
trace_id TEXT,
|
|
71
|
+
status TEXT,
|
|
72
|
+
summary TEXT,
|
|
73
|
+
findings TEXT
|
|
74
|
+
)
|
|
75
|
+
`);
|
|
76
|
+
// Metadata (State) Table
|
|
77
|
+
db.exec(`
|
|
78
|
+
CREATE TABLE IF NOT EXISTS metadata (
|
|
79
|
+
key TEXT PRIMARY KEY,
|
|
80
|
+
value TEXT
|
|
81
|
+
)
|
|
82
|
+
`);
|
|
83
|
+
}
|
|
84
|
+
// --- Agent Operations ---
|
|
85
|
+
static updateAgentStatus(name, state, task, lastUpdated) {
|
|
86
|
+
const db = this.getDB();
|
|
87
|
+
const cleanName = name.replace("@", "");
|
|
88
|
+
const timestamp = lastUpdated || new Date().toISOString();
|
|
89
|
+
db.prepare(`
|
|
90
|
+
INSERT INTO agents (name, state, task, last_updated)
|
|
91
|
+
VALUES (?, ?, ?, ?)
|
|
92
|
+
ON CONFLICT(name) DO UPDATE SET
|
|
93
|
+
state = excluded.state,
|
|
94
|
+
task = excluded.task,
|
|
95
|
+
last_updated = excluded.last_updated
|
|
96
|
+
`).run(cleanName, state, task, timestamp);
|
|
97
|
+
}
|
|
98
|
+
static getAllAgents() {
|
|
99
|
+
return this.getDB().prepare("SELECT * FROM agents").all();
|
|
100
|
+
}
|
|
101
|
+
// --- Message Operations ---
|
|
102
|
+
static saveMessage(msg) {
|
|
103
|
+
const db = this.getDB();
|
|
104
|
+
db.prepare(`
|
|
105
|
+
INSERT INTO messages (sender, receiver, category, content, trace_id, parent_id, status, priority, requires_approval)
|
|
106
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
107
|
+
`).run(msg.from, msg.to, msg.category, msg.content, msg.traceId, msg.parentId || null, msg.status || "PENDING", msg.priority || "NORMAL", msg.requiresApproval ? 1 : 0);
|
|
108
|
+
}
|
|
109
|
+
static getPendingMessages() {
|
|
110
|
+
const rows = this.getDB().prepare("SELECT * FROM messages WHERE status IN ('PENDING', 'APPROVED') ORDER BY priority DESC, timestamp ASC").all();
|
|
111
|
+
return rows.map(r => ({
|
|
112
|
+
id: r.id,
|
|
113
|
+
timestamp: r.timestamp,
|
|
114
|
+
from: r.sender,
|
|
115
|
+
to: r.receiver,
|
|
116
|
+
category: r.category,
|
|
117
|
+
content: r.content,
|
|
118
|
+
traceId: r.trace_id,
|
|
119
|
+
parentId: r.parent_id || undefined,
|
|
120
|
+
status: r.status,
|
|
121
|
+
priority: r.priority,
|
|
122
|
+
requiresApproval: r.requires_approval === 1
|
|
123
|
+
}));
|
|
124
|
+
}
|
|
125
|
+
static updateMessageStatus(id, status) {
|
|
126
|
+
this.getDB().prepare("UPDATE messages SET status = ? WHERE id = ?").run(status, id);
|
|
127
|
+
}
|
|
128
|
+
// --- Task Operations ---
|
|
129
|
+
static saveTask(task) {
|
|
130
|
+
const db = this.getDB();
|
|
131
|
+
db.prepare(`
|
|
132
|
+
INSERT INTO tasks (id, trace_id, description, agent, status, priority, dependencies)
|
|
133
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
134
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
135
|
+
status = excluded.status,
|
|
136
|
+
description = excluded.description
|
|
137
|
+
`).run(task.id, task.traceId, task.description, task.agent, task.status, task.priority, JSON.stringify(task.dependencies || []));
|
|
138
|
+
}
|
|
139
|
+
static getTasks(traceId) {
|
|
140
|
+
const db = this.getDB();
|
|
141
|
+
let rows;
|
|
142
|
+
if (traceId) {
|
|
143
|
+
rows = db.prepare("SELECT * FROM tasks WHERE trace_id = ?").all(traceId);
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
rows = db.prepare("SELECT * FROM tasks").all();
|
|
147
|
+
}
|
|
148
|
+
return rows.map(r => {
|
|
149
|
+
let deps = [];
|
|
150
|
+
try {
|
|
151
|
+
const parsed = JSON.parse(r.dependencies || "[]");
|
|
152
|
+
// Handle cases where dependencies might be double-stringified or are raw JSON strings
|
|
153
|
+
deps = Array.isArray(parsed) ? parsed : JSON.parse(parsed);
|
|
154
|
+
}
|
|
155
|
+
catch {
|
|
156
|
+
// Keep empty array
|
|
157
|
+
}
|
|
158
|
+
return {
|
|
159
|
+
id: r.id,
|
|
160
|
+
traceId: r.trace_id,
|
|
161
|
+
description: r.description,
|
|
162
|
+
agent: r.agent,
|
|
163
|
+
status: r.status,
|
|
164
|
+
priority: r.priority,
|
|
165
|
+
dependencies: deps
|
|
166
|
+
};
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
static getLogs(agentName) {
|
|
170
|
+
const db = this.getDB();
|
|
171
|
+
let rows;
|
|
172
|
+
const cleanName = agentName ? agentName.replace("@", "") : undefined;
|
|
173
|
+
if (cleanName) {
|
|
174
|
+
rows = db.prepare("SELECT * FROM logs WHERE agent = ? ORDER BY timestamp DESC").all(cleanName);
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
rows = db.prepare("SELECT * FROM logs ORDER BY timestamp DESC").all();
|
|
178
|
+
}
|
|
179
|
+
return rows.map(r => ({
|
|
180
|
+
id: r.id,
|
|
181
|
+
timestamp: r.timestamp,
|
|
182
|
+
agent: r.agent,
|
|
183
|
+
action: r.action,
|
|
184
|
+
trace_id: r.trace_id || undefined,
|
|
185
|
+
status: r.status,
|
|
186
|
+
summary: r.summary,
|
|
187
|
+
findings: r.findings || undefined
|
|
188
|
+
}));
|
|
189
|
+
}
|
|
190
|
+
// --- Metadata Operations ---
|
|
191
|
+
static setMetadata(key, value) {
|
|
192
|
+
this.getDB().prepare(`
|
|
193
|
+
INSERT INTO metadata (key, value) VALUES (?, ?)
|
|
194
|
+
ON CONFLICT(key) DO UPDATE SET value = excluded.value
|
|
195
|
+
`).run(key, value);
|
|
196
|
+
}
|
|
197
|
+
static getMetadata(key) {
|
|
198
|
+
const row = this.getDB().prepare("SELECT value FROM metadata WHERE key = ?").get(key);
|
|
199
|
+
return row ? row.value : null;
|
|
200
|
+
}
|
|
201
|
+
static reset() {
|
|
202
|
+
if (this.db) {
|
|
203
|
+
this.db.close();
|
|
204
|
+
this.db = null;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Atabey Framework — Internal Branded Types
|
|
3
|
+
* Used to enforce absolute type safety within the core orchestration logic.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Casts a raw string to a Branded Type.
|
|
7
|
+
* Use this only at the boundaries of the system.
|
|
8
|
+
*/
|
|
9
|
+
export function asTraceID(val) { return val; }
|
|
10
|
+
export function asAgentID(val) { return val; }
|
|
11
|
+
export function asPhaseID(val) { return val; }
|
|
12
|
+
export function asProjectPath(val) { return val; }
|
|
@@ -4,55 +4,39 @@ import path from "path";
|
|
|
4
4
|
import { fileURLToPath } from "url";
|
|
5
5
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
6
6
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
7
|
-
import {
|
|
8
|
-
CallToolRequestSchema,
|
|
9
|
-
ListToolsRequestSchema,
|
|
10
|
-
ListResourcesRequestSchema,
|
|
11
|
-
ReadResourceRequestSchema,
|
|
12
|
-
} from "@modelcontextprotocol/sdk/types.js";
|
|
13
|
-
|
|
14
|
-
|
|
7
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
15
8
|
import { TOOLS, toolHandlers } from "./tools/index.js";
|
|
16
9
|
import { RESOURCES, handleReadResource } from "./resources/index.js";
|
|
17
|
-
|
|
18
|
-
|
|
19
10
|
// ─── Server Setup ─────────────────────────────────────────────────
|
|
20
|
-
|
|
21
11
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
22
|
-
|
|
23
12
|
// Robustly find package.json by walking up from __dirname
|
|
24
|
-
function findPackageJson(startDir
|
|
13
|
+
function findPackageJson(startDir) {
|
|
25
14
|
let currentDir = startDir;
|
|
26
15
|
while (currentDir !== path.parse(currentDir).root) {
|
|
27
16
|
const pkgPath = path.join(currentDir, "package.json");
|
|
28
|
-
if (fs.existsSync(pkgPath))
|
|
17
|
+
if (fs.existsSync(pkgPath))
|
|
18
|
+
return pkgPath;
|
|
29
19
|
currentDir = path.dirname(currentDir);
|
|
30
20
|
}
|
|
31
21
|
throw new Error("Could not find package.json for atabey-mcp");
|
|
32
22
|
}
|
|
33
|
-
|
|
34
23
|
const pkgPath = findPackageJson(__dirname);
|
|
35
24
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
36
25
|
const serverVersion = pkg.version;
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
26
|
+
const server = new Server({
|
|
27
|
+
name: "atabey-mcp",
|
|
28
|
+
version: serverVersion,
|
|
29
|
+
}, {
|
|
30
|
+
capabilities: {
|
|
31
|
+
tools: {},
|
|
32
|
+
resources: {},
|
|
42
33
|
},
|
|
43
|
-
|
|
44
|
-
capabilities: {
|
|
45
|
-
tools: {},
|
|
46
|
-
resources: {},
|
|
47
|
-
},
|
|
48
|
-
}
|
|
49
|
-
);
|
|
50
|
-
|
|
34
|
+
});
|
|
51
35
|
// Basic Schema Validator for Required Fields
|
|
52
|
-
function validateArgs(toolName
|
|
36
|
+
function validateArgs(toolName, args) {
|
|
53
37
|
const definition = TOOLS.find(t => t.name === toolName);
|
|
54
|
-
if (!definition)
|
|
55
|
-
|
|
38
|
+
if (!definition)
|
|
39
|
+
return `Unknown tool: ${toolName}`;
|
|
56
40
|
const required = definition.inputSchema.required || [];
|
|
57
41
|
for (const field of required) {
|
|
58
42
|
if (args[field] === undefined || args[field] === null || args[field] === "") {
|
|
@@ -61,22 +45,19 @@ function validateArgs(toolName: string, args: Record<string, unknown>): string |
|
|
|
61
45
|
}
|
|
62
46
|
return null;
|
|
63
47
|
}
|
|
64
|
-
|
|
65
48
|
server.setRequestHandler(ListToolsRequestSchema, async (request) => {
|
|
66
49
|
// 2026 Stateless Spec: Log client info from metadata if available
|
|
67
|
-
const meta =
|
|
50
|
+
const meta = request._meta;
|
|
68
51
|
if (meta) {
|
|
69
52
|
process.stderr.write(`[MCP] Stateless ListTools from ${meta.client?.name || "unknown"} v${meta.client?.version || "?.?"}\n`);
|
|
70
53
|
}
|
|
71
54
|
return { tools: TOOLS };
|
|
72
55
|
});
|
|
73
|
-
|
|
74
56
|
server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
75
57
|
return { resources: RESOURCES };
|
|
76
58
|
});
|
|
77
|
-
|
|
78
59
|
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
79
|
-
const uri =
|
|
60
|
+
const uri = request.params.uri;
|
|
80
61
|
try {
|
|
81
62
|
const content = await handleReadResource(uri);
|
|
82
63
|
return {
|
|
@@ -88,84 +69,75 @@ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
|
88
69
|
},
|
|
89
70
|
],
|
|
90
71
|
};
|
|
91
|
-
}
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
92
74
|
const message = error instanceof Error ? error.message : String(error);
|
|
93
75
|
throw new Error(`Failed to read resource: ${message}`, { cause: error });
|
|
94
76
|
}
|
|
95
77
|
});
|
|
96
|
-
|
|
97
78
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
98
|
-
const req = request
|
|
79
|
+
const req = request;
|
|
99
80
|
const { name, arguments: args } = req.params;
|
|
100
|
-
const meta =
|
|
101
|
-
|
|
81
|
+
const meta = request._meta;
|
|
102
82
|
// 2026 Stateless Spec: Prioritize metadata-driven context
|
|
103
83
|
if (meta) {
|
|
104
84
|
process.stderr.write(`[MCP] Stateless CallTool: ${name} (Client: ${meta.client?.name || "unknown"})\n`);
|
|
105
85
|
}
|
|
106
|
-
|
|
107
86
|
const projectRoot = process.env.ATABEY_PROJECT_ROOT || process.cwd();
|
|
108
|
-
|
|
109
87
|
try {
|
|
110
88
|
const handler = toolHandlers[name];
|
|
111
89
|
if (!handler) {
|
|
112
90
|
return {
|
|
113
91
|
isError: true,
|
|
114
|
-
content: [{ type: "text"
|
|
92
|
+
content: [{ type: "text", text: `[ERROR] Unknown tool: ${name}` }],
|
|
115
93
|
};
|
|
116
94
|
}
|
|
117
|
-
|
|
118
95
|
// [SECURITY] Runtime Validation
|
|
119
96
|
const validationError = validateArgs(name, args || {});
|
|
120
97
|
if (validationError) {
|
|
121
98
|
return {
|
|
122
99
|
isError: true,
|
|
123
|
-
content: [{ type: "text"
|
|
100
|
+
content: [{ type: "text", text: `[ERROR] Validation Error: ${validationError}` }],
|
|
124
101
|
};
|
|
125
102
|
}
|
|
126
|
-
|
|
127
103
|
return await handler(projectRoot, args || {});
|
|
128
|
-
}
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
129
106
|
const message = error instanceof Error ? error.message : "Unknown error occurred";
|
|
130
107
|
return {
|
|
131
108
|
isError: true,
|
|
132
|
-
content: [{ type: "text"
|
|
109
|
+
content: [{ type: "text", text: `[ERROR] Execution failed: ${message}` }],
|
|
133
110
|
};
|
|
134
111
|
}
|
|
135
112
|
});
|
|
136
|
-
|
|
137
113
|
// ─── Graceful Startup & Shutdown ──────────────────────────────────
|
|
138
|
-
|
|
139
114
|
async function run() {
|
|
140
115
|
const transport = new StdioServerTransport();
|
|
141
|
-
|
|
142
116
|
// Prevent unhandled errors from crashing the MCP stream
|
|
143
|
-
process.on("uncaughtException", (error
|
|
117
|
+
process.on("uncaughtException", (error) => {
|
|
144
118
|
process.stderr.write(`[atabey-mcp] Uncaught exception: ${error.message}
|
|
145
119
|
`);
|
|
146
120
|
});
|
|
147
|
-
process.on("unhandledRejection", (reason
|
|
121
|
+
process.on("unhandledRejection", (reason) => {
|
|
148
122
|
const message = reason instanceof Error ? reason.message : String(reason);
|
|
149
123
|
process.stderr.write(`[atabey-mcp] Unhandled rejection: ${message}
|
|
150
124
|
`);
|
|
151
125
|
});
|
|
152
|
-
|
|
153
126
|
// Graceful shutdown on SIGINT/SIGTERM
|
|
154
127
|
const shutdown = async () => {
|
|
155
128
|
try {
|
|
156
129
|
await server.close();
|
|
157
|
-
}
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
158
132
|
// Already closed or failed — safe to ignore
|
|
159
133
|
}
|
|
160
134
|
process.exit(0);
|
|
161
135
|
};
|
|
162
136
|
process.on("SIGINT", shutdown);
|
|
163
137
|
process.on("SIGTERM", shutdown);
|
|
164
|
-
|
|
165
138
|
await server.connect(transport);
|
|
166
139
|
}
|
|
167
|
-
|
|
168
|
-
run().catch((error: Error) => {
|
|
140
|
+
run().catch((error) => {
|
|
169
141
|
process.stderr.write(`[atabey-mcp] Fatal startup error: ${error.message}
|
|
170
142
|
`);
|
|
171
143
|
process.exit(1);
|