@grafema/mcp 0.3.29 → 0.3.31
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/definitions/enox-tools.d.ts.map +1 -1
- package/dist/definitions/enox-tools.js +5 -10
- package/dist/definitions/enox-tools.js.map +1 -1
- package/dist/handlers/coverage-handlers.d.ts.map +1 -1
- package/dist/handlers/coverage-handlers.js +9 -0
- package/dist/handlers/coverage-handlers.js.map +1 -1
- package/dist/handlers/documentation-handlers.d.ts.map +1 -1
- package/dist/handlers/documentation-handlers.js +26 -5
- package/dist/handlers/documentation-handlers.js.map +1 -1
- package/dist/handlers/enox-handlers.d.ts +11 -2
- package/dist/handlers/enox-handlers.d.ts.map +1 -1
- package/dist/handlers/enox-handlers.js +14 -6
- package/dist/handlers/enox-handlers.js.map +1 -1
- package/dist/handlers/query-handlers.d.ts +7 -0
- package/dist/handlers/query-handlers.d.ts.map +1 -1
- package/dist/handlers/query-handlers.js +28 -14
- package/dist/handlers/query-handlers.js.map +1 -1
- package/package.json +8 -8
- package/src/definitions/enox-tools.ts +5 -10
- package/src/handlers/coverage-handlers.ts +10 -0
- package/src/handlers/documentation-handlers.ts +26 -5
- package/src/handlers/enox-handlers.ts +18 -7
- package/src/handlers/query-handlers.ts +25 -12
- package/dist/definitions.d.ts +0 -23
- package/dist/definitions.d.ts.map +0 -1
- package/dist/definitions.js +0 -644
- package/dist/definitions.js.map +0 -1
- package/dist/handlers.d.ts +0 -61
- package/dist/handlers.d.ts.map +0 -1
- package/dist/handlers.js +0 -1310
- package/dist/handlers.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@grafema/mcp",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.31",
|
|
4
4
|
"description": "MCP server for Grafema code analysis toolkit",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/server.js",
|
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
"import": "./dist/server.js"
|
|
15
15
|
},
|
|
16
16
|
"./handlers": {
|
|
17
|
-
"types": "./dist/handlers.d.ts",
|
|
18
|
-
"import": "./dist/handlers.js"
|
|
17
|
+
"types": "./dist/handlers/index.d.ts",
|
|
18
|
+
"import": "./dist/handlers/index.js"
|
|
19
19
|
}
|
|
20
20
|
},
|
|
21
21
|
"files": [
|
|
@@ -39,9 +39,9 @@
|
|
|
39
39
|
"@modelcontextprotocol/sdk": "^1.25.1",
|
|
40
40
|
"ajv": "^8.17.1",
|
|
41
41
|
"yaml": "^2.8.2",
|
|
42
|
-
"@grafema/api": "0.3.
|
|
43
|
-
"@grafema/
|
|
44
|
-
"@grafema/
|
|
42
|
+
"@grafema/api": "0.3.31",
|
|
43
|
+
"@grafema/types": "0.3.31",
|
|
44
|
+
"@grafema/util": "0.3.31"
|
|
45
45
|
},
|
|
46
46
|
"optionalDependencies": {
|
|
47
47
|
"@grafema/grafema-darwin-arm64": "0.3.29",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"build": "tsc",
|
|
59
59
|
"clean": "rm -rf dist",
|
|
60
60
|
"start": "node dist/server.js",
|
|
61
|
-
"test": "node --import tsx --test test/*.test.ts",
|
|
62
|
-
"test:watch": "node --import tsx --test --watch test/*.test.ts"
|
|
61
|
+
"test": "node --import tsx --experimental-test-module-mocks --test --test-concurrency=1 test/*.test.ts",
|
|
62
|
+
"test:watch": "node --import tsx --experimental-test-module-mocks --test --test-concurrency=1 --watch test/*.test.ts"
|
|
63
63
|
}
|
|
64
64
|
}
|
|
@@ -124,14 +124,13 @@ Example: recall(query="federation architecture", depth=2)`,
|
|
|
124
124
|
},
|
|
125
125
|
{
|
|
126
126
|
name: 'semantic_search',
|
|
127
|
-
description: `
|
|
127
|
+
description: `Substring search across knowledge-graph node names (case-insensitive).
|
|
128
128
|
|
|
129
|
-
|
|
130
|
-
|
|
129
|
+
NOTE: despite the name, embedding-based semantic ranking is not wired yet
|
|
130
|
+
(RFD-63). This currently matches substrings of node names — results are
|
|
131
|
+
plain matches, not similarity-ranked. Use recall for broader retrieval.
|
|
131
132
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
Example: semantic_search(query="Docker container auth token", top_k=5, domain="devops")`,
|
|
133
|
+
Example: semantic_search(query="auth token", top_k=5, domain="devops")`,
|
|
135
134
|
inputSchema: {
|
|
136
135
|
type: 'object',
|
|
137
136
|
properties: {
|
|
@@ -147,10 +146,6 @@ Example: semantic_search(query="Docker container auth token", top_k=5, domain="d
|
|
|
147
146
|
type: 'string',
|
|
148
147
|
description: 'Filter results to a specific domain',
|
|
149
148
|
},
|
|
150
|
-
include_edges: {
|
|
151
|
-
type: 'boolean',
|
|
152
|
-
description: 'Include edges connected to matched nodes (default: false)',
|
|
153
|
-
},
|
|
154
149
|
},
|
|
155
150
|
required: ['query'],
|
|
156
151
|
},
|
|
@@ -31,9 +31,19 @@ export async function handleGetCoverage(args: GetCoverageArgs): Promise<ToolResu
|
|
|
31
31
|
output += `File breakdown:\n`;
|
|
32
32
|
output += ` Total files: ${result.total}\n`;
|
|
33
33
|
output += ` Analyzed: ${result.analyzed.count} (${result.percentages.analyzed}%) - in graph\n`;
|
|
34
|
+
if (result.failed.count > 0) {
|
|
35
|
+
output += ` Failed: ${result.failed.count} (${result.percentages.failed}%) - skipped/failed during analysis\n`;
|
|
36
|
+
}
|
|
34
37
|
output += ` Unsupported: ${result.unsupported.count} (${result.percentages.unsupported}%) - no indexer available\n`;
|
|
35
38
|
output += ` Unreachable: ${result.unreachable.count} (${result.percentages.unreachable}%) - not imported from entrypoints\n`;
|
|
36
39
|
|
|
40
|
+
if (result.failed.count > 0) {
|
|
41
|
+
output += `\nFailed files by reason:\n`;
|
|
42
|
+
for (const [category, files] of Object.entries(result.failed.byCategory)) {
|
|
43
|
+
output += ` ${category}: ${files.length} files\n`;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
37
47
|
if (result.unsupported.count > 0) {
|
|
38
48
|
output += `\nUnsupported files by extension:\n`;
|
|
39
49
|
for (const [ext, files] of Object.entries(result.unsupported.byExtension)) {
|
|
@@ -40,11 +40,21 @@ Grafema is a static code analyzer that builds a graph of your codebase.
|
|
|
40
40
|
## Syntax
|
|
41
41
|
violation(X) :- node(X, "TYPE"), attr(X, "name", "value").
|
|
42
42
|
|
|
43
|
-
##
|
|
44
|
-
-
|
|
45
|
-
- edge(Src, Dst, Type) - match edges
|
|
46
|
-
-
|
|
47
|
-
-
|
|
43
|
+
## Node / Edge / Attribute Predicates
|
|
44
|
+
- node(Id, Type) - match nodes by type (alias: type(Id, Type))
|
|
45
|
+
- edge(Src, Dst, Type) - match outgoing edges Src -> Dst of the given type
|
|
46
|
+
- incoming(Dst, Src, Type) - match edges pointing TO Dst (reverse of edge)
|
|
47
|
+
- path(Src, Dst) - transitive reachability Src -> Dst (BFS over edges)
|
|
48
|
+
- attr(Id, Name, Value) - match node attributes (name, file, line, ...; nested paths like "a.b" supported)
|
|
49
|
+
- attr_edge(Src, Dst, EdgeType, AttrName, Value) - match EDGE attributes; Src/Dst/EdgeType/AttrName must be bound, Value may be variable/constant/wildcard
|
|
50
|
+
- parent_function(NodeId, FunctionId) - bind the enclosing FUNCTION of NodeId via CONTAINS; NodeId must be bound; empty at module level
|
|
51
|
+
|
|
52
|
+
## Negation & String Predicates
|
|
53
|
+
- \\+ - negation (not); also for negative joins on a dst-position variable, e.g. \\+ edge(X, _, "CALLS")
|
|
54
|
+
- neq(X, Y) - inequality; BOTH arguments must be bound
|
|
55
|
+
- starts_with(Value, Prefix) - string prefix match
|
|
56
|
+
- not_starts_with(Value, Prefix) - negative string prefix match
|
|
57
|
+
- string_contains(Value, Substring) - substring match
|
|
48
58
|
|
|
49
59
|
## Numeric Comparison Predicates
|
|
50
60
|
- gt(Value, Threshold) - greater than
|
|
@@ -55,6 +65,11 @@ violation(X) :- node(X, "TYPE"), attr(X, "name", "value").
|
|
|
55
65
|
Values are parsed as floating-point numbers. Non-numeric values produce no matches.
|
|
56
66
|
Use with attr() to filter by metadata values (e.g., metrics, line numbers).
|
|
57
67
|
|
|
68
|
+
## Limitations
|
|
69
|
+
- No aggregations (count/sum/avg), no GROUP BY, no ORDER BY - aggregate/sort client-side.
|
|
70
|
+
- Predicate ORDER matters: bind a variable (via node/edge/attr) before a comparison /
|
|
71
|
+
string / attr_edge predicate uses it, or the query fails to place ("circular dependency").
|
|
72
|
+
|
|
58
73
|
## Examples
|
|
59
74
|
Find all functions:
|
|
60
75
|
violation(X) :- node(X, "FUNCTION").
|
|
@@ -67,6 +82,12 @@ Find files where parsing took > 500ms:
|
|
|
67
82
|
|
|
68
83
|
Find functions with more than 100 lines:
|
|
69
84
|
violation(X, Lines) :- node(X, "FUNCTION"), attr(X, "line", Start), attr(X, "endLine", End), gt(End, Start).
|
|
85
|
+
|
|
86
|
+
Find the enclosing function of every call:
|
|
87
|
+
violation(C, F) :- node(C, "CALL"), parent_function(C, F).
|
|
88
|
+
|
|
89
|
+
Find nodes that have incoming CALLS edges (i.e. are called):
|
|
90
|
+
violation(D) :- incoming(D, _, "CALLS").
|
|
70
91
|
`,
|
|
71
92
|
types: `
|
|
72
93
|
# Node & Edge Types
|
|
@@ -348,14 +348,25 @@ export async function handleRecall(args: RecallArgs): Promise<ToolResult> {
|
|
|
348
348
|
|
|
349
349
|
export interface SemanticSearchArgs {
|
|
350
350
|
query: string;
|
|
351
|
-
|
|
351
|
+
top_k?: number;
|
|
352
352
|
domain?: string;
|
|
353
353
|
}
|
|
354
354
|
|
|
355
|
-
|
|
355
|
+
/**
|
|
356
|
+
* Handle the `semantic_search` MCP tool.
|
|
357
|
+
*
|
|
358
|
+
* @param args - tool input. `top_k` (matching the published schema) caps the
|
|
359
|
+
* number of results; defaults to 10 per the schema's documented default.
|
|
360
|
+
* @param clientOverride - optional RFDB client, injected by tests; production
|
|
361
|
+
* callers omit it and the shared knowledge client is used.
|
|
362
|
+
*/
|
|
363
|
+
export async function handleSemanticSearch(
|
|
364
|
+
args: SemanticSearchArgs,
|
|
365
|
+
clientOverride?: RFDBClient,
|
|
366
|
+
): Promise<ToolResult> {
|
|
356
367
|
try {
|
|
357
|
-
const client = await getKnowledgeClient();
|
|
358
|
-
const limit = args.
|
|
368
|
+
const client = clientOverride ?? await getKnowledgeClient();
|
|
369
|
+
const limit = args.top_k ?? 10;
|
|
359
370
|
|
|
360
371
|
// TODO: Wire to RFDB embedding engine when enabled.
|
|
361
372
|
// For now, fall back to substring search via queryNodes.
|
|
@@ -378,9 +389,9 @@ export async function handleSemanticSearch(args: SemanticSearchArgs): Promise<To
|
|
|
378
389
|
for (let i = 0; i < results.length; i++) {
|
|
379
390
|
const node = results[i];
|
|
380
391
|
const meta = parseMeta(node);
|
|
381
|
-
//
|
|
382
|
-
|
|
383
|
-
lines.push(`${i + 1}.
|
|
392
|
+
// NOTE: this is substring matching, not embeddings — do not fabricate a
|
|
393
|
+
// similarity score (it would read to an agent as a real metric). Just rank.
|
|
394
|
+
lines.push(`${i + 1}. ${node.name} (${node.nodeType})`);
|
|
384
395
|
if (meta.domain) lines.push(` Domain: ${meta.domain}`);
|
|
385
396
|
if (meta.content) lines.push(` ${String(meta.content).slice(0, 200)}`);
|
|
386
397
|
lines.push('');
|
|
@@ -524,22 +524,35 @@ async function enrichNodes(
|
|
|
524
524
|
|
|
525
525
|
/** Convert a semantic ID to human-readable "name in file.ts" format.
|
|
526
526
|
* Handles: grafema://host/path/file.ts#TYPE->name[scope], path/file.ts#TYPE->name,
|
|
527
|
-
* TYPE-%3Ename (URL-encoded, no file prefix), or raw node IDs.
|
|
528
|
-
|
|
527
|
+
* TYPE-%3Ename (URL-encoded, no file prefix), or raw node IDs.
|
|
528
|
+
*
|
|
529
|
+
* Exported for unit testing.
|
|
530
|
+
* @internal */
|
|
531
|
+
export function humanReadableId(semanticId: string): string {
|
|
529
532
|
if (!semanticId) return '?';
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
//
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
533
|
+
|
|
534
|
+
// Split file path from node descriptor on the FIRST literal '#' of the RAW
|
|
535
|
+
// (still percent-encoded) id, BEFORE decoding. The grafema:// URI form encodes
|
|
536
|
+
// the disambiguation counter ('#N', appended by computeSemanticIdV2 for hash
|
|
537
|
+
// collisions) as '%23N' inside the fragment. Decoding first and then splitting
|
|
538
|
+
// on the LAST '#' would mistake that counter for the path/fragment boundary and
|
|
539
|
+
// corrupt the label. A file path never contains a literal '#'. This mirrors the
|
|
540
|
+
// canonical parseSemanticIdV2 (packages/util/src/core/SemanticId.ts).
|
|
541
|
+
const hashIdx = semanticId.indexOf('#');
|
|
542
|
+
let rawFile = '';
|
|
543
|
+
let rawNode = semanticId;
|
|
538
544
|
if (hashIdx !== -1) {
|
|
539
|
-
|
|
540
|
-
|
|
545
|
+
rawFile = semanticId.slice(0, hashIdx);
|
|
546
|
+
rawNode = semanticId.slice(hashIdx + 1);
|
|
541
547
|
}
|
|
542
548
|
|
|
549
|
+
const decode = (s: string): string => {
|
|
550
|
+
try { return decodeURIComponent(s); } catch { return s; }
|
|
551
|
+
};
|
|
552
|
+
const filePart = decode(rawFile);
|
|
553
|
+
// Strip the trailing disambiguation counter ('#N') from the decoded descriptor.
|
|
554
|
+
const nodePart = decode(rawNode).replace(/#\d+$/, '');
|
|
555
|
+
|
|
543
556
|
const fileName = filePart ? (filePart.split('/').pop() || '') : '';
|
|
544
557
|
|
|
545
558
|
// Extract name from TYPE->name or TYPE->name[in:scope,h:hash]
|
package/dist/definitions.d.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MCP Tool Definitions
|
|
3
|
-
*/
|
|
4
|
-
interface SchemaProperty {
|
|
5
|
-
type: string;
|
|
6
|
-
description?: string;
|
|
7
|
-
enum?: string[];
|
|
8
|
-
items?: SchemaProperty;
|
|
9
|
-
properties?: Record<string, SchemaProperty>;
|
|
10
|
-
required?: string[];
|
|
11
|
-
}
|
|
12
|
-
export interface ToolDefinition {
|
|
13
|
-
name: string;
|
|
14
|
-
description: string;
|
|
15
|
-
inputSchema: {
|
|
16
|
-
type: 'object';
|
|
17
|
-
properties: Record<string, SchemaProperty>;
|
|
18
|
-
required?: string[];
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
|
-
export declare const TOOLS: ToolDefinition[];
|
|
22
|
-
export {};
|
|
23
|
-
//# sourceMappingURL=definitions.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"definitions.d.ts","sourceRoot":"","sources":["../src/definitions.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,UAAU,cAAc;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAC5C,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ,CAAC;QACf,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;QAC3C,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;KACrB,CAAC;CACH;AAED,eAAO,MAAM,KAAK,EAAE,cAAc,EA+nBjC,CAAC"}
|