mcp-adr-analysis-server 2.0.8 → 2.0.10
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/dist/src/index.d.ts +8 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +434 -5
- package/dist/src/index.js.map +1 -1
- package/dist/src/resources/index.d.ts.map +1 -1
- package/dist/src/resources/index.js +71 -1
- package/dist/src/resources/index.js.map +1 -1
- package/dist/src/tools/human-override-tool.d.ts +42 -0
- package/dist/src/tools/human-override-tool.d.ts.map +1 -0
- package/dist/src/tools/human-override-tool.js +431 -0
- package/dist/src/tools/human-override-tool.js.map +1 -0
- package/dist/src/tools/smart-git-push-tool.d.ts +5 -1
- package/dist/src/tools/smart-git-push-tool.d.ts.map +1 -1
- package/dist/src/tools/smart-git-push-tool.js +75 -30
- package/dist/src/tools/smart-git-push-tool.js.map +1 -1
- package/dist/src/tools/smart-score-tool.d.ts +138 -0
- package/dist/src/tools/smart-score-tool.d.ts.map +1 -0
- package/dist/src/tools/smart-score-tool.js +602 -0
- package/dist/src/tools/smart-score-tool.js.map +1 -0
- package/dist/src/tools/todo-management-tool.d.ts +184 -6
- package/dist/src/tools/todo-management-tool.d.ts.map +1 -1
- package/dist/src/tools/todo-management-tool.js +819 -42
- package/dist/src/tools/todo-management-tool.js.map +1 -1
- package/dist/src/tools/tool-chain-orchestrator.d.ts +164 -0
- package/dist/src/tools/tool-chain-orchestrator.d.ts.map +1 -0
- package/dist/src/tools/tool-chain-orchestrator.js +738 -0
- package/dist/src/tools/tool-chain-orchestrator.js.map +1 -0
- package/dist/src/tools/troubleshoot-guided-workflow-tool.d.ts +150 -0
- package/dist/src/tools/troubleshoot-guided-workflow-tool.d.ts.map +1 -0
- package/dist/src/tools/troubleshoot-guided-workflow-tool.js +520 -0
- package/dist/src/tools/troubleshoot-guided-workflow-tool.js.map +1 -0
- package/dist/src/types/knowledge-graph-schemas.d.ts +481 -0
- package/dist/src/types/knowledge-graph-schemas.d.ts.map +1 -0
- package/dist/src/types/knowledge-graph-schemas.js +59 -0
- package/dist/src/types/knowledge-graph-schemas.js.map +1 -0
- package/dist/src/utils/enhanced-sensitive-detector.js +1 -1
- package/dist/src/utils/enhanced-sensitive-detector.js.map +1 -1
- package/dist/src/utils/json-safe.d.ts +38 -0
- package/dist/src/utils/json-safe.d.ts.map +1 -0
- package/dist/src/utils/json-safe.js +86 -0
- package/dist/src/utils/json-safe.js.map +1 -0
- package/dist/src/utils/knowledge-graph-manager.d.ts +28 -0
- package/dist/src/utils/knowledge-graph-manager.d.ts.map +1 -0
- package/dist/src/utils/knowledge-graph-manager.js +192 -0
- package/dist/src/utils/knowledge-graph-manager.js.map +1 -0
- package/dist/src/utils/mcp-response-validator.d.ts +30 -0
- package/dist/src/utils/mcp-response-validator.d.ts.map +1 -0
- package/dist/src/utils/mcp-response-validator.js +197 -0
- package/dist/src/utils/mcp-response-validator.js.map +1 -0
- package/dist/src/utils/project-health-scoring.d.ts +177 -0
- package/dist/src/utils/project-health-scoring.d.ts.map +1 -0
- package/dist/src/utils/project-health-scoring.js +370 -0
- package/dist/src/utils/project-health-scoring.js.map +1 -0
- package/dist/src/utils/release-readiness-detector.d.ts +2 -0
- package/dist/src/utils/release-readiness-detector.d.ts.map +1 -1
- package/dist/src/utils/release-readiness-detector.js +4 -2
- package/dist/src/utils/release-readiness-detector.js.map +1 -1
- package/dist/src/utils/todo-file-watcher.d.ts +26 -0
- package/dist/src/utils/todo-file-watcher.d.ts.map +1 -0
- package/dist/src/utils/todo-file-watcher.js +163 -0
- package/dist/src/utils/todo-file-watcher.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON-Safe String Utilities
|
|
3
|
+
*
|
|
4
|
+
* Utilities for safely escaping strings in JSON-RPC 2.0 responses
|
|
5
|
+
* to prevent parse errors in MCP communication
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Escape a string to be JSON-safe for MCP responses
|
|
9
|
+
* Only escapes actual control characters and problematic JSON chars
|
|
10
|
+
*/
|
|
11
|
+
export function jsonSafe(str) {
|
|
12
|
+
if (str == null) {
|
|
13
|
+
return '';
|
|
14
|
+
}
|
|
15
|
+
return String(str)
|
|
16
|
+
// Escape backslashes first (before escaping quotes)
|
|
17
|
+
.replace(/\\/g, '\\\\')
|
|
18
|
+
// Escape double quotes
|
|
19
|
+
.replace(/"/g, '\\"')
|
|
20
|
+
// Only escape actual control characters (not regex \b)
|
|
21
|
+
.replace(/\n/g, '\\n')
|
|
22
|
+
.replace(/\r/g, '\\r')
|
|
23
|
+
.replace(/\t/g, '\\t')
|
|
24
|
+
// Escape other potentially problematic control characters (but not \b word boundary)
|
|
25
|
+
.replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F-\u009F]/g, (char) => {
|
|
26
|
+
const code = char.charCodeAt(0);
|
|
27
|
+
switch (code) {
|
|
28
|
+
case 0x08: return '\\b'; // actual backspace character
|
|
29
|
+
case 0x0C: return '\\f'; // form feed
|
|
30
|
+
default:
|
|
31
|
+
return '\\u' + ('0000' + code.toString(16)).slice(-4);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Escape an array of strings to be JSON-safe
|
|
37
|
+
*/
|
|
38
|
+
export function jsonSafeArray(arr) {
|
|
39
|
+
return arr.map(jsonSafe);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Safely join an array of strings with JSON-safe escaping
|
|
43
|
+
*/
|
|
44
|
+
export function jsonSafeJoin(arr, separator = '\n') {
|
|
45
|
+
return jsonSafeArray(arr).join(separator);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Create a JSON-safe markdown list from an array of items
|
|
49
|
+
*/
|
|
50
|
+
export function jsonSafeMarkdownList(items, prefix = '- ') {
|
|
51
|
+
return jsonSafeArray(items)
|
|
52
|
+
.filter(item => item.length > 0)
|
|
53
|
+
.map(item => `${prefix}${item}`)
|
|
54
|
+
.join('\n');
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Safely escape file paths for JSON output - minimal escaping for display
|
|
58
|
+
* Only normalizes path separators and escapes quotes
|
|
59
|
+
*/
|
|
60
|
+
export function jsonSafeFilePath(filePath) {
|
|
61
|
+
if (!filePath) {
|
|
62
|
+
return '';
|
|
63
|
+
}
|
|
64
|
+
// For file paths, only escape quotes and backslashes, normalize separators
|
|
65
|
+
return String(filePath)
|
|
66
|
+
.replace(/\\/g, '/') // Normalize Windows paths
|
|
67
|
+
.replace(/"/g, '\\"'); // Only escape quotes for JSON safety
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Safely escape user input content that might contain dangerous characters
|
|
71
|
+
* Use this for content that comes from external sources or user input
|
|
72
|
+
*/
|
|
73
|
+
export function jsonSafeUserInput(str) {
|
|
74
|
+
return jsonSafe(str);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Safely escape error messages that might contain quotes or special chars
|
|
78
|
+
*/
|
|
79
|
+
export function jsonSafeError(error) {
|
|
80
|
+
if (!error) {
|
|
81
|
+
return 'Unknown error';
|
|
82
|
+
}
|
|
83
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
84
|
+
return jsonSafe(message);
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=json-safe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json-safe.js","sourceRoot":"","sources":["../../../src/utils/json-safe.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;GAGG;AACH,MAAM,UAAU,QAAQ,CAAC,GAA8B;IACrD,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC;QAChB,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,OAAO,MAAM,CAAC,GAAG,CAAC;QAChB,oDAAoD;SACnD,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC;QACvB,uBAAuB;SACtB,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC;QACrB,uDAAuD;SACtD,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC;SACrB,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC;SACrB,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC;QACtB,qFAAqF;SACpF,OAAO,CAAC,wDAAwD,EAAE,CAAC,IAAI,EAAE,EAAE;QAC1E,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QAChC,QAAQ,IAAI,EAAE,CAAC;YACb,KAAK,IAAI,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,6BAA6B;YACtD,KAAK,IAAI,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,YAAY;YACrC;gBACE,OAAO,KAAK,GAAG,CAAC,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,GAAkC;IAC9D,OAAO,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AAC3B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,GAAkC,EAAE,YAAoB,IAAI;IACvF,OAAO,aAAa,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;AAC5C,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB,CAAC,KAAoC,EAAE,SAAiB,IAAI;IAC9F,OAAO,aAAa,CAAC,KAAK,CAAC;SACxB,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;SAC/B,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,MAAM,GAAG,IAAI,EAAE,CAAC;SAC/B,IAAI,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAAmC;IAClE,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,2EAA2E;IAC3E,OAAO,MAAM,CAAC,QAAQ,CAAC;SACpB,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,0BAA0B;SAC9C,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,qCAAqC;AAChE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,GAA8B;IAC9D,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC;AACvB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,eAAe,CAAC;IACzB,CAAC;IAED,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACvE,OAAO,QAAQ,CAAC,OAAO,CAAC,CAAC;AAC3B,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { IntentSnapshot, TodoSyncState, KnowledgeGraphSnapshot } from '../types/knowledge-graph-schemas.js';
|
|
2
|
+
export declare class KnowledgeGraphManager {
|
|
3
|
+
private cacheDir;
|
|
4
|
+
private snapshotsFile;
|
|
5
|
+
private syncStateFile;
|
|
6
|
+
constructor();
|
|
7
|
+
ensureCacheDirectory(): Promise<void>;
|
|
8
|
+
loadKnowledgeGraph(): Promise<KnowledgeGraphSnapshot>;
|
|
9
|
+
saveKnowledgeGraph(snapshot: KnowledgeGraphSnapshot): Promise<void>;
|
|
10
|
+
createIntent(humanRequest: string, parsedGoals: string[], priority?: 'high' | 'medium' | 'low'): Promise<string>;
|
|
11
|
+
addToolExecution(intentId: string, toolName: string, parameters: Record<string, any>, result: Record<string, any>, success: boolean, todoTasksCreated?: string[], todoTasksModified?: string[], error?: string): Promise<void>;
|
|
12
|
+
updateIntentStatus(intentId: string, status: 'planning' | 'executing' | 'completed' | 'failed'): Promise<void>;
|
|
13
|
+
updateTodoSnapshot(intentId: string, todoContent: string): Promise<void>;
|
|
14
|
+
getSyncState(): Promise<TodoSyncState>;
|
|
15
|
+
updateSyncState(updates: Partial<TodoSyncState>): Promise<void>;
|
|
16
|
+
getIntentById(intentId: string): Promise<IntentSnapshot | null>;
|
|
17
|
+
getActiveIntents(): Promise<IntentSnapshot[]>;
|
|
18
|
+
getIntentsByStatus(status: 'planning' | 'executing' | 'completed' | 'failed'): Promise<IntentSnapshot[]>;
|
|
19
|
+
private getDefaultSyncState;
|
|
20
|
+
private updateAnalytics;
|
|
21
|
+
calculateTodoMdHash(todoPath: string): Promise<string>;
|
|
22
|
+
detectTodoChanges(todoPath: string): Promise<{
|
|
23
|
+
hasChanges: boolean;
|
|
24
|
+
currentHash: string;
|
|
25
|
+
lastHash: string;
|
|
26
|
+
}>;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=knowledge-graph-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"knowledge-graph-manager.d.ts","sourceRoot":"","sources":["../../../src/utils/knowledge-graph-manager.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,cAAc,EAEd,aAAa,EACb,sBAAsB,EAGvB,MAAM,qCAAqC,CAAC;AAG7C,qBAAa,qBAAqB;IAChC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,aAAa,CAAS;;IASxB,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC;IAQrC,kBAAkB,IAAI,OAAO,CAAC,sBAAsB,CAAC;IA2BrD,kBAAkB,CAAC,QAAQ,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC;IAKnE,YAAY,CAChB,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,MAAM,EAAE,EACrB,QAAQ,GAAE,MAAM,GAAG,QAAQ,GAAG,KAAgB,GAC7C,OAAO,CAAC,MAAM,CAAC;IAwBZ,gBAAgB,CACpB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC/B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC3B,OAAO,EAAE,OAAO,EAChB,gBAAgB,GAAE,MAAM,EAAO,EAC/B,iBAAiB,GAAE,MAAM,EAAO,EAChC,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC;IA0BV,kBAAkB,CACtB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,UAAU,GAAG,WAAW,GAAG,WAAW,GAAG,QAAQ,GACxD,OAAO,CAAC,IAAI,CAAC;IAaV,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAYxE,YAAY,IAAI,OAAO,CAAC,aAAa,CAAC;IAUtC,eAAe,CAAC,OAAO,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAU/D,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAK/D,gBAAgB,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;IAK7C,kBAAkB,CAAC,MAAM,EAAE,UAAU,GAAG,WAAW,GAAG,WAAW,GAAG,QAAQ,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;YAKhG,mBAAmB;IAWjC,OAAO,CAAC,eAAe;IAwBjB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAStD,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;QACjD,UAAU,EAAE,OAAO,CAAC;QACpB,WAAW,EAAE,MAAM,CAAC;QACpB,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;CAUH"}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import * as fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import crypto from 'crypto';
|
|
4
|
+
import { TodoSyncStateSchema, KnowledgeGraphSnapshotSchema } from '../types/knowledge-graph-schemas.js';
|
|
5
|
+
import { loadConfig } from './config.js';
|
|
6
|
+
export class KnowledgeGraphManager {
|
|
7
|
+
cacheDir;
|
|
8
|
+
snapshotsFile;
|
|
9
|
+
syncStateFile;
|
|
10
|
+
constructor() {
|
|
11
|
+
const config = loadConfig();
|
|
12
|
+
this.cacheDir = path.join(config.projectPath, '.mcp-adr-cache');
|
|
13
|
+
this.snapshotsFile = path.join(this.cacheDir, 'knowledge-graph-snapshots.json');
|
|
14
|
+
this.syncStateFile = path.join(this.cacheDir, 'todo-sync-state.json');
|
|
15
|
+
}
|
|
16
|
+
async ensureCacheDirectory() {
|
|
17
|
+
try {
|
|
18
|
+
await fs.access(this.cacheDir);
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
await fs.mkdir(this.cacheDir, { recursive: true });
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
async loadKnowledgeGraph() {
|
|
25
|
+
await this.ensureCacheDirectory();
|
|
26
|
+
try {
|
|
27
|
+
const data = await fs.readFile(this.snapshotsFile, 'utf-8');
|
|
28
|
+
const parsed = JSON.parse(data);
|
|
29
|
+
return KnowledgeGraphSnapshotSchema.parse(parsed);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
const defaultSnapshot = {
|
|
33
|
+
version: '1.0.0',
|
|
34
|
+
timestamp: new Date().toISOString(),
|
|
35
|
+
intents: [],
|
|
36
|
+
todoSyncState: await this.getDefaultSyncState(),
|
|
37
|
+
analytics: {
|
|
38
|
+
totalIntents: 0,
|
|
39
|
+
completedIntents: 0,
|
|
40
|
+
activeIntents: 0,
|
|
41
|
+
averageGoalCompletion: 0,
|
|
42
|
+
mostUsedTools: [],
|
|
43
|
+
successfulPatterns: []
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
await this.saveKnowledgeGraph(defaultSnapshot);
|
|
47
|
+
return defaultSnapshot;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
async saveKnowledgeGraph(snapshot) {
|
|
51
|
+
await this.ensureCacheDirectory();
|
|
52
|
+
await fs.writeFile(this.snapshotsFile, JSON.stringify(snapshot, null, 2));
|
|
53
|
+
}
|
|
54
|
+
async createIntent(humanRequest, parsedGoals, priority = 'medium') {
|
|
55
|
+
const intentId = crypto.randomUUID();
|
|
56
|
+
const timestamp = new Date().toISOString();
|
|
57
|
+
const intent = {
|
|
58
|
+
intentId,
|
|
59
|
+
humanRequest,
|
|
60
|
+
parsedGoals,
|
|
61
|
+
priority,
|
|
62
|
+
timestamp,
|
|
63
|
+
toolChain: [],
|
|
64
|
+
currentStatus: 'planning',
|
|
65
|
+
todoMdSnapshot: ''
|
|
66
|
+
};
|
|
67
|
+
const kg = await this.loadKnowledgeGraph();
|
|
68
|
+
kg.intents.push(intent);
|
|
69
|
+
kg.analytics.totalIntents = kg.intents.length;
|
|
70
|
+
kg.analytics.activeIntents = kg.intents.filter(i => i.currentStatus !== 'completed').length;
|
|
71
|
+
await this.saveKnowledgeGraph(kg);
|
|
72
|
+
return intentId;
|
|
73
|
+
}
|
|
74
|
+
async addToolExecution(intentId, toolName, parameters, result, success, todoTasksCreated = [], todoTasksModified = [], error) {
|
|
75
|
+
const kg = await this.loadKnowledgeGraph();
|
|
76
|
+
const intent = kg.intents.find(i => i.intentId === intentId);
|
|
77
|
+
if (!intent) {
|
|
78
|
+
throw new Error(`Intent ${intentId} not found`);
|
|
79
|
+
}
|
|
80
|
+
const execution = {
|
|
81
|
+
toolName,
|
|
82
|
+
parameters,
|
|
83
|
+
result,
|
|
84
|
+
todoTasksCreated,
|
|
85
|
+
todoTasksModified,
|
|
86
|
+
executionTime: new Date().toISOString(),
|
|
87
|
+
success,
|
|
88
|
+
error
|
|
89
|
+
};
|
|
90
|
+
intent.toolChain.push(execution);
|
|
91
|
+
intent.currentStatus = success ? 'executing' : 'failed';
|
|
92
|
+
this.updateAnalytics(kg);
|
|
93
|
+
await this.saveKnowledgeGraph(kg);
|
|
94
|
+
}
|
|
95
|
+
async updateIntentStatus(intentId, status) {
|
|
96
|
+
const kg = await this.loadKnowledgeGraph();
|
|
97
|
+
const intent = kg.intents.find(i => i.intentId === intentId);
|
|
98
|
+
if (!intent) {
|
|
99
|
+
throw new Error(`Intent ${intentId} not found`);
|
|
100
|
+
}
|
|
101
|
+
intent.currentStatus = status;
|
|
102
|
+
this.updateAnalytics(kg);
|
|
103
|
+
await this.saveKnowledgeGraph(kg);
|
|
104
|
+
}
|
|
105
|
+
async updateTodoSnapshot(intentId, todoContent) {
|
|
106
|
+
const kg = await this.loadKnowledgeGraph();
|
|
107
|
+
const intent = kg.intents.find(i => i.intentId === intentId);
|
|
108
|
+
if (!intent) {
|
|
109
|
+
throw new Error(`Intent ${intentId} not found`);
|
|
110
|
+
}
|
|
111
|
+
intent.todoMdSnapshot = todoContent;
|
|
112
|
+
await this.saveKnowledgeGraph(kg);
|
|
113
|
+
}
|
|
114
|
+
async getSyncState() {
|
|
115
|
+
try {
|
|
116
|
+
const data = await fs.readFile(this.syncStateFile, 'utf-8');
|
|
117
|
+
const parsed = JSON.parse(data);
|
|
118
|
+
return TodoSyncStateSchema.parse(parsed);
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
return this.getDefaultSyncState();
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
async updateSyncState(updates) {
|
|
125
|
+
const current = await this.getSyncState();
|
|
126
|
+
const updated = { ...current, ...updates };
|
|
127
|
+
await fs.writeFile(this.syncStateFile, JSON.stringify(updated, null, 2));
|
|
128
|
+
const kg = await this.loadKnowledgeGraph();
|
|
129
|
+
kg.todoSyncState = updated;
|
|
130
|
+
await this.saveKnowledgeGraph(kg);
|
|
131
|
+
}
|
|
132
|
+
async getIntentById(intentId) {
|
|
133
|
+
const kg = await this.loadKnowledgeGraph();
|
|
134
|
+
return kg.intents.find(i => i.intentId === intentId) || null;
|
|
135
|
+
}
|
|
136
|
+
async getActiveIntents() {
|
|
137
|
+
const kg = await this.loadKnowledgeGraph();
|
|
138
|
+
return kg.intents.filter(i => i.currentStatus !== 'completed' && i.currentStatus !== 'failed');
|
|
139
|
+
}
|
|
140
|
+
async getIntentsByStatus(status) {
|
|
141
|
+
const kg = await this.loadKnowledgeGraph();
|
|
142
|
+
return kg.intents.filter(i => i.currentStatus === status);
|
|
143
|
+
}
|
|
144
|
+
async getDefaultSyncState() {
|
|
145
|
+
return {
|
|
146
|
+
lastSyncTimestamp: new Date().toISOString(),
|
|
147
|
+
todoMdHash: '',
|
|
148
|
+
knowledgeGraphHash: '',
|
|
149
|
+
syncStatus: 'synced',
|
|
150
|
+
lastModifiedBy: 'tool',
|
|
151
|
+
version: 1
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
updateAnalytics(kg) {
|
|
155
|
+
const intents = kg.intents;
|
|
156
|
+
kg.analytics.totalIntents = intents.length;
|
|
157
|
+
kg.analytics.completedIntents = intents.filter(i => i.currentStatus === 'completed').length;
|
|
158
|
+
kg.analytics.activeIntents = intents.filter(i => i.currentStatus !== 'completed' && i.currentStatus !== 'failed').length;
|
|
159
|
+
if (kg.analytics.totalIntents > 0) {
|
|
160
|
+
kg.analytics.averageGoalCompletion = kg.analytics.completedIntents / kg.analytics.totalIntents;
|
|
161
|
+
}
|
|
162
|
+
const toolUsage = new Map();
|
|
163
|
+
intents.forEach(intent => {
|
|
164
|
+
intent.toolChain.forEach(execution => {
|
|
165
|
+
toolUsage.set(execution.toolName, (toolUsage.get(execution.toolName) || 0) + 1);
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
kg.analytics.mostUsedTools = Array.from(toolUsage.entries())
|
|
169
|
+
.map(([toolName, usageCount]) => ({ toolName, usageCount }))
|
|
170
|
+
.sort((a, b) => b.usageCount - a.usageCount)
|
|
171
|
+
.slice(0, 10);
|
|
172
|
+
}
|
|
173
|
+
async calculateTodoMdHash(todoPath) {
|
|
174
|
+
try {
|
|
175
|
+
const content = await fs.readFile(todoPath, 'utf-8');
|
|
176
|
+
return crypto.createHash('sha256').update(content).digest('hex');
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
return '';
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
async detectTodoChanges(todoPath) {
|
|
183
|
+
const syncState = await this.getSyncState();
|
|
184
|
+
const currentHash = await this.calculateTodoMdHash(todoPath);
|
|
185
|
+
return {
|
|
186
|
+
hasChanges: currentHash !== syncState.todoMdHash,
|
|
187
|
+
currentHash,
|
|
188
|
+
lastHash: syncState.todoMdHash
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
//# sourceMappingURL=knowledge-graph-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"knowledge-graph-manager.js","sourceRoot":"","sources":["../../../src/utils/knowledge-graph-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Response Validator
|
|
3
|
+
*
|
|
4
|
+
* Validates and sanitizes MCP tool responses to prevent JSON-RPC 2.0 parse errors
|
|
5
|
+
*/
|
|
6
|
+
export interface McpToolResponse {
|
|
7
|
+
content: Array<{
|
|
8
|
+
type: 'text' | 'image' | 'resource';
|
|
9
|
+
text?: string;
|
|
10
|
+
data?: string;
|
|
11
|
+
mimeType?: string;
|
|
12
|
+
}>;
|
|
13
|
+
isError?: boolean;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Validate and sanitize an MCP tool response
|
|
17
|
+
*/
|
|
18
|
+
export declare function validateMcpResponse(response: any): McpToolResponse;
|
|
19
|
+
/**
|
|
20
|
+
* Validate that a response can be safely serialized as JSON-RPC 2.0
|
|
21
|
+
*/
|
|
22
|
+
export declare function validateJsonRpcSerialization(response: any): {
|
|
23
|
+
valid: boolean;
|
|
24
|
+
error?: string;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Safe wrapper for MCP tool functions
|
|
28
|
+
*/
|
|
29
|
+
export declare function safeMcpToolWrapper<T extends any[], R>(toolFunction: (...args: T) => Promise<R>, toolName: string): (...args: T) => Promise<McpToolResponse>;
|
|
30
|
+
//# sourceMappingURL=mcp-response-validator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-response-validator.d.ts","sourceRoot":"","sources":["../../../src/utils/mcp-response-validator.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,KAAK,CAAC;QACb,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,UAAU,CAAC;QACpC,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC,CAAC;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,GAAG,GAAG,eAAe,CAuClE;AAsHD;;GAEG;AACH,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,GAAG,GAAG;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CA0B9F;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,GAAG,EAAE,EAAE,CAAC,EACnD,YAAY,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,EACxC,QAAQ,EAAE,MAAM,IAEF,GAAG,MAAM,CAAC,KAAG,OAAO,CAAC,eAAe,CAAC,CAkBpD"}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Response Validator
|
|
3
|
+
*
|
|
4
|
+
* Validates and sanitizes MCP tool responses to prevent JSON-RPC 2.0 parse errors
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Validate and sanitize an MCP tool response
|
|
8
|
+
*/
|
|
9
|
+
export function validateMcpResponse(response) {
|
|
10
|
+
try {
|
|
11
|
+
// Ensure response has the correct structure
|
|
12
|
+
if (!response || typeof response !== 'object') {
|
|
13
|
+
return createErrorResponse('Invalid response structure');
|
|
14
|
+
}
|
|
15
|
+
// Ensure content array exists
|
|
16
|
+
if (!Array.isArray(response.content)) {
|
|
17
|
+
return createErrorResponse('Response must have content array');
|
|
18
|
+
}
|
|
19
|
+
// Validate and sanitize each content item
|
|
20
|
+
const sanitizedContent = response.content.map((item, index) => {
|
|
21
|
+
try {
|
|
22
|
+
return sanitizeContentItem(item, index);
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
console.error(`Error sanitizing content item ${index}:`, error);
|
|
26
|
+
return {
|
|
27
|
+
type: 'text',
|
|
28
|
+
text: `[Error: Could not render content item ${index}]`
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
// Test JSON serialization
|
|
33
|
+
const testSerialization = JSON.stringify({ content: sanitizedContent });
|
|
34
|
+
// Verify it can be parsed back
|
|
35
|
+
JSON.parse(testSerialization);
|
|
36
|
+
return {
|
|
37
|
+
content: sanitizedContent,
|
|
38
|
+
isError: response.isError || false
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
console.error('Response validation failed:', error);
|
|
43
|
+
return createErrorResponse(`Response validation failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Sanitize a single content item
|
|
48
|
+
*/
|
|
49
|
+
function sanitizeContentItem(item, index) {
|
|
50
|
+
if (!item || typeof item !== 'object') {
|
|
51
|
+
throw new Error(`Content item ${index} is not an object`);
|
|
52
|
+
}
|
|
53
|
+
const type = item.type;
|
|
54
|
+
if (!type || !['text', 'image', 'resource'].includes(type)) {
|
|
55
|
+
throw new Error(`Content item ${index} has invalid type: ${type}`);
|
|
56
|
+
}
|
|
57
|
+
switch (type) {
|
|
58
|
+
case 'text':
|
|
59
|
+
return {
|
|
60
|
+
type: 'text',
|
|
61
|
+
text: sanitizeTextContent(item.text || '')
|
|
62
|
+
};
|
|
63
|
+
case 'image':
|
|
64
|
+
return {
|
|
65
|
+
type: 'image',
|
|
66
|
+
data: sanitizeBase64Content(item.data || ''),
|
|
67
|
+
mimeType: sanitizeMimeType(item.mimeType || 'image/png')
|
|
68
|
+
};
|
|
69
|
+
case 'resource':
|
|
70
|
+
return {
|
|
71
|
+
type: 'resource',
|
|
72
|
+
data: sanitizeTextContent(item.data || ''),
|
|
73
|
+
mimeType: sanitizeMimeType(item.mimeType || 'text/plain')
|
|
74
|
+
};
|
|
75
|
+
default:
|
|
76
|
+
throw new Error(`Unsupported content type: ${type}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Sanitize text content for JSON-RPC safety
|
|
81
|
+
*/
|
|
82
|
+
function sanitizeTextContent(text) {
|
|
83
|
+
if (typeof text !== 'string') {
|
|
84
|
+
text = String(text);
|
|
85
|
+
}
|
|
86
|
+
// Remove or escape problematic characters
|
|
87
|
+
return text
|
|
88
|
+
// Escape backslashes first
|
|
89
|
+
.replace(/\\/g, '\\\\')
|
|
90
|
+
// Escape double quotes
|
|
91
|
+
.replace(/"/g, '\\"')
|
|
92
|
+
// Handle control characters
|
|
93
|
+
.replace(/\n/g, '\\n')
|
|
94
|
+
.replace(/\r/g, '\\r')
|
|
95
|
+
.replace(/\t/g, '\\t')
|
|
96
|
+
// Remove or escape other control characters
|
|
97
|
+
.replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F-\u009F]/g, (char) => {
|
|
98
|
+
const code = char.charCodeAt(0);
|
|
99
|
+
switch (code) {
|
|
100
|
+
case 0x08: return '\\b';
|
|
101
|
+
case 0x0C: return '\\f';
|
|
102
|
+
default:
|
|
103
|
+
// Replace with safe placeholder for other control chars
|
|
104
|
+
return `[CTRL:${code.toString(16).padStart(2, '0')}]`;
|
|
105
|
+
}
|
|
106
|
+
})
|
|
107
|
+
// Limit length to prevent oversized responses
|
|
108
|
+
.slice(0, 1000000); // 1MB limit
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Sanitize base64 content
|
|
112
|
+
*/
|
|
113
|
+
function sanitizeBase64Content(data) {
|
|
114
|
+
if (typeof data !== 'string') {
|
|
115
|
+
return '';
|
|
116
|
+
}
|
|
117
|
+
// Validate base64 format and remove invalid characters
|
|
118
|
+
return data.replace(/[^A-Za-z0-9+/=]/g, '').slice(0, 10000000); // 10MB limit
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Sanitize MIME type
|
|
122
|
+
*/
|
|
123
|
+
function sanitizeMimeType(mimeType) {
|
|
124
|
+
if (typeof mimeType !== 'string') {
|
|
125
|
+
return 'text/plain';
|
|
126
|
+
}
|
|
127
|
+
// Only allow safe MIME types
|
|
128
|
+
const allowedMimeTypes = [
|
|
129
|
+
'text/plain', 'text/html', 'text/markdown', 'text/css', 'text/javascript',
|
|
130
|
+
'application/json', 'application/xml', 'application/yaml',
|
|
131
|
+
'image/png', 'image/jpeg', 'image/gif', 'image/svg+xml', 'image/webp'
|
|
132
|
+
];
|
|
133
|
+
const sanitized = mimeType.toLowerCase().trim();
|
|
134
|
+
return allowedMimeTypes.includes(sanitized) ? sanitized : 'text/plain';
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Create a safe error response
|
|
138
|
+
*/
|
|
139
|
+
function createErrorResponse(message) {
|
|
140
|
+
return {
|
|
141
|
+
content: [{
|
|
142
|
+
type: 'text',
|
|
143
|
+
text: sanitizeTextContent(`Error: ${message}`)
|
|
144
|
+
}],
|
|
145
|
+
isError: true
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Validate that a response can be safely serialized as JSON-RPC 2.0
|
|
150
|
+
*/
|
|
151
|
+
export function validateJsonRpcSerialization(response) {
|
|
152
|
+
try {
|
|
153
|
+
// Test full JSON-RPC 2.0 structure
|
|
154
|
+
const jsonRpcResponse = {
|
|
155
|
+
jsonrpc: '2.0',
|
|
156
|
+
id: 1,
|
|
157
|
+
result: response
|
|
158
|
+
};
|
|
159
|
+
const serialized = JSON.stringify(jsonRpcResponse);
|
|
160
|
+
// Verify it can be parsed back
|
|
161
|
+
const parsed = JSON.parse(serialized);
|
|
162
|
+
// Verify structure is preserved
|
|
163
|
+
if (!parsed.result || !Array.isArray(parsed.result.content)) {
|
|
164
|
+
return { valid: false, error: 'Structure not preserved after serialization' };
|
|
165
|
+
}
|
|
166
|
+
return { valid: true };
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
return {
|
|
170
|
+
valid: false,
|
|
171
|
+
error: `JSON-RPC serialization failed: ${error instanceof Error ? error.message : String(error)}`
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Safe wrapper for MCP tool functions
|
|
177
|
+
*/
|
|
178
|
+
export function safeMcpToolWrapper(toolFunction, toolName) {
|
|
179
|
+
return async (...args) => {
|
|
180
|
+
try {
|
|
181
|
+
const result = await toolFunction(...args);
|
|
182
|
+
const validated = validateMcpResponse(result);
|
|
183
|
+
// Double-check JSON-RPC compatibility
|
|
184
|
+
const jsonRpcCheck = validateJsonRpcSerialization(validated);
|
|
185
|
+
if (!jsonRpcCheck.valid) {
|
|
186
|
+
console.error(`JSON-RPC validation failed for ${toolName}:`, jsonRpcCheck.error);
|
|
187
|
+
return createErrorResponse(`Tool response not JSON-RPC compatible: ${jsonRpcCheck.error}`);
|
|
188
|
+
}
|
|
189
|
+
return validated;
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
console.error(`Error in ${toolName}:`, error);
|
|
193
|
+
return createErrorResponse(`Tool execution failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
//# sourceMappingURL=mcp-response-validator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-response-validator.js","sourceRoot":"","sources":["../../../src/utils/mcp-response-validator.ts"],"names":[],"mappings":"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"}
|