@musashishao/agent-kit 1.2.2 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agent/mcp-gateway/README.md +121 -0
- package/.agent/mcp-gateway/dist/index.d.ts +11 -0
- package/.agent/mcp-gateway/dist/index.js +504 -0
- package/.agent/mcp-gateway/dist/sync/debouncer.d.ts +56 -0
- package/.agent/mcp-gateway/dist/sync/debouncer.js +112 -0
- package/.agent/mcp-gateway/dist/sync/incremental_syncer.d.ts +58 -0
- package/.agent/mcp-gateway/dist/sync/incremental_syncer.js +172 -0
- package/.agent/mcp-gateway/dist/sync/index.d.ts +6 -0
- package/.agent/mcp-gateway/dist/sync/index.js +6 -0
- package/.agent/mcp-gateway/dist/sync/timestamp_checker.d.ts +69 -0
- package/.agent/mcp-gateway/dist/sync/timestamp_checker.js +169 -0
- package/.agent/mcp-gateway/package.json +28 -0
- package/.agent/mcp-gateway/src/index.ts +608 -0
- package/.agent/mcp-gateway/src/sync/debouncer.ts +129 -0
- package/.agent/mcp-gateway/src/sync/incremental_syncer.ts +237 -0
- package/.agent/mcp-gateway/src/sync/index.ts +7 -0
- package/.agent/mcp-gateway/src/sync/timestamp_checker.ts +194 -0
- package/.agent/scripts/ak_cli.py +549 -0
- package/.agent/scripts/setup_host.py +557 -0
- package/.agent/scripts/verify_install.py +174 -0
- package/.agent/skills/app-builder/SKILL.md +51 -1
- package/.agent/skills/app-builder/scripts/generate_ai_infra.py +510 -0
- package/.agent/skills/documentation-templates/SKILL.md +9 -1
- package/.agent/skills/documentation-templates/agents-template.md +202 -0
- package/.agent/skills/graph-mapper/SKILL.md +211 -0
- package/.agent/skills/graph-mapper/scripts/generate_graph.py +705 -0
- package/.agent/skills/rag-engineering/SKILL.md +342 -0
- package/.agent/skills/rag-engineering/chunking-strategies.md +229 -0
- package/.agent/skills/rag-engineering/contextual-retrieval.md +261 -0
- package/.agent/skills/rag-engineering/hybrid-search.md +356 -0
- package/.agent/skills/rag-engineering/scripts/chunk_code.py +916 -0
- package/.agent/templates/mcp_configs/claude_desktop.json +14 -0
- package/.agent/templates/mcp_configs/cursor.json +13 -0
- package/.agent/templates/mcp_configs/vscode.json +13 -0
- package/.agent/workflows/create.md +70 -2
- package/bin/cli.js +91 -0
- package/docs/AI_DATA_INFRASTRUCTURE.md +288 -0
- package/docs/CHANGELOG_AI_INFRA.md +111 -0
- package/docs/PLAN-universal-intelligence.md +48 -0
- package/package.json +7 -2
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# Agent Kit MCP Gateway
|
|
2
|
+
|
|
3
|
+
> Live Project Cortex - MCP Server providing real-time project context to AI agents.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **get_project_context** - Read AGENTS.md sections
|
|
8
|
+
- **analyze_dependencies** - Query dependency graph
|
|
9
|
+
- **search_code_logic** - Semantic code search
|
|
10
|
+
- **get_impact_zone** - Impact analysis for changes
|
|
11
|
+
- **force_sync** - Refresh all AI data
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
cd .agent/mcp-gateway
|
|
17
|
+
npm install
|
|
18
|
+
npm run build
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
### With Claude Desktop
|
|
24
|
+
|
|
25
|
+
Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
|
|
26
|
+
|
|
27
|
+
```json
|
|
28
|
+
{
|
|
29
|
+
"mcpServers": {
|
|
30
|
+
"agent-kit": {
|
|
31
|
+
"command": "node",
|
|
32
|
+
"args": ["/path/to/.agent/mcp-gateway/dist/index.js"],
|
|
33
|
+
"env": {
|
|
34
|
+
"PROJECT_ROOT": "/path/to/your/project"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### With Cursor
|
|
42
|
+
|
|
43
|
+
Add to `.cursor/mcp.json`:
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
{
|
|
47
|
+
"mcpServers": {
|
|
48
|
+
"agent-kit": {
|
|
49
|
+
"command": "node",
|
|
50
|
+
"args": [".agent/mcp-gateway/dist/index.js"],
|
|
51
|
+
"env": {
|
|
52
|
+
"PROJECT_ROOT": "."
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Development
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# Run in dev mode
|
|
63
|
+
npm run dev
|
|
64
|
+
|
|
65
|
+
# Build for production
|
|
66
|
+
npm run build
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Environment Variables
|
|
70
|
+
|
|
71
|
+
| Variable | Description | Default |
|
|
72
|
+
|----------|-------------|---------|
|
|
73
|
+
| `PROJECT_ROOT` | Path to project root | Current directory |
|
|
74
|
+
| `AGENT_KIT_PATH` | Path to Agent Kit installation | Auto-detected |
|
|
75
|
+
|
|
76
|
+
## Tools Reference
|
|
77
|
+
|
|
78
|
+
### get_project_context
|
|
79
|
+
|
|
80
|
+
```json
|
|
81
|
+
{
|
|
82
|
+
"section": "tech_stack" // or "structure", "conventions", "commands", "all"
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### analyze_dependencies
|
|
87
|
+
|
|
88
|
+
```json
|
|
89
|
+
{
|
|
90
|
+
"file_path": "src/components/Button.tsx",
|
|
91
|
+
"direction": "both", // "imports", "imported_by", "both"
|
|
92
|
+
"depth": 2
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### search_code_logic
|
|
97
|
+
|
|
98
|
+
```json
|
|
99
|
+
{
|
|
100
|
+
"query": "authentication login",
|
|
101
|
+
"file_filter": "*.ts",
|
|
102
|
+
"top_k": 10
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### get_impact_zone
|
|
107
|
+
|
|
108
|
+
```json
|
|
109
|
+
{
|
|
110
|
+
"file_path": "src/utils/auth.ts",
|
|
111
|
+
"depth": 3
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### force_sync
|
|
116
|
+
|
|
117
|
+
```json
|
|
118
|
+
{
|
|
119
|
+
"target": "all" // or "graph", "rag", "agents_md"
|
|
120
|
+
}
|
|
121
|
+
```
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Agent Kit MCP Gateway Server
|
|
4
|
+
*
|
|
5
|
+
* Provides AI agents with live access to project context, dependency graphs,
|
|
6
|
+
* and semantic code search with automatic sync capabilities.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* node dist/index.js [--project-root /path/to/project]
|
|
10
|
+
*/
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,504 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Agent Kit MCP Gateway Server
|
|
4
|
+
*
|
|
5
|
+
* Provides AI agents with live access to project context, dependency graphs,
|
|
6
|
+
* and semantic code search with automatic sync capabilities.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* node dist/index.js [--project-root /path/to/project]
|
|
10
|
+
*/
|
|
11
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
12
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
13
|
+
import { z } from "zod";
|
|
14
|
+
import * as fs from "fs";
|
|
15
|
+
import * as path from "path";
|
|
16
|
+
// Import sync modules
|
|
17
|
+
import { IncrementalSyncer, getGlobalDebouncer, TimestampChecker } from "./sync/index.js";
|
|
18
|
+
// Get project root from environment or command line
|
|
19
|
+
const PROJECT_ROOT = process.env.PROJECT_ROOT || process.cwd();
|
|
20
|
+
// Initialize sync components
|
|
21
|
+
const syncer = new IncrementalSyncer(PROJECT_ROOT);
|
|
22
|
+
const debouncer = getGlobalDebouncer(30000); // 30 second cooldown
|
|
23
|
+
const timestampChecker = new TimestampChecker(PROJECT_ROOT);
|
|
24
|
+
// ============================================================================
|
|
25
|
+
// Auto-Sync Helper
|
|
26
|
+
// ============================================================================
|
|
27
|
+
async function autoSyncIfNeeded() {
|
|
28
|
+
// Check if data is stale
|
|
29
|
+
const status = timestampChecker.getSyncStatus();
|
|
30
|
+
if (!status.needsSync) {
|
|
31
|
+
return { synced: false, message: "Data is fresh" };
|
|
32
|
+
}
|
|
33
|
+
// Try to sync with debouncing
|
|
34
|
+
const result = await debouncer.executeWithDebounce(async () => {
|
|
35
|
+
return await syncer.syncAll();
|
|
36
|
+
});
|
|
37
|
+
if (result.executed) {
|
|
38
|
+
return { synced: true, message: `Auto-synced ${status.changedFiles} changed files` };
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
return { synced: false, message: result.reason || "Sync skipped" };
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// ============================================================================
|
|
45
|
+
// Utility Functions
|
|
46
|
+
// ============================================================================
|
|
47
|
+
function readJsonFile(filePath) {
|
|
48
|
+
try {
|
|
49
|
+
const fullPath = path.resolve(PROJECT_ROOT, filePath);
|
|
50
|
+
if (!fs.existsSync(fullPath)) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
return JSON.parse(fs.readFileSync(fullPath, "utf-8"));
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function readTextFile(filePath) {
|
|
60
|
+
try {
|
|
61
|
+
const fullPath = path.resolve(PROJECT_ROOT, filePath);
|
|
62
|
+
if (!fs.existsSync(fullPath)) {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
return fs.readFileSync(fullPath, "utf-8");
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function getFileModTime(filePath) {
|
|
72
|
+
try {
|
|
73
|
+
const fullPath = path.resolve(PROJECT_ROOT, filePath);
|
|
74
|
+
const stats = fs.statSync(fullPath);
|
|
75
|
+
return stats.mtimeMs;
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return 0;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
// ============================================================================
|
|
82
|
+
// Create MCP Server
|
|
83
|
+
// ============================================================================
|
|
84
|
+
const server = new McpServer({
|
|
85
|
+
name: "agent-kit-gateway",
|
|
86
|
+
version: "1.0.0",
|
|
87
|
+
});
|
|
88
|
+
// ============================================================================
|
|
89
|
+
// Tool 1: get_project_context
|
|
90
|
+
// ============================================================================
|
|
91
|
+
server.tool("get_project_context", "Get project overview including tech stack, structure, and conventions from AGENTS.md", {
|
|
92
|
+
section: z
|
|
93
|
+
.enum(["all", "tech_stack", "structure", "conventions", "commands"])
|
|
94
|
+
.optional()
|
|
95
|
+
.default("all")
|
|
96
|
+
.describe("Specific section to retrieve"),
|
|
97
|
+
}, async ({ section }) => {
|
|
98
|
+
const agentsMd = readTextFile("AGENTS.md");
|
|
99
|
+
if (!agentsMd) {
|
|
100
|
+
return {
|
|
101
|
+
content: [
|
|
102
|
+
{
|
|
103
|
+
type: "text",
|
|
104
|
+
text: JSON.stringify({
|
|
105
|
+
error: "AGENTS.md not found",
|
|
106
|
+
suggestion: "Run 'ak init' to generate AI infrastructure",
|
|
107
|
+
project_root: PROJECT_ROOT,
|
|
108
|
+
}),
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
// Parse sections from AGENTS.md
|
|
114
|
+
const sections = {};
|
|
115
|
+
const sectionRegex = /^## (.+)$/gm;
|
|
116
|
+
let matches;
|
|
117
|
+
let lastIndex = 0;
|
|
118
|
+
let lastSection = "";
|
|
119
|
+
const lines = agentsMd.split("\n");
|
|
120
|
+
let currentSection = "";
|
|
121
|
+
let currentContent = [];
|
|
122
|
+
for (const line of lines) {
|
|
123
|
+
if (line.startsWith("## ")) {
|
|
124
|
+
if (currentSection) {
|
|
125
|
+
sections[currentSection] = currentContent.join("\n").trim();
|
|
126
|
+
}
|
|
127
|
+
currentSection = line.replace("## ", "").trim();
|
|
128
|
+
currentContent = [];
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
currentContent.push(line);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
if (currentSection) {
|
|
135
|
+
sections[currentSection] = currentContent.join("\n").trim();
|
|
136
|
+
}
|
|
137
|
+
// Map section names to content
|
|
138
|
+
const sectionMap = {
|
|
139
|
+
tech_stack: sections["🛠️ Tech Stack"] || sections["Tech Stack"],
|
|
140
|
+
structure: sections["📁 Directory Map"] || sections["Directory Map"],
|
|
141
|
+
conventions: sections["📜 Rules & Conventions"] || sections["Rules & Conventions"],
|
|
142
|
+
commands: sections["🔧 Commands Reference"] || sections["Commands Reference"],
|
|
143
|
+
all: agentsMd,
|
|
144
|
+
};
|
|
145
|
+
const result = sectionMap[section] || agentsMd;
|
|
146
|
+
return {
|
|
147
|
+
content: [
|
|
148
|
+
{
|
|
149
|
+
type: "text",
|
|
150
|
+
text: result,
|
|
151
|
+
},
|
|
152
|
+
],
|
|
153
|
+
};
|
|
154
|
+
});
|
|
155
|
+
// ============================================================================
|
|
156
|
+
// Tool 2: analyze_dependencies
|
|
157
|
+
// ============================================================================
|
|
158
|
+
server.tool("analyze_dependencies", "Get dependency graph for a file or module. Shows imports and files that import this file. Auto-syncs if data is stale.", {
|
|
159
|
+
file_path: z.string().describe("File path to analyze (relative to project root)"),
|
|
160
|
+
direction: z
|
|
161
|
+
.enum(["imports", "imported_by", "both"])
|
|
162
|
+
.optional()
|
|
163
|
+
.default("both")
|
|
164
|
+
.describe("Direction of dependencies to show"),
|
|
165
|
+
depth: z.number().optional().default(2).describe("Depth of traversal"),
|
|
166
|
+
}, async ({ file_path, direction, depth }) => {
|
|
167
|
+
// Auto-sync if data is stale
|
|
168
|
+
const syncStatus = await autoSyncIfNeeded();
|
|
169
|
+
const graph = readJsonFile(".agent/graph.json");
|
|
170
|
+
if (!graph) {
|
|
171
|
+
return {
|
|
172
|
+
content: [
|
|
173
|
+
{
|
|
174
|
+
type: "text",
|
|
175
|
+
text: JSON.stringify({
|
|
176
|
+
error: "Dependency graph not found",
|
|
177
|
+
suggestion: "Run 'ak sync' to generate dependency graph",
|
|
178
|
+
}),
|
|
179
|
+
},
|
|
180
|
+
],
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
// Normalize file path
|
|
184
|
+
const normalizedPath = file_path.replace(/^\.\//, "");
|
|
185
|
+
// Find the node
|
|
186
|
+
const node = graph.nodes?.find((n) => n.id === normalizedPath || n.path === normalizedPath);
|
|
187
|
+
if (!node) {
|
|
188
|
+
return {
|
|
189
|
+
content: [
|
|
190
|
+
{
|
|
191
|
+
type: "text",
|
|
192
|
+
text: JSON.stringify({
|
|
193
|
+
error: `File not found in graph: ${file_path}`,
|
|
194
|
+
available_files: graph.nodes?.slice(0, 10).map((n) => n.id),
|
|
195
|
+
}),
|
|
196
|
+
},
|
|
197
|
+
],
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
// Build result based on direction
|
|
201
|
+
const result = {
|
|
202
|
+
file: normalizedPath,
|
|
203
|
+
type: node.type,
|
|
204
|
+
};
|
|
205
|
+
if (direction === "imports" || direction === "both") {
|
|
206
|
+
result.imports = node.imports || [];
|
|
207
|
+
}
|
|
208
|
+
if (direction === "imported_by" || direction === "both") {
|
|
209
|
+
// Find files that import this file
|
|
210
|
+
const importedBy = graph.edges
|
|
211
|
+
?.filter((e) => e.target === normalizedPath)
|
|
212
|
+
.map((e) => e.source) || [];
|
|
213
|
+
result.imported_by = importedBy;
|
|
214
|
+
}
|
|
215
|
+
// Calculate impact score
|
|
216
|
+
result.impact_score = (result.imported_by?.length || 0);
|
|
217
|
+
return {
|
|
218
|
+
content: [
|
|
219
|
+
{
|
|
220
|
+
type: "text",
|
|
221
|
+
text: JSON.stringify(result, null, 2),
|
|
222
|
+
},
|
|
223
|
+
],
|
|
224
|
+
};
|
|
225
|
+
});
|
|
226
|
+
// ============================================================================
|
|
227
|
+
// Tool 3: search_code_logic
|
|
228
|
+
// ============================================================================
|
|
229
|
+
server.tool("search_code_logic", "Semantic search through code chunks with context. Auto-syncs if data is stale.", {
|
|
230
|
+
query: z.string().describe("Search query describing what you're looking for"),
|
|
231
|
+
file_filter: z.string().optional().describe("Glob pattern to filter files (e.g., '*.ts')"),
|
|
232
|
+
top_k: z.number().optional().default(10).describe("Number of results to return"),
|
|
233
|
+
}, async ({ query, file_filter, top_k }) => {
|
|
234
|
+
// Auto-sync if data is stale
|
|
235
|
+
const syncStatus = await autoSyncIfNeeded();
|
|
236
|
+
const chunks = readJsonFile(".agent/rag/chunks.json");
|
|
237
|
+
if (!chunks || !chunks.chunks) {
|
|
238
|
+
return {
|
|
239
|
+
content: [
|
|
240
|
+
{
|
|
241
|
+
type: "text",
|
|
242
|
+
text: JSON.stringify({
|
|
243
|
+
error: "RAG chunks not found",
|
|
244
|
+
suggestion: "Run 'ak sync' to generate code chunks",
|
|
245
|
+
}),
|
|
246
|
+
},
|
|
247
|
+
],
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
// Simple keyword-based search (in production, use embeddings)
|
|
251
|
+
const queryWords = query.toLowerCase().split(/\s+/);
|
|
252
|
+
const scored = chunks.chunks.map((chunk) => {
|
|
253
|
+
const content = (chunk.content || "").toLowerCase();
|
|
254
|
+
const metadata = chunk.metadata || {};
|
|
255
|
+
// Apply file filter
|
|
256
|
+
if (file_filter) {
|
|
257
|
+
const pattern = file_filter.replace("*", ".*");
|
|
258
|
+
if (!new RegExp(pattern).test(metadata.file_path || "")) {
|
|
259
|
+
return { chunk, score: -1 };
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
// Score based on keyword matches
|
|
263
|
+
let score = 0;
|
|
264
|
+
for (const word of queryWords) {
|
|
265
|
+
if (content.includes(word)) {
|
|
266
|
+
score += 1;
|
|
267
|
+
}
|
|
268
|
+
if ((metadata.name || "").toLowerCase().includes(word)) {
|
|
269
|
+
score += 2; // Boost for name matches
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
return { chunk, score };
|
|
273
|
+
});
|
|
274
|
+
// Sort and take top k
|
|
275
|
+
const results = scored
|
|
276
|
+
.filter((s) => s.score > 0)
|
|
277
|
+
.sort((a, b) => b.score - a.score)
|
|
278
|
+
.slice(0, top_k)
|
|
279
|
+
.map((s) => ({
|
|
280
|
+
file: s.chunk.metadata?.file_path,
|
|
281
|
+
type: s.chunk.metadata?.chunk_type,
|
|
282
|
+
name: s.chunk.metadata?.name,
|
|
283
|
+
lines: `${s.chunk.metadata?.start_line}-${s.chunk.metadata?.end_line}`,
|
|
284
|
+
preview: s.chunk.content?.substring(0, 200) + "...",
|
|
285
|
+
relevance_score: s.score,
|
|
286
|
+
}));
|
|
287
|
+
return {
|
|
288
|
+
content: [
|
|
289
|
+
{
|
|
290
|
+
type: "text",
|
|
291
|
+
text: JSON.stringify({
|
|
292
|
+
query,
|
|
293
|
+
total_results: results.length,
|
|
294
|
+
results,
|
|
295
|
+
}, null, 2),
|
|
296
|
+
},
|
|
297
|
+
],
|
|
298
|
+
};
|
|
299
|
+
});
|
|
300
|
+
// ============================================================================
|
|
301
|
+
// Tool 4: get_impact_zone
|
|
302
|
+
// ============================================================================
|
|
303
|
+
server.tool("get_impact_zone", "Get all files that would be affected if a specific file is modified. Auto-syncs if data is stale.", {
|
|
304
|
+
file_path: z.string().describe("File path to analyze impact for"),
|
|
305
|
+
depth: z.number().optional().default(3).describe("How many levels of dependencies to traverse"),
|
|
306
|
+
}, async ({ file_path, depth }) => {
|
|
307
|
+
// Auto-sync if data is stale
|
|
308
|
+
const syncStatus = await autoSyncIfNeeded();
|
|
309
|
+
const graph = readJsonFile(".agent/graph.json");
|
|
310
|
+
if (!graph) {
|
|
311
|
+
return {
|
|
312
|
+
content: [
|
|
313
|
+
{
|
|
314
|
+
type: "text",
|
|
315
|
+
text: JSON.stringify({
|
|
316
|
+
error: "Dependency graph not found",
|
|
317
|
+
suggestion: "Run 'ak sync' to generate dependency graph",
|
|
318
|
+
}),
|
|
319
|
+
},
|
|
320
|
+
],
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
const normalizedPath = file_path.replace(/^\.\//, "");
|
|
324
|
+
// Build adjacency list for reverse dependencies (who imports this?)
|
|
325
|
+
const reverseGraph = {};
|
|
326
|
+
for (const edge of graph.edges || []) {
|
|
327
|
+
if (!reverseGraph[edge.target]) {
|
|
328
|
+
reverseGraph[edge.target] = [];
|
|
329
|
+
}
|
|
330
|
+
reverseGraph[edge.target].push(edge.source);
|
|
331
|
+
}
|
|
332
|
+
// BFS to find all affected files
|
|
333
|
+
const visited = new Set();
|
|
334
|
+
const queue = [{ file: normalizedPath, level: 0 }];
|
|
335
|
+
const impactByLevel = {};
|
|
336
|
+
while (queue.length > 0) {
|
|
337
|
+
const { file, level } = queue.shift();
|
|
338
|
+
if (visited.has(file) || level > depth) {
|
|
339
|
+
continue;
|
|
340
|
+
}
|
|
341
|
+
visited.add(file);
|
|
342
|
+
if (level > 0) {
|
|
343
|
+
if (!impactByLevel[level]) {
|
|
344
|
+
impactByLevel[level] = [];
|
|
345
|
+
}
|
|
346
|
+
impactByLevel[level].push(file);
|
|
347
|
+
}
|
|
348
|
+
// Add files that import this file
|
|
349
|
+
const dependents = reverseGraph[file] || [];
|
|
350
|
+
for (const dep of dependents) {
|
|
351
|
+
if (!visited.has(dep)) {
|
|
352
|
+
queue.push({ file: dep, level: level + 1 });
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
// Flatten results
|
|
357
|
+
const allAffected = Object.values(impactByLevel).flat();
|
|
358
|
+
return {
|
|
359
|
+
content: [
|
|
360
|
+
{
|
|
361
|
+
type: "text",
|
|
362
|
+
text: JSON.stringify({
|
|
363
|
+
source_file: normalizedPath,
|
|
364
|
+
total_affected: allAffected.length,
|
|
365
|
+
impact_by_level: impactByLevel,
|
|
366
|
+
all_affected_files: allAffected,
|
|
367
|
+
warning: allAffected.length > 10
|
|
368
|
+
? "High impact change! Review carefully."
|
|
369
|
+
: null,
|
|
370
|
+
}, null, 2),
|
|
371
|
+
},
|
|
372
|
+
],
|
|
373
|
+
};
|
|
374
|
+
});
|
|
375
|
+
// ============================================================================
|
|
376
|
+
// Tool 5: force_sync
|
|
377
|
+
// ============================================================================
|
|
378
|
+
server.tool("force_sync", "Force refresh all AI infrastructure data (AGENTS.md, graph, RAG chunks)", {
|
|
379
|
+
target: z
|
|
380
|
+
.enum(["all", "graph", "rag", "agents_md"])
|
|
381
|
+
.optional()
|
|
382
|
+
.default("all")
|
|
383
|
+
.describe("What to sync"),
|
|
384
|
+
}, async ({ target }) => {
|
|
385
|
+
const { spawn } = await import("child_process");
|
|
386
|
+
const results = {};
|
|
387
|
+
const runScript = (scriptPath, args) => {
|
|
388
|
+
return new Promise((resolve, reject) => {
|
|
389
|
+
const proc = spawn("python", [scriptPath, ...args], {
|
|
390
|
+
cwd: PROJECT_ROOT,
|
|
391
|
+
});
|
|
392
|
+
let output = "";
|
|
393
|
+
let error = "";
|
|
394
|
+
proc.stdout.on("data", (data) => {
|
|
395
|
+
output += data.toString();
|
|
396
|
+
});
|
|
397
|
+
proc.stderr.on("data", (data) => {
|
|
398
|
+
error += data.toString();
|
|
399
|
+
});
|
|
400
|
+
proc.on("close", (code) => {
|
|
401
|
+
if (code === 0) {
|
|
402
|
+
resolve(output);
|
|
403
|
+
}
|
|
404
|
+
else {
|
|
405
|
+
resolve(`Error: ${error || output}`);
|
|
406
|
+
}
|
|
407
|
+
});
|
|
408
|
+
proc.on("error", (err) => {
|
|
409
|
+
resolve(`Failed to run: ${err.message}`);
|
|
410
|
+
});
|
|
411
|
+
});
|
|
412
|
+
};
|
|
413
|
+
// Determine script paths (relative to kit installation)
|
|
414
|
+
const kitPath = process.env.AGENT_KIT_PATH || path.join(__dirname, "..", "..");
|
|
415
|
+
if (target === "all" || target === "graph") {
|
|
416
|
+
const graphScript = path.join(kitPath, "skills/graph-mapper/scripts/generate_graph.py");
|
|
417
|
+
if (fs.existsSync(graphScript)) {
|
|
418
|
+
results.graph = await runScript(graphScript, [
|
|
419
|
+
"--src", path.join(PROJECT_ROOT, "src"),
|
|
420
|
+
"--output", path.join(PROJECT_ROOT, ".agent/graph.json"),
|
|
421
|
+
"--format", "both"
|
|
422
|
+
]);
|
|
423
|
+
}
|
|
424
|
+
else {
|
|
425
|
+
results.graph = "Graph script not found";
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
if (target === "all" || target === "rag") {
|
|
429
|
+
const ragScript = path.join(kitPath, "skills/rag-engineering/scripts/chunk_code.py");
|
|
430
|
+
if (fs.existsSync(ragScript)) {
|
|
431
|
+
results.rag = await runScript(ragScript, [
|
|
432
|
+
"--src", path.join(PROJECT_ROOT, "src"),
|
|
433
|
+
"--output", path.join(PROJECT_ROOT, ".agent/rag/chunks.json")
|
|
434
|
+
]);
|
|
435
|
+
}
|
|
436
|
+
else {
|
|
437
|
+
results.rag = "RAG script not found";
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
if (target === "all" || target === "agents_md") {
|
|
441
|
+
const infraScript = path.join(kitPath, "skills/app-builder/scripts/generate_ai_infra.py");
|
|
442
|
+
if (fs.existsSync(infraScript)) {
|
|
443
|
+
results.agents_md = await runScript(infraScript, [
|
|
444
|
+
"--project-root", PROJECT_ROOT
|
|
445
|
+
]);
|
|
446
|
+
}
|
|
447
|
+
else {
|
|
448
|
+
results.agents_md = "AI infra script not found";
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
return {
|
|
452
|
+
content: [
|
|
453
|
+
{
|
|
454
|
+
type: "text",
|
|
455
|
+
text: JSON.stringify({
|
|
456
|
+
status: "Sync completed",
|
|
457
|
+
target,
|
|
458
|
+
results,
|
|
459
|
+
timestamp: new Date().toISOString(),
|
|
460
|
+
}, null, 2),
|
|
461
|
+
},
|
|
462
|
+
],
|
|
463
|
+
};
|
|
464
|
+
});
|
|
465
|
+
// ============================================================================
|
|
466
|
+
// Resource: Project Status
|
|
467
|
+
// ============================================================================
|
|
468
|
+
server.resource("project-status", "status://project", async (uri) => {
|
|
469
|
+
const agentsMd = fs.existsSync(path.join(PROJECT_ROOT, "AGENTS.md"));
|
|
470
|
+
const graphJson = fs.existsSync(path.join(PROJECT_ROOT, ".agent/graph.json"));
|
|
471
|
+
const ragChunks = fs.existsSync(path.join(PROJECT_ROOT, ".agent/rag/chunks.json"));
|
|
472
|
+
const status = {
|
|
473
|
+
project_root: PROJECT_ROOT,
|
|
474
|
+
infrastructure: {
|
|
475
|
+
agents_md: agentsMd ? "✅ Present" : "❌ Missing",
|
|
476
|
+
graph: graphJson ? "✅ Present" : "❌ Missing",
|
|
477
|
+
rag: ragChunks ? "✅ Present" : "❌ Missing",
|
|
478
|
+
},
|
|
479
|
+
last_checked: new Date().toISOString(),
|
|
480
|
+
};
|
|
481
|
+
return {
|
|
482
|
+
contents: [
|
|
483
|
+
{
|
|
484
|
+
uri: uri.href,
|
|
485
|
+
mimeType: "application/json",
|
|
486
|
+
text: JSON.stringify(status, null, 2),
|
|
487
|
+
},
|
|
488
|
+
],
|
|
489
|
+
};
|
|
490
|
+
});
|
|
491
|
+
// ============================================================================
|
|
492
|
+
// Start Server
|
|
493
|
+
// ============================================================================
|
|
494
|
+
async function main() {
|
|
495
|
+
console.error(`Agent Kit MCP Gateway starting...`);
|
|
496
|
+
console.error(`Project root: ${PROJECT_ROOT}`);
|
|
497
|
+
const transport = new StdioServerTransport();
|
|
498
|
+
await server.connect(transport);
|
|
499
|
+
console.error("MCP Gateway ready and listening on stdio");
|
|
500
|
+
}
|
|
501
|
+
main().catch((error) => {
|
|
502
|
+
console.error("Fatal error:", error);
|
|
503
|
+
process.exit(1);
|
|
504
|
+
});
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Debouncer
|
|
3
|
+
*
|
|
4
|
+
* Prevents excessive syncing by enforcing a cooldown period
|
|
5
|
+
* between sync operations. Essential for performance when
|
|
6
|
+
* AI makes many rapid queries.
|
|
7
|
+
*/
|
|
8
|
+
export declare class Debouncer {
|
|
9
|
+
private lastSync;
|
|
10
|
+
private cooldownMs;
|
|
11
|
+
private pendingSync;
|
|
12
|
+
/**
|
|
13
|
+
* @param cooldownMs Minimum time between syncs (default: 30 seconds)
|
|
14
|
+
*/
|
|
15
|
+
constructor(cooldownMs?: number);
|
|
16
|
+
/**
|
|
17
|
+
* Check if enough time has passed since last sync
|
|
18
|
+
*/
|
|
19
|
+
canSync(): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Get remaining cooldown time in milliseconds
|
|
22
|
+
*/
|
|
23
|
+
getRemainingCooldown(): number;
|
|
24
|
+
/**
|
|
25
|
+
* Get remaining cooldown time in human-readable format
|
|
26
|
+
*/
|
|
27
|
+
getRemainingCooldownFormatted(): string;
|
|
28
|
+
/**
|
|
29
|
+
* Mark that a sync has occurred
|
|
30
|
+
*/
|
|
31
|
+
markSynced(): void;
|
|
32
|
+
/**
|
|
33
|
+
* Execute a sync operation with debouncing
|
|
34
|
+
*
|
|
35
|
+
* @param syncFn The sync function to execute
|
|
36
|
+
* @returns Result of sync or null if debounced
|
|
37
|
+
*/
|
|
38
|
+
executeWithDebounce<T>(syncFn: () => Promise<T>): Promise<{
|
|
39
|
+
executed: boolean;
|
|
40
|
+
result?: T;
|
|
41
|
+
reason?: string;
|
|
42
|
+
}>;
|
|
43
|
+
/**
|
|
44
|
+
* Force reset the debouncer (for manual sync commands)
|
|
45
|
+
*/
|
|
46
|
+
reset(): void;
|
|
47
|
+
/**
|
|
48
|
+
* Get debouncer status
|
|
49
|
+
*/
|
|
50
|
+
getStatus(): {
|
|
51
|
+
lastSync: string;
|
|
52
|
+
canSync: boolean;
|
|
53
|
+
cooldownRemaining: string;
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
export declare function getGlobalDebouncer(cooldownMs?: number): Debouncer;
|