ai-enderun 0.0.3 → 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.
|
@@ -103,3 +103,11 @@ Bu dosya, projenin tek gerçeklik kaynağı (Single Source of Truth) ve kalıcı
|
|
|
103
103
|
- **Yapılan:** npm yayınlama hatası (0.0.2 zaten mevcut) üzerine tüm paketlerin versiyonu `0.0.3` olarak güncellendi.
|
|
104
104
|
- **Karar:** Yayın hatalarını gidermek için tüm monorepo paketlerinin versiyonları senkronize edildi.
|
|
105
105
|
- **Sonraki Adım:** Yeniden `npm publish` yapılması.
|
|
106
|
+
|
|
107
|
+
### 2026-05-07 — Eksik Kaynak Dosyaları ve v0.0.4 Yayını
|
|
108
|
+
|
|
109
|
+
- **Ajan:** @manager
|
|
110
|
+
- **Trace ID:** 18969447-95fb-4a8f-b0af-18336c3f1931
|
|
111
|
+
- **Yapılan:** `pnpm dev` sırasında yaşanan `ERR_MODULE_NOT_FOUND` hatasını gidermek için `src` klasörleri yayın dosyalarına dahil edildi. Tüm paketler v0.0.4'e yükseltildi.
|
|
112
|
+
- **Karar:** Geliştirme modunda kaynak dosyalarına erişim gerekliliği nedeniyle `src` klasörlerinin pakete dahil edilmesine karar verildi.
|
|
113
|
+
- **Sonraki Adım:** Git push ve npm publish.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-enderun",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "Supreme AI Orchestration Framework — Senior Discipline & Ottoman Wisdom",
|
|
5
5
|
"author": "Yusuf BEKAR <ybekar@msn.com>",
|
|
6
6
|
"repository": {
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
},
|
|
15
15
|
"files": [
|
|
16
16
|
"bin",
|
|
17
|
+
"packages/framework-mcp/src",
|
|
17
18
|
"packages/framework-mcp/dist",
|
|
18
19
|
"packages/framework-mcp/package.json",
|
|
19
20
|
"packages/framework-mcp/README.md",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-enderun-mcp",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "Enterprise-grade MCP Server for AI Agent Framework",
|
|
5
5
|
"author": "Yusuf BEKAR <ybekar@msn.com>",
|
|
6
6
|
"repository": {
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"framework-mcp": "dist/index.js"
|
|
16
16
|
},
|
|
17
17
|
"files": [
|
|
18
|
+
"src",
|
|
18
19
|
"dist",
|
|
19
20
|
"README.md"
|
|
20
21
|
],
|
|
@@ -0,0 +1,759 @@
|
|
|
1
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import {
|
|
4
|
+
CallToolRequestSchema,
|
|
5
|
+
ListToolsRequestSchema,
|
|
6
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import path from "path";
|
|
9
|
+
import fs from "fs";
|
|
10
|
+
|
|
11
|
+
const server = new Server(
|
|
12
|
+
{
|
|
13
|
+
name: "ai-agent-framework-mcp",
|
|
14
|
+
version: "0.0.2",
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
capabilities: {
|
|
18
|
+
tools: {},
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
const SECURITY_AUDIT_ARGS_SCHEMA = z.object({
|
|
24
|
+
path: z.string().default("."),
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const SEARCH_CODEBASE_ARGS_SCHEMA = z.object({
|
|
28
|
+
query: z.string().min(1).max(300),
|
|
29
|
+
extension: z
|
|
30
|
+
.string()
|
|
31
|
+
.regex(/^[a-z0-9]+$/i)
|
|
32
|
+
.default("ts"),
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const UPDATE_MEMORY_ARGS_SCHEMA = z.object({
|
|
36
|
+
section: z.enum(["MEVCUT DURUM", "HISTORY", "AKTİF GÖREVLER"]),
|
|
37
|
+
content: z.string().min(1),
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const ANALYZE_DEPENDENCIES_ARGS_SCHEMA = z.object({
|
|
41
|
+
path: z.string().min(1),
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const FRAMEWORK_VERSION = "0.0.2";
|
|
45
|
+
|
|
46
|
+
function resolveSafePath(projectRoot: string, targetPath: string): string {
|
|
47
|
+
const resolved = path.resolve(projectRoot, targetPath);
|
|
48
|
+
const relative = path.relative(projectRoot, resolved);
|
|
49
|
+
if (relative.startsWith("..") || path.isAbsolute(relative)) {
|
|
50
|
+
throw new Error("Path escapes project root.");
|
|
51
|
+
}
|
|
52
|
+
return resolved;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function collectFilesRecursively(
|
|
56
|
+
targetPath: string,
|
|
57
|
+
extensions: Set<string>,
|
|
58
|
+
): string[] {
|
|
59
|
+
const results: string[] = [];
|
|
60
|
+
const entries = fs.readdirSync(targetPath, { withFileTypes: true });
|
|
61
|
+
|
|
62
|
+
for (const entry of entries) {
|
|
63
|
+
const fullPath = path.join(targetPath, entry.name);
|
|
64
|
+
|
|
65
|
+
if (entry.isDirectory()) {
|
|
66
|
+
if (entry.name === "node_modules" || entry.name === ".git") continue;
|
|
67
|
+
results.push(...collectFilesRecursively(fullPath, extensions));
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const ext = path.extname(entry.name).slice(1).toLowerCase();
|
|
72
|
+
if (extensions.has(ext)) results.push(fullPath);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return results;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function buildLineMatches(
|
|
79
|
+
files: string[],
|
|
80
|
+
matcher: (line: string) => boolean,
|
|
81
|
+
maxResults: number,
|
|
82
|
+
projectRoot: string,
|
|
83
|
+
): string[] {
|
|
84
|
+
const matches: string[] = [];
|
|
85
|
+
|
|
86
|
+
for (const filePath of files) {
|
|
87
|
+
if (matches.length >= maxResults) break;
|
|
88
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
89
|
+
const lines = content.split("\n");
|
|
90
|
+
|
|
91
|
+
for (let i = 0; i < lines.length; i++) {
|
|
92
|
+
if (matches.length >= maxResults) break;
|
|
93
|
+
const line = lines[i];
|
|
94
|
+
if (!matcher(line)) continue;
|
|
95
|
+
|
|
96
|
+
const relativePath = path.relative(projectRoot, filePath);
|
|
97
|
+
matches.push(`${relativePath}:${i + 1}:${line}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return matches;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function collectMarkdownArtifacts(projectRoot: string): string[] {
|
|
105
|
+
const docsRoot = path.join(projectRoot, ".gemini", "docs");
|
|
106
|
+
if (!fs.existsSync(docsRoot)) return [];
|
|
107
|
+
|
|
108
|
+
return collectFilesRecursively(docsRoot, new Set(["md"])).map((filePath) =>
|
|
109
|
+
path.relative(projectRoot, filePath),
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function replaceSectionContent(
|
|
114
|
+
markdown: string,
|
|
115
|
+
sectionTitle: string,
|
|
116
|
+
newBody: string,
|
|
117
|
+
): string {
|
|
118
|
+
const escaped = sectionTitle.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
119
|
+
const sectionRegex = new RegExp(
|
|
120
|
+
`## ${escaped}[\\s\\S]*?(?=\\n## |$)`,
|
|
121
|
+
"m",
|
|
122
|
+
);
|
|
123
|
+
if (!sectionRegex.test(markdown)) {
|
|
124
|
+
throw new Error(`Section not found: ${sectionTitle}`);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return markdown.replace(
|
|
128
|
+
sectionRegex,
|
|
129
|
+
`## ${sectionTitle}\n\n${newBody.trim()}\n`,
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function prependToSection(
|
|
134
|
+
markdown: string,
|
|
135
|
+
sectionTitle: string,
|
|
136
|
+
contentToPrepend: string,
|
|
137
|
+
): string {
|
|
138
|
+
const escaped = sectionTitle.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
139
|
+
const sectionRegex = new RegExp(`(## ${escaped}\\n)([\\s\\S]*?)(?=\\n## |$)`, "m");
|
|
140
|
+
const match = markdown.match(sectionRegex);
|
|
141
|
+
|
|
142
|
+
if (!match) {
|
|
143
|
+
throw new Error(`Section not found: ${sectionTitle}`);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const currentBody = match[2].trimStart();
|
|
147
|
+
const updatedBody = `${contentToPrepend.trim()}\n\n${currentBody}`.trim();
|
|
148
|
+
return markdown.replace(sectionRegex, `$1\n${updatedBody}\n`);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Tool definitions
|
|
153
|
+
*/
|
|
154
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
155
|
+
return {
|
|
156
|
+
tools: [
|
|
157
|
+
{
|
|
158
|
+
name: "get_framework_status",
|
|
159
|
+
description:
|
|
160
|
+
"Get the current status of the AI Agent Framework, including active phase and agent states.",
|
|
161
|
+
inputSchema: { type: "object", properties: {} },
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
name: "search_codebase",
|
|
165
|
+
description:
|
|
166
|
+
"Semantic search across the codebase using grep for exact matches and context. Ideal for finding logic and patterns.",
|
|
167
|
+
inputSchema: {
|
|
168
|
+
type: "object",
|
|
169
|
+
properties: {
|
|
170
|
+
query: {
|
|
171
|
+
type: "string",
|
|
172
|
+
description: "Search query or regex pattern",
|
|
173
|
+
},
|
|
174
|
+
extension: {
|
|
175
|
+
type: "string",
|
|
176
|
+
description: "File extension filter (e.g., ts, md)",
|
|
177
|
+
default: "ts",
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
required: ["query"],
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
name: "codebase_search",
|
|
185
|
+
description:
|
|
186
|
+
"Compatibility alias for search_codebase. Use when older agent prompts still reference codebase_search.",
|
|
187
|
+
inputSchema: {
|
|
188
|
+
type: "object",
|
|
189
|
+
properties: {
|
|
190
|
+
query: {
|
|
191
|
+
type: "string",
|
|
192
|
+
description: "Search query or regex pattern",
|
|
193
|
+
},
|
|
194
|
+
extension: {
|
|
195
|
+
type: "string",
|
|
196
|
+
description: "File extension filter (e.g., ts, md)",
|
|
197
|
+
default: "ts",
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
required: ["query"],
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
name: "analyze_dependencies",
|
|
205
|
+
description:
|
|
206
|
+
"Analyze code dependencies for a specific file or folder using import tracking.",
|
|
207
|
+
inputSchema: {
|
|
208
|
+
type: "object",
|
|
209
|
+
properties: {
|
|
210
|
+
path: {
|
|
211
|
+
type: "string",
|
|
212
|
+
description: "Path to analyze (relative to project root)",
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
required: ["path"],
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
name: "codebase_graph_query",
|
|
220
|
+
description:
|
|
221
|
+
"Compatibility alias for analyze_dependencies. Returns import-level dependency information for a file or folder.",
|
|
222
|
+
inputSchema: {
|
|
223
|
+
type: "object",
|
|
224
|
+
properties: {
|
|
225
|
+
path: {
|
|
226
|
+
type: "string",
|
|
227
|
+
description: "Path to analyze (relative to project root)",
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
required: ["path"],
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
name: "get_memory_insights",
|
|
235
|
+
description:
|
|
236
|
+
"Analyze PROJECT_MEMORY.md and BRAIN_DASHBOARD.md to provide insights on what was done, what is being done, and the history.",
|
|
237
|
+
inputSchema: { type: "object", properties: {} },
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
name: "codebase_context",
|
|
241
|
+
description:
|
|
242
|
+
"Compatibility helper for non-code context discovery. Lists known markdown artifacts under .gemini/docs and memory files.",
|
|
243
|
+
inputSchema: { type: "object", properties: {} },
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
name: "codebase_context_search",
|
|
247
|
+
description:
|
|
248
|
+
"Compatibility alias for search_codebase focused on markdown artifacts. Defaults to md files.",
|
|
249
|
+
inputSchema: {
|
|
250
|
+
type: "object",
|
|
251
|
+
properties: {
|
|
252
|
+
query: {
|
|
253
|
+
type: "string",
|
|
254
|
+
description: "Search query or regex pattern",
|
|
255
|
+
},
|
|
256
|
+
extension: {
|
|
257
|
+
type: "string",
|
|
258
|
+
description: "File extension filter (defaults to md)",
|
|
259
|
+
default: "md",
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
required: ["query"],
|
|
263
|
+
},
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
name: "get_project_gaps",
|
|
267
|
+
description:
|
|
268
|
+
"Scans the project structure against the defined standards in Gemini.md and identifies missing files, folders, or documentation.",
|
|
269
|
+
inputSchema: { type: "object", properties: {} },
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
name: "codebase_status",
|
|
273
|
+
description: "Compatibility alias for get_framework_status.",
|
|
274
|
+
inputSchema: { type: "object", properties: {} },
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
name: "security_audit_scan",
|
|
278
|
+
description:
|
|
279
|
+
"Scans the codebase for security vulnerabilities like hardcoded secrets, raw SQL, and unsafe async patterns.",
|
|
280
|
+
inputSchema: {
|
|
281
|
+
type: "object",
|
|
282
|
+
properties: {
|
|
283
|
+
path: {
|
|
284
|
+
type: "string",
|
|
285
|
+
description: "Path to scan (relative to project root)",
|
|
286
|
+
default: ".",
|
|
287
|
+
},
|
|
288
|
+
},
|
|
289
|
+
},
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
name: "update_project_memory",
|
|
293
|
+
description:
|
|
294
|
+
"Update a specific section of PROJECT_MEMORY.md (MEVCUT DURUM, HISTORY, or AKTİF GÖREVLER) with new content.",
|
|
295
|
+
inputSchema: {
|
|
296
|
+
type: "object",
|
|
297
|
+
properties: {
|
|
298
|
+
section: {
|
|
299
|
+
type: "string",
|
|
300
|
+
enum: ["MEVCUT DURUM", "HISTORY", "AKTİF GÖREVLER"],
|
|
301
|
+
description: "The section to update.",
|
|
302
|
+
},
|
|
303
|
+
content: {
|
|
304
|
+
type: "string",
|
|
305
|
+
description: "The new content to append or replace in that section.",
|
|
306
|
+
},
|
|
307
|
+
},
|
|
308
|
+
required: ["section", "content"],
|
|
309
|
+
},
|
|
310
|
+
},
|
|
311
|
+
],
|
|
312
|
+
};
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Tool execution logic
|
|
317
|
+
*/
|
|
318
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
319
|
+
const { name, arguments: args } = request.params;
|
|
320
|
+
const projectRoot = process.cwd();
|
|
321
|
+
|
|
322
|
+
switch (name) {
|
|
323
|
+
case "get_framework_status":
|
|
324
|
+
case "codebase_status": {
|
|
325
|
+
try {
|
|
326
|
+
const memoryPath = path.join(
|
|
327
|
+
projectRoot,
|
|
328
|
+
".gemini",
|
|
329
|
+
"PROJECT_MEMORY.md",
|
|
330
|
+
);
|
|
331
|
+
const memoryContent = fs.readFileSync(memoryPath, "utf-8");
|
|
332
|
+
const statusRowMatch = memoryContent.match(
|
|
333
|
+
/\| Aktif Faz \| Profile \| Son Güncelleme \| Aktif Trace ID \| Blokaj \|\n\| :-------- \| :------ \| :------------- \| :------------- \| :----- \|\n\| ([^|]+) \| ([^|]+) \| ([^|]+) \| ([^|]+) \| ([^|]+) \|/,
|
|
334
|
+
);
|
|
335
|
+
const phase = statusRowMatch?.[1]?.trim() ?? "UNKNOWN";
|
|
336
|
+
const profile = statusRowMatch?.[2]?.trim() ?? "UNKNOWN";
|
|
337
|
+
return {
|
|
338
|
+
content: [
|
|
339
|
+
{
|
|
340
|
+
type: "text",
|
|
341
|
+
text: `Framework active (v${FRAMEWORK_VERSION}). Phase: ${phase}. Profile: ${profile}.`,
|
|
342
|
+
},
|
|
343
|
+
],
|
|
344
|
+
};
|
|
345
|
+
} catch (error) {
|
|
346
|
+
return {
|
|
347
|
+
content: [
|
|
348
|
+
{ type: "text", text: "Framework active. Memory unreadable." },
|
|
349
|
+
],
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
case "security_audit_scan": {
|
|
355
|
+
const parsed = SECURITY_AUDIT_ARGS_SCHEMA.safeParse(args ?? {});
|
|
356
|
+
if (!parsed.success) {
|
|
357
|
+
return {
|
|
358
|
+
content: [{ type: "text", text: "Invalid path argument." }],
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
const vulnerabilities = [];
|
|
363
|
+
let safeTargetPath: string;
|
|
364
|
+
const scanRules = [
|
|
365
|
+
{
|
|
366
|
+
pattern: "sql`",
|
|
367
|
+
message: "Potential Raw SQL usage detected (check Kysely usage)",
|
|
368
|
+
severity: "HIGH",
|
|
369
|
+
},
|
|
370
|
+
{
|
|
371
|
+
pattern: "console.log",
|
|
372
|
+
message: "console.log found in production code",
|
|
373
|
+
severity: "LOW",
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
pattern: "password:",
|
|
377
|
+
message: "Potential hardcoded secret/password detected",
|
|
378
|
+
severity: "CRITICAL",
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
pattern: "secret:",
|
|
382
|
+
message: "Potential hardcoded secret detected",
|
|
383
|
+
severity: "CRITICAL",
|
|
384
|
+
},
|
|
385
|
+
{
|
|
386
|
+
pattern: "apiKey:",
|
|
387
|
+
message: "Potential hardcoded API Key detected",
|
|
388
|
+
severity: "CRITICAL",
|
|
389
|
+
},
|
|
390
|
+
{
|
|
391
|
+
pattern: "any",
|
|
392
|
+
message: "Usage of 'any' type detected",
|
|
393
|
+
severity: "MEDIUM",
|
|
394
|
+
},
|
|
395
|
+
{
|
|
396
|
+
pattern: "eval(",
|
|
397
|
+
message: "Dangerous 'eval()' usage detected",
|
|
398
|
+
severity: "HIGH",
|
|
399
|
+
},
|
|
400
|
+
{
|
|
401
|
+
pattern: ".innerHTML =",
|
|
402
|
+
message: "Unsafe innerHTML assignment detected (XSS risk)",
|
|
403
|
+
severity: "MEDIUM",
|
|
404
|
+
},
|
|
405
|
+
{
|
|
406
|
+
pattern: "dangerouslySetInnerHTML",
|
|
407
|
+
message: "React dangerouslySetInnerHTML detected",
|
|
408
|
+
severity: "MEDIUM",
|
|
409
|
+
},
|
|
410
|
+
{
|
|
411
|
+
pattern: "TODO:",
|
|
412
|
+
message: "Outstanding TODO item found",
|
|
413
|
+
severity: "LOW",
|
|
414
|
+
},
|
|
415
|
+
];
|
|
416
|
+
|
|
417
|
+
try {
|
|
418
|
+
safeTargetPath = resolveSafePath(projectRoot, parsed.data.path);
|
|
419
|
+
if (!fs.existsSync(safeTargetPath)) {
|
|
420
|
+
return {
|
|
421
|
+
content: [{ type: "text", text: "Target path not found." }],
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
const files = collectFilesRecursively(
|
|
426
|
+
safeTargetPath,
|
|
427
|
+
new Set(["ts", "tsx"]),
|
|
428
|
+
);
|
|
429
|
+
|
|
430
|
+
for (const rule of scanRules) {
|
|
431
|
+
const ruleMatches = buildLineMatches(
|
|
432
|
+
files,
|
|
433
|
+
(line) => line.includes(rule.pattern),
|
|
434
|
+
5,
|
|
435
|
+
projectRoot,
|
|
436
|
+
);
|
|
437
|
+
if (ruleMatches.length > 0) {
|
|
438
|
+
vulnerabilities.push(
|
|
439
|
+
`[${rule.severity}] ${rule.message}:\n${ruleMatches.join("\n")}`,
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
return {
|
|
445
|
+
content: [
|
|
446
|
+
{
|
|
447
|
+
type: "text",
|
|
448
|
+
text:
|
|
449
|
+
vulnerabilities.length > 0
|
|
450
|
+
? `### SECURITY AUDIT RESULTS\n\n${vulnerabilities.join("\n\n")}`
|
|
451
|
+
: "No common security patterns or rule violations detected.",
|
|
452
|
+
},
|
|
453
|
+
],
|
|
454
|
+
};
|
|
455
|
+
} catch (error) {
|
|
456
|
+
return { content: [{ type: "text", text: "Security scan failed." }] };
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
case "get_memory_insights": {
|
|
461
|
+
try {
|
|
462
|
+
const memoryPath = path.join(
|
|
463
|
+
projectRoot,
|
|
464
|
+
".gemini",
|
|
465
|
+
"PROJECT_MEMORY.md",
|
|
466
|
+
);
|
|
467
|
+
const dashboardPath = path.join(
|
|
468
|
+
projectRoot,
|
|
469
|
+
".gemini",
|
|
470
|
+
"BRAIN_DASHBOARD.md",
|
|
471
|
+
);
|
|
472
|
+
|
|
473
|
+
const memory = fs.existsSync(memoryPath)
|
|
474
|
+
? fs.readFileSync(memoryPath, "utf-8")
|
|
475
|
+
: "Memory file missing.";
|
|
476
|
+
const dashboard = fs.existsSync(dashboardPath)
|
|
477
|
+
? fs.readFileSync(dashboardPath, "utf-8")
|
|
478
|
+
: "Dashboard file missing.";
|
|
479
|
+
|
|
480
|
+
const history = memory.split("## HISTORY")[1] || "No history found.";
|
|
481
|
+
const activeTasks =
|
|
482
|
+
memory.split("## AKTİF GÖREVLER")[1]?.split("##")[0] ||
|
|
483
|
+
"No active tasks.";
|
|
484
|
+
const dashboardAgents =
|
|
485
|
+
dashboard.split("## 📈 Visualizations")[0] || dashboard;
|
|
486
|
+
|
|
487
|
+
return {
|
|
488
|
+
content: [
|
|
489
|
+
{
|
|
490
|
+
type: "text",
|
|
491
|
+
text: `### LIVE MEMORY INSIGHTS\n\n**Active Tasks:**\n${activeTasks.trim()}\n\n**Recent History:**\n${history.trim().substring(0, 1000)}...\n\n**Brain Snapshot:**\n${dashboardAgents.trim().substring(0, 500)}...`,
|
|
492
|
+
},
|
|
493
|
+
],
|
|
494
|
+
};
|
|
495
|
+
} catch (error) {
|
|
496
|
+
return {
|
|
497
|
+
content: [
|
|
498
|
+
{ type: "text", text: "Failed to gather memory insights." },
|
|
499
|
+
],
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
case "codebase_context": {
|
|
505
|
+
try {
|
|
506
|
+
const artifacts = collectMarkdownArtifacts(projectRoot);
|
|
507
|
+
const memoryPath = path.join(
|
|
508
|
+
projectRoot,
|
|
509
|
+
".gemini",
|
|
510
|
+
"PROJECT_MEMORY.md",
|
|
511
|
+
);
|
|
512
|
+
const dashboardPath = path.join(
|
|
513
|
+
projectRoot,
|
|
514
|
+
".gemini",
|
|
515
|
+
"BRAIN_DASHBOARD.md",
|
|
516
|
+
);
|
|
517
|
+
|
|
518
|
+
return {
|
|
519
|
+
content: [
|
|
520
|
+
{
|
|
521
|
+
type: "text",
|
|
522
|
+
text:
|
|
523
|
+
"### CONTEXT ARTIFACTS\n\n" +
|
|
524
|
+
`PROJECT_MEMORY: ${fs.existsSync(memoryPath) ? "present" : "missing"}\n` +
|
|
525
|
+
`BRAIN_DASHBOARD: ${fs.existsSync(dashboardPath) ? "present" : "missing"}\n` +
|
|
526
|
+
`Docs:\n${artifacts.length > 0 ? artifacts.join("\n") : "No markdown artifacts found."}`,
|
|
527
|
+
},
|
|
528
|
+
],
|
|
529
|
+
};
|
|
530
|
+
} catch (error) {
|
|
531
|
+
return {
|
|
532
|
+
content: [{ type: "text", text: "Context discovery failed." }],
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
case "get_project_gaps": {
|
|
538
|
+
const missing = [];
|
|
539
|
+
const checkPaths = [
|
|
540
|
+
{ path: "apps", type: "folder" },
|
|
541
|
+
{ path: "packages/shared-types/src/index.ts", type: "file" },
|
|
542
|
+
{ path: ".gemini/docs/api/README.md", type: "file" },
|
|
543
|
+
{ path: ".env", type: "file" },
|
|
544
|
+
{ path: ".env.development", type: "file" },
|
|
545
|
+
{ path: ".gemini/PROJECT_MEMORY.md", type: "file" },
|
|
546
|
+
{ path: ".gemini/logs/manager.json", type: "file" },
|
|
547
|
+
{ path: ".gemini/logs/analyst.json", type: "file" },
|
|
548
|
+
{ path: ".gemini/logs/backend.json", type: "file" },
|
|
549
|
+
{ path: ".gemini/logs/frontend.json", type: "file" },
|
|
550
|
+
{ path: ".gemini/logs/explorer.json", type: "file" },
|
|
551
|
+
];
|
|
552
|
+
|
|
553
|
+
for (const item of checkPaths) {
|
|
554
|
+
const fullPath = path.join(projectRoot, item.path);
|
|
555
|
+
if (!fs.existsSync(fullPath)) {
|
|
556
|
+
missing.push(`[MISSING ${item.type.toUpperCase()}] ${item.path}`);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
return {
|
|
561
|
+
content: [
|
|
562
|
+
{
|
|
563
|
+
type: "text",
|
|
564
|
+
text:
|
|
565
|
+
missing.length > 0
|
|
566
|
+
? `Detected Gaps:\n${missing.join("\n")}`
|
|
567
|
+
: "No structural gaps detected based on core standards.",
|
|
568
|
+
},
|
|
569
|
+
],
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
case "search_codebase":
|
|
574
|
+
case "codebase_search":
|
|
575
|
+
case "codebase_context_search": {
|
|
576
|
+
const mergedArgs =
|
|
577
|
+
name === "codebase_context_search"
|
|
578
|
+
? { extension: "md", ...(args ?? {}) }
|
|
579
|
+
: (args ?? {});
|
|
580
|
+
const parsed = SEARCH_CODEBASE_ARGS_SCHEMA.safeParse(mergedArgs);
|
|
581
|
+
if (!parsed.success) {
|
|
582
|
+
return {
|
|
583
|
+
content: [
|
|
584
|
+
{ type: "text", text: "Invalid query/extension argument." },
|
|
585
|
+
],
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
const { query, extension } = parsed.data;
|
|
590
|
+
try {
|
|
591
|
+
const files = collectFilesRecursively(
|
|
592
|
+
projectRoot,
|
|
593
|
+
new Set([extension]),
|
|
594
|
+
);
|
|
595
|
+
let queryRegex: RegExp;
|
|
596
|
+
try {
|
|
597
|
+
queryRegex = new RegExp(query);
|
|
598
|
+
} catch (error) {
|
|
599
|
+
return {
|
|
600
|
+
content: [
|
|
601
|
+
{
|
|
602
|
+
type: "text",
|
|
603
|
+
text: "Invalid regex pattern in query.",
|
|
604
|
+
},
|
|
605
|
+
],
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
const matches = buildLineMatches(
|
|
609
|
+
files,
|
|
610
|
+
(line) => queryRegex.test(line),
|
|
611
|
+
20,
|
|
612
|
+
projectRoot,
|
|
613
|
+
);
|
|
614
|
+
const result = matches.join("\n");
|
|
615
|
+
return {
|
|
616
|
+
content: [{ type: "text", text: result || "No matches found." }],
|
|
617
|
+
};
|
|
618
|
+
} catch (error) {
|
|
619
|
+
return {
|
|
620
|
+
content: [
|
|
621
|
+
{ type: "text", text: "Search found no matches or failed." },
|
|
622
|
+
],
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
case "analyze_dependencies":
|
|
628
|
+
case "codebase_graph_query": {
|
|
629
|
+
const parsed = ANALYZE_DEPENDENCIES_ARGS_SCHEMA.safeParse(args ?? {});
|
|
630
|
+
if (!parsed.success) {
|
|
631
|
+
return {
|
|
632
|
+
content: [{ type: "text", text: "Invalid path argument." }],
|
|
633
|
+
};
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
const targetPath = parsed.data.path;
|
|
637
|
+
try {
|
|
638
|
+
const fullPath = resolveSafePath(projectRoot, targetPath);
|
|
639
|
+
if (!fs.existsSync(fullPath))
|
|
640
|
+
return {
|
|
641
|
+
content: [{ type: "text", text: `Path not found: ${targetPath}` }],
|
|
642
|
+
};
|
|
643
|
+
const stats = fs.statSync(fullPath);
|
|
644
|
+
if (stats.isDirectory()) {
|
|
645
|
+
const files = fs
|
|
646
|
+
.readdirSync(fullPath)
|
|
647
|
+
.filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
|
648
|
+
return {
|
|
649
|
+
content: [
|
|
650
|
+
{
|
|
651
|
+
type: "text",
|
|
652
|
+
text: `Directory contains ${files.length} TS files.`,
|
|
653
|
+
},
|
|
654
|
+
],
|
|
655
|
+
};
|
|
656
|
+
} else {
|
|
657
|
+
const content = fs.readFileSync(fullPath, "utf-8");
|
|
658
|
+
const importRegex = /from\s+['"](.+?)['"]/g;
|
|
659
|
+
const imports = [];
|
|
660
|
+
let match;
|
|
661
|
+
while ((match = importRegex.exec(content)) !== null) {
|
|
662
|
+
const importPath = match[1];
|
|
663
|
+
let resolved = "unresolved";
|
|
664
|
+
|
|
665
|
+
// Basic relative path resolution
|
|
666
|
+
if (importPath.startsWith(".")) {
|
|
667
|
+
const absImport = path.resolve(path.dirname(fullPath), importPath);
|
|
668
|
+
const possiblePaths = [absImport, absImport + ".ts", absImport + ".tsx", path.join(absImport, "index.ts")];
|
|
669
|
+
for (const p of possiblePaths) {
|
|
670
|
+
if (fs.existsSync(p)) {
|
|
671
|
+
resolved = path.relative(projectRoot, p);
|
|
672
|
+
break;
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
imports.push(`- ${importPath} (${resolved})`);
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
return {
|
|
681
|
+
content: [
|
|
682
|
+
{
|
|
683
|
+
type: "text",
|
|
684
|
+
text: `Dependencies for ${targetPath}:\n${imports.length > 0 ? imports.join("\n") : "No imports found."}`,
|
|
685
|
+
},
|
|
686
|
+
],
|
|
687
|
+
};
|
|
688
|
+
}
|
|
689
|
+
} catch (error) {
|
|
690
|
+
return { content: [{ type: "text", text: "Analysis failed." }] };
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
case "update_project_memory": {
|
|
694
|
+
const parsed = UPDATE_MEMORY_ARGS_SCHEMA.safeParse(args ?? {});
|
|
695
|
+
if (!parsed.success) {
|
|
696
|
+
return { content: [{ type: "text", text: "Invalid section or content." }] };
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
const { section, content } = parsed.data;
|
|
700
|
+
const memoryPath = path.join(projectRoot, ".gemini", "PROJECT_MEMORY.md");
|
|
701
|
+
const lockPath = memoryPath + ".lock";
|
|
702
|
+
|
|
703
|
+
try {
|
|
704
|
+
// Lock protocol (simplified for MCP)
|
|
705
|
+
if (fs.existsSync(lockPath)) {
|
|
706
|
+
return { content: [{ type: "text", text: "Memory is locked. Try again later." }] };
|
|
707
|
+
}
|
|
708
|
+
fs.writeFileSync(lockPath, "LOCKED");
|
|
709
|
+
|
|
710
|
+
let memoryContent = fs.readFileSync(memoryPath, "utf-8");
|
|
711
|
+
|
|
712
|
+
if (section === "HISTORY") {
|
|
713
|
+
memoryContent = prependToSection(
|
|
714
|
+
memoryContent,
|
|
715
|
+
"HISTORY (Kalıcı Hafıza)",
|
|
716
|
+
content,
|
|
717
|
+
);
|
|
718
|
+
} else if (section === "MEVCUT DURUM") {
|
|
719
|
+
memoryContent = replaceSectionContent(
|
|
720
|
+
memoryContent,
|
|
721
|
+
"MEVCUT DURUM",
|
|
722
|
+
content,
|
|
723
|
+
);
|
|
724
|
+
} else if (section === "AKTİF GÖREVLER") {
|
|
725
|
+
memoryContent = replaceSectionContent(
|
|
726
|
+
memoryContent,
|
|
727
|
+
"AKTİF GÖREVLER",
|
|
728
|
+
content,
|
|
729
|
+
);
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
fs.writeFileSync(memoryPath, memoryContent);
|
|
733
|
+
fs.unlinkSync(lockPath);
|
|
734
|
+
|
|
735
|
+
return { content: [{ type: "text", text: `Section ${section} updated successfully.` }] };
|
|
736
|
+
} catch (error) {
|
|
737
|
+
if (fs.existsSync(lockPath)) fs.unlinkSync(lockPath);
|
|
738
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
739
|
+
return {
|
|
740
|
+
content: [{ type: "text", text: `Memory update failed: ${message}` }],
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
default:
|
|
746
|
+
throw new Error(`Tool not found: ${name}`);
|
|
747
|
+
}
|
|
748
|
+
});
|
|
749
|
+
|
|
750
|
+
async function main() {
|
|
751
|
+
const transport = new StdioServerTransport();
|
|
752
|
+
await server.connect(transport);
|
|
753
|
+
console.error("Framework MCP Server running on stdio");
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
main().catch((error) => {
|
|
757
|
+
console.error("Fatal error in main():", error);
|
|
758
|
+
process.exit(1);
|
|
759
|
+
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-enderun-shared-types",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "Shared TypeScript types for backend ↔ frontend contract. Owned by @backend, consumed by @frontend.",
|
|
5
5
|
"author": "Yusuf BEKAR <ybekar@msn.com>",
|
|
6
6
|
"repository": {
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
"main": "dist/index.js",
|
|
12
12
|
"types": "dist/index.d.ts",
|
|
13
13
|
"files": [
|
|
14
|
+
"src",
|
|
14
15
|
"dist",
|
|
15
16
|
"README.md"
|
|
16
17
|
],
|