@musashishao/agent-kit 1.4.0 → 1.5.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.
|
@@ -3,10 +3,7 @@
|
|
|
3
3
|
* Agent Kit MCP Gateway Server
|
|
4
4
|
*
|
|
5
5
|
* Provides AI agents with live access to project context, dependency graphs,
|
|
6
|
-
* and semantic
|
|
7
|
-
*
|
|
8
|
-
* Usage:
|
|
9
|
-
* node dist/index.js [--project-root /path/to/project]
|
|
6
|
+
* and semantic search with automatic sync capabilities.
|
|
10
7
|
*/
|
|
11
8
|
|
|
12
9
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
@@ -31,14 +28,11 @@ const timestampChecker = new TimestampChecker(PROJECT_ROOT);
|
|
|
31
28
|
// ============================================================================
|
|
32
29
|
|
|
33
30
|
async function autoSyncIfNeeded(): Promise<{ synced: boolean; message: string }> {
|
|
34
|
-
// Check if data is stale
|
|
35
31
|
const status = timestampChecker.getSyncStatus();
|
|
36
|
-
|
|
37
32
|
if (!status.needsSync) {
|
|
38
33
|
return { synced: false, message: "Data is fresh" };
|
|
39
34
|
}
|
|
40
35
|
|
|
41
|
-
// Try to sync with debouncing
|
|
42
36
|
const result = await debouncer.executeWithDebounce(async () => {
|
|
43
37
|
return await syncer.syncAll();
|
|
44
38
|
});
|
|
@@ -57,35 +51,17 @@ async function autoSyncIfNeeded(): Promise<{ synced: boolean; message: string }>
|
|
|
57
51
|
function readJsonFile(filePath: string): any {
|
|
58
52
|
try {
|
|
59
53
|
const fullPath = path.resolve(PROJECT_ROOT, filePath);
|
|
60
|
-
if (!fs.existsSync(fullPath))
|
|
61
|
-
return null;
|
|
62
|
-
}
|
|
54
|
+
if (!fs.existsSync(fullPath)) return null;
|
|
63
55
|
return JSON.parse(fs.readFileSync(fullPath, "utf-8"));
|
|
64
|
-
} catch
|
|
65
|
-
return null;
|
|
66
|
-
}
|
|
56
|
+
} catch { return null; }
|
|
67
57
|
}
|
|
68
58
|
|
|
69
59
|
function readTextFile(filePath: string): string | null {
|
|
70
60
|
try {
|
|
71
61
|
const fullPath = path.resolve(PROJECT_ROOT, filePath);
|
|
72
|
-
if (!fs.existsSync(fullPath))
|
|
73
|
-
return null;
|
|
74
|
-
}
|
|
62
|
+
if (!fs.existsSync(fullPath)) return null;
|
|
75
63
|
return fs.readFileSync(fullPath, "utf-8");
|
|
76
|
-
} catch
|
|
77
|
-
return null;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function getFileModTime(filePath: string): number {
|
|
82
|
-
try {
|
|
83
|
-
const fullPath = path.resolve(PROJECT_ROOT, filePath);
|
|
84
|
-
const stats = fs.statSync(fullPath);
|
|
85
|
-
return stats.mtimeMs;
|
|
86
|
-
} catch {
|
|
87
|
-
return 0;
|
|
88
|
-
}
|
|
64
|
+
} catch { return null; }
|
|
89
65
|
}
|
|
90
66
|
|
|
91
67
|
// ============================================================================
|
|
@@ -94,68 +70,43 @@ function getFileModTime(filePath: string): number {
|
|
|
94
70
|
|
|
95
71
|
const server = new McpServer({
|
|
96
72
|
name: "agent-kit-gateway",
|
|
97
|
-
version: "1.
|
|
73
|
+
version: "1.1.0", // Universal Intelligence Version
|
|
98
74
|
});
|
|
99
75
|
|
|
100
76
|
// ============================================================================
|
|
101
|
-
// Tool
|
|
77
|
+
// Tool: get_project_context
|
|
102
78
|
// ============================================================================
|
|
103
79
|
|
|
104
80
|
server.tool(
|
|
105
81
|
"get_project_context",
|
|
106
82
|
"Get project overview including tech stack, structure, and conventions from AGENTS.md",
|
|
107
83
|
{
|
|
108
|
-
section: z
|
|
109
|
-
.enum(["all", "tech_stack", "structure", "conventions", "commands"])
|
|
110
|
-
.optional()
|
|
111
|
-
.default("all")
|
|
112
|
-
.describe("Specific section to retrieve"),
|
|
84
|
+
section: z.enum(["all", "tech_stack", "structure", "conventions", "commands"]).optional().default("all")
|
|
113
85
|
},
|
|
114
86
|
async ({ section }) => {
|
|
115
87
|
const agentsMd = readTextFile("AGENTS.md");
|
|
116
|
-
|
|
117
88
|
if (!agentsMd) {
|
|
118
89
|
return {
|
|
119
|
-
content: [
|
|
120
|
-
{
|
|
121
|
-
type: "text",
|
|
122
|
-
text: JSON.stringify({
|
|
123
|
-
error: "AGENTS.md not found",
|
|
124
|
-
suggestion: "Run 'ak init' to generate AI infrastructure",
|
|
125
|
-
project_root: PROJECT_ROOT,
|
|
126
|
-
}),
|
|
127
|
-
},
|
|
128
|
-
],
|
|
90
|
+
content: [{ type: "text" as const, text: "AGENTS.md not found. Run 'ak init' first." }]
|
|
129
91
|
};
|
|
130
92
|
}
|
|
131
93
|
|
|
132
|
-
// Parse sections from AGENTS.md
|
|
133
94
|
const sections: Record<string, string> = {};
|
|
134
|
-
const sectionRegex = /^## (.+)$/gm;
|
|
135
|
-
let matches;
|
|
136
|
-
let lastIndex = 0;
|
|
137
|
-
let lastSection = "";
|
|
138
|
-
|
|
139
95
|
const lines = agentsMd.split("\n");
|
|
140
96
|
let currentSection = "";
|
|
141
97
|
let currentContent: string[] = [];
|
|
142
98
|
|
|
143
99
|
for (const line of lines) {
|
|
144
100
|
if (line.startsWith("## ")) {
|
|
145
|
-
if (currentSection)
|
|
146
|
-
sections[currentSection] = currentContent.join("\n").trim();
|
|
147
|
-
}
|
|
101
|
+
if (currentSection) sections[currentSection] = currentContent.join("\n").trim();
|
|
148
102
|
currentSection = line.replace("## ", "").trim();
|
|
149
103
|
currentContent = [];
|
|
150
104
|
} else {
|
|
151
105
|
currentContent.push(line);
|
|
152
106
|
}
|
|
153
107
|
}
|
|
154
|
-
if (currentSection)
|
|
155
|
-
sections[currentSection] = currentContent.join("\n").trim();
|
|
156
|
-
}
|
|
108
|
+
if (currentSection) sections[currentSection] = currentContent.join("\n").trim();
|
|
157
109
|
|
|
158
|
-
// Map section names to content
|
|
159
110
|
const sectionMap: Record<string, string | undefined> = {
|
|
160
111
|
tech_stack: sections["🛠️ Tech Stack"] || sections["Tech Stack"],
|
|
161
112
|
structure: sections["📁 Directory Map"] || sections["Directory Map"],
|
|
@@ -164,427 +115,143 @@ server.tool(
|
|
|
164
115
|
all: agentsMd,
|
|
165
116
|
};
|
|
166
117
|
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
return {
|
|
170
|
-
content: [
|
|
171
|
-
{
|
|
172
|
-
type: "text",
|
|
173
|
-
text: result,
|
|
174
|
-
},
|
|
175
|
-
],
|
|
176
|
-
};
|
|
118
|
+
return { content: [{ type: "text" as const, text: sectionMap[section] || agentsMd }] };
|
|
177
119
|
}
|
|
178
120
|
);
|
|
179
121
|
|
|
180
122
|
// ============================================================================
|
|
181
|
-
// Tool
|
|
123
|
+
// Tool: get_project_intelligence
|
|
182
124
|
// ============================================================================
|
|
183
125
|
|
|
184
126
|
server.tool(
|
|
185
|
-
"
|
|
186
|
-
"Get
|
|
187
|
-
{
|
|
188
|
-
|
|
189
|
-
direction: z
|
|
190
|
-
.enum(["imports", "imported_by", "both"])
|
|
191
|
-
.optional()
|
|
192
|
-
.default("both")
|
|
193
|
-
.describe("Direction of dependencies to show"),
|
|
194
|
-
depth: z.number().optional().default(2).describe("Depth of traversal"),
|
|
195
|
-
},
|
|
196
|
-
async ({ file_path, direction, depth }) => {
|
|
197
|
-
// Auto-sync if data is stale
|
|
198
|
-
const syncStatus = await autoSyncIfNeeded();
|
|
199
|
-
|
|
127
|
+
"get_project_intelligence",
|
|
128
|
+
"Get high-level project summary: Code vs Docs distribution, primary types, and impact analysis.",
|
|
129
|
+
{},
|
|
130
|
+
async () => {
|
|
200
131
|
const graph = readJsonFile(".agent/graph.json");
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
};
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// Normalize file path
|
|
217
|
-
const normalizedPath = file_path.replace(/^\.\//, "");
|
|
218
|
-
|
|
219
|
-
// Find the node
|
|
220
|
-
const node = graph.nodes?.find(
|
|
221
|
-
(n: any) => n.id === normalizedPath || n.path === normalizedPath
|
|
222
|
-
);
|
|
223
|
-
|
|
224
|
-
if (!node) {
|
|
225
|
-
return {
|
|
226
|
-
content: [
|
|
227
|
-
{
|
|
228
|
-
type: "text",
|
|
229
|
-
text: JSON.stringify({
|
|
230
|
-
error: `File not found in graph: ${file_path}`,
|
|
231
|
-
available_files: graph.nodes?.slice(0, 10).map((n: any) => n.id),
|
|
232
|
-
}),
|
|
233
|
-
},
|
|
234
|
-
],
|
|
235
|
-
};
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// Build result based on direction
|
|
239
|
-
const result: any = {
|
|
240
|
-
file: normalizedPath,
|
|
241
|
-
type: node.type,
|
|
132
|
+
if (!graph) return { content: [{ type: "text" as const, text: "Project data not generated." }] };
|
|
133
|
+
|
|
134
|
+
const stats = graph.metadata?.file_types || {};
|
|
135
|
+
const total = graph.metadata?.total_files || 1;
|
|
136
|
+
const docCount = (stats.documentation || 0) + (stats.document || 0) + (stats.readme || 0);
|
|
137
|
+
const codeCount = (stats.component || 0) + (stats.module || 0) + (stats.utility || 0);
|
|
138
|
+
|
|
139
|
+
const intelligence = {
|
|
140
|
+
project_name: path.basename(PROJECT_ROOT),
|
|
141
|
+
project_type: docCount > codeCount ? "Documentation-Heavy" : "Code-Centric",
|
|
142
|
+
distribution: { docs: `${Math.round((docCount / total) * 100)}%`, code: `${Math.round((codeCount / total) * 100)}%` },
|
|
143
|
+
top_files: graph.nodes?.slice(0, 5).map((n: any) => n.id)
|
|
242
144
|
};
|
|
243
145
|
|
|
244
|
-
|
|
245
|
-
result.imports = node.imports || [];
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
if (direction === "imported_by" || direction === "both") {
|
|
249
|
-
// Find files that import this file
|
|
250
|
-
const importedBy = graph.edges
|
|
251
|
-
?.filter((e: any) => e.target === normalizedPath)
|
|
252
|
-
.map((e: any) => e.source) || [];
|
|
253
|
-
result.imported_by = importedBy;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
// Calculate impact score
|
|
257
|
-
result.impact_score = (result.imported_by?.length || 0);
|
|
258
|
-
|
|
259
|
-
return {
|
|
260
|
-
content: [
|
|
261
|
-
{
|
|
262
|
-
type: "text",
|
|
263
|
-
text: JSON.stringify(result, null, 2),
|
|
264
|
-
},
|
|
265
|
-
],
|
|
266
|
-
};
|
|
146
|
+
return { content: [{ type: "text" as const, text: JSON.stringify(intelligence, null, 2) }] };
|
|
267
147
|
}
|
|
268
148
|
);
|
|
269
149
|
|
|
270
150
|
// ============================================================================
|
|
271
|
-
// Tool
|
|
151
|
+
// Tool: analyze_dependencies
|
|
272
152
|
// ============================================================================
|
|
273
153
|
|
|
274
154
|
server.tool(
|
|
275
|
-
"
|
|
276
|
-
"
|
|
155
|
+
"analyze_dependencies",
|
|
156
|
+
"Analyze imports/references for a file.",
|
|
277
157
|
{
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
top_k: z.number().optional().default(10).describe("Number of results to return"),
|
|
158
|
+
file_path: z.string(),
|
|
159
|
+
direction: z.enum(["imports", "imported_by", "both"]).optional().default("both")
|
|
281
160
|
},
|
|
282
|
-
async ({
|
|
283
|
-
|
|
284
|
-
const
|
|
285
|
-
|
|
286
|
-
const chunks = readJsonFile(".agent/rag/chunks.json");
|
|
287
|
-
|
|
288
|
-
if (!chunks || !chunks.chunks) {
|
|
289
|
-
return {
|
|
290
|
-
content: [
|
|
291
|
-
{
|
|
292
|
-
type: "text",
|
|
293
|
-
text: JSON.stringify({
|
|
294
|
-
error: "RAG chunks not found",
|
|
295
|
-
suggestion: "Run 'ak sync' to generate code chunks",
|
|
296
|
-
}),
|
|
297
|
-
},
|
|
298
|
-
],
|
|
299
|
-
};
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
// Simple keyword-based search (in production, use embeddings)
|
|
303
|
-
const queryWords = query.toLowerCase().split(/\s+/);
|
|
161
|
+
async ({ file_path, direction }) => {
|
|
162
|
+
await autoSyncIfNeeded();
|
|
163
|
+
const graph = readJsonFile(".agent/graph.json");
|
|
164
|
+
if (!graph) return { content: [{ type: "text" as const, text: "Graph not found." }] };
|
|
304
165
|
|
|
305
|
-
const
|
|
306
|
-
|
|
307
|
-
|
|
166
|
+
const normalizedPath = file_path.replace(/^\.\//, "");
|
|
167
|
+
const node = graph.nodes?.find((n: any) => n.id === normalizedPath);
|
|
168
|
+
if (!node) return { content: [{ type: "text" as const, text: "File not found in graph." }] };
|
|
308
169
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
if (!new RegExp(pattern).test(metadata.file_path || "")) {
|
|
313
|
-
return { chunk, score: -1 };
|
|
314
|
-
}
|
|
315
|
-
}
|
|
170
|
+
const isDoc = ["documentation", "readme", "document"].includes(node.type);
|
|
171
|
+
const outLabel = isDoc ? "references" : "imports";
|
|
172
|
+
const inLabel = isDoc ? "referenced_by" : "imported_by";
|
|
316
173
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
}
|
|
323
|
-
if ((metadata.name || "").toLowerCase().includes(word)) {
|
|
324
|
-
score += 2; // Boost for name matches
|
|
325
|
-
}
|
|
326
|
-
}
|
|
174
|
+
const result: any = { file: normalizedPath, type: node.type };
|
|
175
|
+
if (direction !== "imported_by") result[outLabel] = node.imports || [];
|
|
176
|
+
if (direction !== "imports") {
|
|
177
|
+
result[inLabel] = graph.edges?.filter((e: any) => e.target === normalizedPath).map((e: any) => e.source) || [];
|
|
178
|
+
}
|
|
327
179
|
|
|
328
|
-
|
|
329
|
-
});
|
|
330
|
-
|
|
331
|
-
// Sort and take top k
|
|
332
|
-
const results = scored
|
|
333
|
-
.filter((s: any) => s.score > 0)
|
|
334
|
-
.sort((a: any, b: any) => b.score - a.score)
|
|
335
|
-
.slice(0, top_k)
|
|
336
|
-
.map((s: any) => ({
|
|
337
|
-
file: s.chunk.metadata?.file_path,
|
|
338
|
-
type: s.chunk.metadata?.chunk_type,
|
|
339
|
-
name: s.chunk.metadata?.name,
|
|
340
|
-
lines: `${s.chunk.metadata?.start_line}-${s.chunk.metadata?.end_line}`,
|
|
341
|
-
preview: s.chunk.content?.substring(0, 200) + "...",
|
|
342
|
-
relevance_score: s.score,
|
|
343
|
-
}));
|
|
344
|
-
|
|
345
|
-
return {
|
|
346
|
-
content: [
|
|
347
|
-
{
|
|
348
|
-
type: "text",
|
|
349
|
-
text: JSON.stringify({
|
|
350
|
-
query,
|
|
351
|
-
total_results: results.length,
|
|
352
|
-
results,
|
|
353
|
-
}, null, 2),
|
|
354
|
-
},
|
|
355
|
-
],
|
|
356
|
-
};
|
|
180
|
+
return { content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }] };
|
|
357
181
|
}
|
|
358
182
|
);
|
|
359
183
|
|
|
360
184
|
// ============================================================================
|
|
361
|
-
// Tool
|
|
185
|
+
// Tool: search_knowledge (Search Engine)
|
|
362
186
|
// ============================================================================
|
|
363
187
|
|
|
188
|
+
async function handleSearch(args: { query: string, file_filter?: string, top_k: number }) {
|
|
189
|
+
await autoSyncIfNeeded();
|
|
190
|
+
const chunks = readJsonFile(".agent/rag/chunks.json");
|
|
191
|
+
if (!chunks?.chunks) return { content: [{ type: "text" as const, text: "No chunks found." }] };
|
|
192
|
+
|
|
193
|
+
const query = args.query.toLowerCase();
|
|
194
|
+
const results = chunks.chunks
|
|
195
|
+
.map((chunk: any) => {
|
|
196
|
+
let score = 0;
|
|
197
|
+
const content = chunk.content.toLowerCase();
|
|
198
|
+
if (content.includes(query)) score += 5;
|
|
199
|
+
if (chunk.metadata.name?.toLowerCase().includes(query)) score += 10;
|
|
200
|
+
return { chunk, score };
|
|
201
|
+
})
|
|
202
|
+
.filter((s: any) => s.score > 0)
|
|
203
|
+
.sort((a: any, b: any) => b.score - a.score)
|
|
204
|
+
.slice(0, args.top_k)
|
|
205
|
+
.map((s: any) => ({
|
|
206
|
+
file: s.chunk.metadata.file_path,
|
|
207
|
+
context: s.chunk.metadata.context_path || s.chunk.metadata.name,
|
|
208
|
+
preview: s.chunk.content.substring(0, 250) + "..."
|
|
209
|
+
}));
|
|
210
|
+
|
|
211
|
+
return { content: [{ type: "text" as const, text: JSON.stringify(results, null, 2) }] };
|
|
212
|
+
}
|
|
213
|
+
|
|
364
214
|
server.tool(
|
|
365
|
-
"
|
|
366
|
-
"
|
|
215
|
+
"search_knowledge",
|
|
216
|
+
"Universal search through project knowledge (Code + Docs).",
|
|
367
217
|
{
|
|
368
|
-
|
|
369
|
-
|
|
218
|
+
query: z.string(),
|
|
219
|
+
file_filter: z.string().optional(),
|
|
220
|
+
top_k: z.number().optional().default(10)
|
|
370
221
|
},
|
|
371
|
-
async (
|
|
372
|
-
// Auto-sync if data is stale
|
|
373
|
-
const syncStatus = await autoSyncIfNeeded();
|
|
374
|
-
|
|
375
|
-
const graph = readJsonFile(".agent/graph.json");
|
|
376
|
-
|
|
377
|
-
if (!graph) {
|
|
378
|
-
return {
|
|
379
|
-
content: [
|
|
380
|
-
{
|
|
381
|
-
type: "text",
|
|
382
|
-
text: JSON.stringify({
|
|
383
|
-
error: "Dependency graph not found",
|
|
384
|
-
suggestion: "Run 'ak sync' to generate dependency graph",
|
|
385
|
-
}),
|
|
386
|
-
},
|
|
387
|
-
],
|
|
388
|
-
};
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
const normalizedPath = file_path.replace(/^\.\//, "");
|
|
392
|
-
|
|
393
|
-
// Build adjacency list for reverse dependencies (who imports this?)
|
|
394
|
-
const reverseGraph: Record<string, string[]> = {};
|
|
395
|
-
for (const edge of graph.edges || []) {
|
|
396
|
-
if (!reverseGraph[edge.target]) {
|
|
397
|
-
reverseGraph[edge.target] = [];
|
|
398
|
-
}
|
|
399
|
-
reverseGraph[edge.target].push(edge.source);
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
// BFS to find all affected files
|
|
403
|
-
const visited = new Set<string>();
|
|
404
|
-
const queue: { file: string; level: number }[] = [{ file: normalizedPath, level: 0 }];
|
|
405
|
-
const impactByLevel: Record<number, string[]> = {};
|
|
406
|
-
|
|
407
|
-
while (queue.length > 0) {
|
|
408
|
-
const { file, level } = queue.shift()!;
|
|
409
|
-
|
|
410
|
-
if (visited.has(file) || level > depth) {
|
|
411
|
-
continue;
|
|
412
|
-
}
|
|
413
|
-
visited.add(file);
|
|
414
|
-
|
|
415
|
-
if (level > 0) {
|
|
416
|
-
if (!impactByLevel[level]) {
|
|
417
|
-
impactByLevel[level] = [];
|
|
418
|
-
}
|
|
419
|
-
impactByLevel[level].push(file);
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
// Add files that import this file
|
|
423
|
-
const dependents = reverseGraph[file] || [];
|
|
424
|
-
for (const dep of dependents) {
|
|
425
|
-
if (!visited.has(dep)) {
|
|
426
|
-
queue.push({ file: dep, level: level + 1 });
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
// Flatten results
|
|
432
|
-
const allAffected = Object.values(impactByLevel).flat();
|
|
433
|
-
|
|
434
|
-
return {
|
|
435
|
-
content: [
|
|
436
|
-
{
|
|
437
|
-
type: "text",
|
|
438
|
-
text: JSON.stringify({
|
|
439
|
-
source_file: normalizedPath,
|
|
440
|
-
total_affected: allAffected.length,
|
|
441
|
-
impact_by_level: impactByLevel,
|
|
442
|
-
all_affected_files: allAffected,
|
|
443
|
-
warning: allAffected.length > 10
|
|
444
|
-
? "High impact change! Review carefully."
|
|
445
|
-
: null,
|
|
446
|
-
}, null, 2),
|
|
447
|
-
},
|
|
448
|
-
],
|
|
449
|
-
};
|
|
450
|
-
}
|
|
222
|
+
async (args) => await handleSearch(args)
|
|
451
223
|
);
|
|
452
224
|
|
|
453
|
-
// ============================================================================
|
|
454
|
-
// Tool 5: force_sync
|
|
455
|
-
// ============================================================================
|
|
456
|
-
|
|
457
225
|
server.tool(
|
|
458
|
-
"
|
|
459
|
-
"
|
|
226
|
+
"search_code_logic",
|
|
227
|
+
"Alias for search_knowledge.",
|
|
460
228
|
{
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
.default("all")
|
|
465
|
-
.describe("What to sync"),
|
|
229
|
+
query: z.string(),
|
|
230
|
+
file_filter: z.string().optional(),
|
|
231
|
+
top_k: z.number().optional().default(10)
|
|
466
232
|
},
|
|
467
|
-
async (
|
|
468
|
-
const { spawn } = await import("child_process");
|
|
469
|
-
const results: Record<string, string> = {};
|
|
470
|
-
|
|
471
|
-
const runScript = (scriptPath: string, args: string[]): Promise<string> => {
|
|
472
|
-
return new Promise((resolve, reject) => {
|
|
473
|
-
const proc = spawn("python", [scriptPath, ...args], {
|
|
474
|
-
cwd: PROJECT_ROOT,
|
|
475
|
-
});
|
|
476
|
-
|
|
477
|
-
let output = "";
|
|
478
|
-
let error = "";
|
|
479
|
-
|
|
480
|
-
proc.stdout.on("data", (data) => {
|
|
481
|
-
output += data.toString();
|
|
482
|
-
});
|
|
483
|
-
|
|
484
|
-
proc.stderr.on("data", (data) => {
|
|
485
|
-
error += data.toString();
|
|
486
|
-
});
|
|
487
|
-
|
|
488
|
-
proc.on("close", (code) => {
|
|
489
|
-
if (code === 0) {
|
|
490
|
-
resolve(output);
|
|
491
|
-
} else {
|
|
492
|
-
resolve(`Error: ${error || output}`);
|
|
493
|
-
}
|
|
494
|
-
});
|
|
495
|
-
|
|
496
|
-
proc.on("error", (err) => {
|
|
497
|
-
resolve(`Failed to run: ${err.message}`);
|
|
498
|
-
});
|
|
499
|
-
});
|
|
500
|
-
};
|
|
501
|
-
|
|
502
|
-
// Determine script paths (relative to kit installation)
|
|
503
|
-
const kitPath = process.env.AGENT_KIT_PATH || path.join(__dirname, "..", "..");
|
|
504
|
-
|
|
505
|
-
if (target === "all" || target === "graph") {
|
|
506
|
-
const graphScript = path.join(kitPath, "skills/graph-mapper/scripts/generate_graph.py");
|
|
507
|
-
if (fs.existsSync(graphScript)) {
|
|
508
|
-
results.graph = await runScript(graphScript, [
|
|
509
|
-
"--src", path.join(PROJECT_ROOT, "src"),
|
|
510
|
-
"--output", path.join(PROJECT_ROOT, ".agent/graph.json"),
|
|
511
|
-
"--format", "both"
|
|
512
|
-
]);
|
|
513
|
-
} else {
|
|
514
|
-
results.graph = "Graph script not found";
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
if (target === "all" || target === "rag") {
|
|
519
|
-
const ragScript = path.join(kitPath, "skills/rag-engineering/scripts/chunk_code.py");
|
|
520
|
-
if (fs.existsSync(ragScript)) {
|
|
521
|
-
results.rag = await runScript(ragScript, [
|
|
522
|
-
"--src", path.join(PROJECT_ROOT, "src"),
|
|
523
|
-
"--output", path.join(PROJECT_ROOT, ".agent/rag/chunks.json")
|
|
524
|
-
]);
|
|
525
|
-
} else {
|
|
526
|
-
results.rag = "RAG script not found";
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
if (target === "all" || target === "agents_md") {
|
|
531
|
-
const infraScript = path.join(kitPath, "skills/app-builder/scripts/generate_ai_infra.py");
|
|
532
|
-
if (fs.existsSync(infraScript)) {
|
|
533
|
-
results.agents_md = await runScript(infraScript, [
|
|
534
|
-
"--project-root", PROJECT_ROOT
|
|
535
|
-
]);
|
|
536
|
-
} else {
|
|
537
|
-
results.agents_md = "AI infra script not found";
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
return {
|
|
542
|
-
content: [
|
|
543
|
-
{
|
|
544
|
-
type: "text",
|
|
545
|
-
text: JSON.stringify({
|
|
546
|
-
status: "Sync completed",
|
|
547
|
-
target,
|
|
548
|
-
results,
|
|
549
|
-
timestamp: new Date().toISOString(),
|
|
550
|
-
}, null, 2),
|
|
551
|
-
},
|
|
552
|
-
],
|
|
553
|
-
};
|
|
554
|
-
}
|
|
233
|
+
async (args) => await handleSearch(args)
|
|
555
234
|
);
|
|
556
235
|
|
|
557
236
|
// ============================================================================
|
|
558
|
-
//
|
|
237
|
+
// Tool: get_impact_zone
|
|
559
238
|
// ============================================================================
|
|
560
239
|
|
|
561
|
-
server.
|
|
562
|
-
"
|
|
563
|
-
"
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
const
|
|
568
|
-
|
|
569
|
-
const status = {
|
|
570
|
-
project_root: PROJECT_ROOT,
|
|
571
|
-
infrastructure: {
|
|
572
|
-
agents_md: agentsMd ? "✅ Present" : "❌ Missing",
|
|
573
|
-
graph: graphJson ? "✅ Present" : "❌ Missing",
|
|
574
|
-
rag: ragChunks ? "✅ Present" : "❌ Missing",
|
|
575
|
-
},
|
|
576
|
-
last_checked: new Date().toISOString(),
|
|
577
|
-
};
|
|
240
|
+
server.tool(
|
|
241
|
+
"get_impact_zone",
|
|
242
|
+
"Find files affected by changing a file.",
|
|
243
|
+
{ file_path: z.string() },
|
|
244
|
+
async ({ file_path }) => {
|
|
245
|
+
await autoSyncIfNeeded();
|
|
246
|
+
const graph = readJsonFile(".agent/graph.json");
|
|
247
|
+
if (!graph) return { content: [{ type: "text" as const, text: "Graph not found." }] };
|
|
578
248
|
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
},
|
|
586
|
-
],
|
|
587
|
-
};
|
|
249
|
+
const normalizedPath = file_path.replace(/^\.\//, "");
|
|
250
|
+
const affected = graph.edges
|
|
251
|
+
?.filter((e: any) => e.target === normalizedPath)
|
|
252
|
+
.map((e: any) => e.source) || [];
|
|
253
|
+
|
|
254
|
+
return { content: [{ type: "text" as const, text: JSON.stringify({ file: normalizedPath, affected }, null, 2) }] };
|
|
588
255
|
}
|
|
589
256
|
);
|
|
590
257
|
|
|
@@ -593,16 +260,11 @@ server.resource(
|
|
|
593
260
|
// ============================================================================
|
|
594
261
|
|
|
595
262
|
async function main() {
|
|
596
|
-
console.error(`Agent Kit MCP Gateway starting...`);
|
|
597
|
-
console.error(`Project root: ${PROJECT_ROOT}`);
|
|
598
|
-
|
|
599
263
|
const transport = new StdioServerTransport();
|
|
600
264
|
await server.connect(transport);
|
|
601
|
-
|
|
602
|
-
console.error("MCP Gateway ready and listening on stdio");
|
|
603
265
|
}
|
|
604
266
|
|
|
605
|
-
main().catch(
|
|
606
|
-
console.error(
|
|
267
|
+
main().catch(err => {
|
|
268
|
+
console.error(err);
|
|
607
269
|
process.exit(1);
|
|
608
270
|
});
|