@grafema/mcp 0.1.0-alpha.1
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/LICENSE +190 -0
- package/README.md +67 -0
- package/dist/analysis-worker.d.ts +9 -0
- package/dist/analysis-worker.d.ts.map +1 -0
- package/dist/analysis-worker.js +194 -0
- package/dist/analysis.d.ts +13 -0
- package/dist/analysis.d.ts.map +1 -0
- package/dist/analysis.js +116 -0
- package/dist/config.d.ts +34 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +165 -0
- package/dist/definitions.d.ts +23 -0
- package/dist/definitions.d.ts.map +1 -0
- package/dist/definitions.js +419 -0
- package/dist/handlers.d.ts +34 -0
- package/dist/handlers.d.ts.map +1 -0
- package/dist/handlers.js +747 -0
- package/dist/server.d.ts +8 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +122 -0
- package/dist/state.d.ts +23 -0
- package/dist/state.d.ts.map +1 -0
- package/dist/state.js +138 -0
- package/dist/types.d.ts +197 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +4 -0
- package/dist/utils.d.ts +24 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +180 -0
- package/package.json +53 -0
- package/src/.rfguard/current-session.txt +1 -0
- package/src/analysis-worker.ts +286 -0
- package/src/analysis.ts +158 -0
- package/src/config.ts +240 -0
- package/src/definitions.ts +438 -0
- package/src/handlers.ts +918 -0
- package/src/server.ts +178 -0
- package/src/state.ts +169 -0
- package/src/types.ts +245 -0
- package/src/utils.ts +204 -0
package/src/config.ts
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Server Configuration
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { join } from 'path';
|
|
6
|
+
import { existsSync, readFileSync, writeFileSync, readdirSync } from 'fs';
|
|
7
|
+
import { pathToFileURL } from 'url';
|
|
8
|
+
import { log } from './utils.js';
|
|
9
|
+
import type { GrafemaConfig } from './types.js';
|
|
10
|
+
|
|
11
|
+
// === PLUGIN IMPORTS ===
|
|
12
|
+
import {
|
|
13
|
+
// Indexing
|
|
14
|
+
JSModuleIndexer,
|
|
15
|
+
RustModuleIndexer,
|
|
16
|
+
// Analysis
|
|
17
|
+
JSASTAnalyzer,
|
|
18
|
+
ExpressRouteAnalyzer,
|
|
19
|
+
SocketIOAnalyzer,
|
|
20
|
+
DatabaseAnalyzer,
|
|
21
|
+
FetchAnalyzer,
|
|
22
|
+
ServiceLayerAnalyzer,
|
|
23
|
+
ReactAnalyzer,
|
|
24
|
+
RustAnalyzer,
|
|
25
|
+
// Enrichment
|
|
26
|
+
MethodCallResolver,
|
|
27
|
+
AliasTracker,
|
|
28
|
+
ValueDomainAnalyzer,
|
|
29
|
+
MountPointResolver,
|
|
30
|
+
PrefixEvaluator,
|
|
31
|
+
InstanceOfResolver,
|
|
32
|
+
HTTPConnectionEnricher,
|
|
33
|
+
RustFFIEnricher,
|
|
34
|
+
// Validation
|
|
35
|
+
CallResolverValidator,
|
|
36
|
+
EvalBanValidator,
|
|
37
|
+
SQLInjectionValidator,
|
|
38
|
+
ShadowingDetector,
|
|
39
|
+
GraphConnectivityValidator,
|
|
40
|
+
DataFlowValidator,
|
|
41
|
+
TypeScriptDeadCodeValidator,
|
|
42
|
+
} from '@grafema/core';
|
|
43
|
+
|
|
44
|
+
// === DEFAULT CONFIG ===
|
|
45
|
+
export interface PluginConfig {
|
|
46
|
+
indexing: string[];
|
|
47
|
+
analysis: string[];
|
|
48
|
+
enrichment: string[];
|
|
49
|
+
validation: string[];
|
|
50
|
+
discovery?: string[];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface ProjectConfig {
|
|
54
|
+
plugins: PluginConfig;
|
|
55
|
+
discovery: {
|
|
56
|
+
enabled: boolean;
|
|
57
|
+
customOnly: boolean;
|
|
58
|
+
};
|
|
59
|
+
analysis?: {
|
|
60
|
+
service?: string;
|
|
61
|
+
};
|
|
62
|
+
backend?: 'local' | 'rfdb';
|
|
63
|
+
rfdb_socket?: string;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export const DEFAULT_CONFIG: ProjectConfig = {
|
|
67
|
+
plugins: {
|
|
68
|
+
indexing: ['JSModuleIndexer'],
|
|
69
|
+
analysis: [
|
|
70
|
+
'JSASTAnalyzer',
|
|
71
|
+
'ExpressRouteAnalyzer',
|
|
72
|
+
'SocketIOAnalyzer',
|
|
73
|
+
'DatabaseAnalyzer',
|
|
74
|
+
'FetchAnalyzer',
|
|
75
|
+
'ServiceLayerAnalyzer',
|
|
76
|
+
],
|
|
77
|
+
enrichment: [
|
|
78
|
+
'MethodCallResolver',
|
|
79
|
+
'AliasTracker',
|
|
80
|
+
'ValueDomainAnalyzer',
|
|
81
|
+
'MountPointResolver',
|
|
82
|
+
'PrefixEvaluator',
|
|
83
|
+
'HTTPConnectionEnricher',
|
|
84
|
+
],
|
|
85
|
+
validation: [
|
|
86
|
+
'CallResolverValidator',
|
|
87
|
+
'EvalBanValidator',
|
|
88
|
+
'SQLInjectionValidator',
|
|
89
|
+
'ShadowingDetector',
|
|
90
|
+
'GraphConnectivityValidator',
|
|
91
|
+
'DataFlowValidator',
|
|
92
|
+
'TypeScriptDeadCodeValidator',
|
|
93
|
+
],
|
|
94
|
+
},
|
|
95
|
+
discovery: {
|
|
96
|
+
enabled: true,
|
|
97
|
+
customOnly: false,
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
// === BUILTIN PLUGINS ===
|
|
102
|
+
type PluginFactory = () => unknown;
|
|
103
|
+
|
|
104
|
+
export const BUILTIN_PLUGINS: Record<string, PluginFactory> = {
|
|
105
|
+
// Indexing
|
|
106
|
+
JSModuleIndexer: () => new JSModuleIndexer(),
|
|
107
|
+
RustModuleIndexer: () => new RustModuleIndexer(),
|
|
108
|
+
|
|
109
|
+
// Analysis
|
|
110
|
+
JSASTAnalyzer: () => new JSASTAnalyzer(),
|
|
111
|
+
ExpressRouteAnalyzer: () => new ExpressRouteAnalyzer(),
|
|
112
|
+
SocketIOAnalyzer: () => new SocketIOAnalyzer(),
|
|
113
|
+
DatabaseAnalyzer: () => new DatabaseAnalyzer(),
|
|
114
|
+
FetchAnalyzer: () => new FetchAnalyzer(),
|
|
115
|
+
ServiceLayerAnalyzer: () => new ServiceLayerAnalyzer(),
|
|
116
|
+
ReactAnalyzer: () => new ReactAnalyzer(),
|
|
117
|
+
RustAnalyzer: () => new RustAnalyzer(),
|
|
118
|
+
|
|
119
|
+
// Enrichment
|
|
120
|
+
MethodCallResolver: () => new MethodCallResolver(),
|
|
121
|
+
AliasTracker: () => new AliasTracker(),
|
|
122
|
+
ValueDomainAnalyzer: () => new ValueDomainAnalyzer(),
|
|
123
|
+
MountPointResolver: () => new MountPointResolver(),
|
|
124
|
+
PrefixEvaluator: () => new PrefixEvaluator(),
|
|
125
|
+
InstanceOfResolver: () => new InstanceOfResolver(),
|
|
126
|
+
HTTPConnectionEnricher: () => new HTTPConnectionEnricher(),
|
|
127
|
+
RustFFIEnricher: () => new RustFFIEnricher(),
|
|
128
|
+
|
|
129
|
+
// Validation
|
|
130
|
+
CallResolverValidator: () => new CallResolverValidator(),
|
|
131
|
+
EvalBanValidator: () => new EvalBanValidator(),
|
|
132
|
+
SQLInjectionValidator: () => new SQLInjectionValidator(),
|
|
133
|
+
ShadowingDetector: () => new ShadowingDetector(),
|
|
134
|
+
GraphConnectivityValidator: () => new GraphConnectivityValidator(),
|
|
135
|
+
DataFlowValidator: () => new DataFlowValidator(),
|
|
136
|
+
TypeScriptDeadCodeValidator: () => new TypeScriptDeadCodeValidator(),
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
// === CONFIG LOADING ===
|
|
140
|
+
export function loadConfig(projectPath: string): ProjectConfig {
|
|
141
|
+
const configPath = join(projectPath, '.grafema', 'config.json');
|
|
142
|
+
|
|
143
|
+
if (!existsSync(configPath)) {
|
|
144
|
+
try {
|
|
145
|
+
const grafemaDir = join(projectPath, '.grafema');
|
|
146
|
+
if (!existsSync(grafemaDir)) {
|
|
147
|
+
const { mkdirSync } = require('fs');
|
|
148
|
+
mkdirSync(grafemaDir, { recursive: true });
|
|
149
|
+
}
|
|
150
|
+
writeFileSync(configPath, JSON.stringify(DEFAULT_CONFIG, null, 2));
|
|
151
|
+
log(`[Grafema MCP] Created default config: ${configPath}`);
|
|
152
|
+
} catch (err) {
|
|
153
|
+
log(`[Grafema MCP] Failed to create config: ${(err as Error).message}`);
|
|
154
|
+
}
|
|
155
|
+
return DEFAULT_CONFIG;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
try {
|
|
159
|
+
const configContent = readFileSync(configPath, 'utf-8');
|
|
160
|
+
const config = JSON.parse(configContent) as Partial<ProjectConfig>;
|
|
161
|
+
log(`[Grafema MCP] Loaded config from ${configPath}`);
|
|
162
|
+
return { ...DEFAULT_CONFIG, ...config };
|
|
163
|
+
} catch (err) {
|
|
164
|
+
log(`[Grafema MCP] Failed to load config: ${(err as Error).message}, using defaults`);
|
|
165
|
+
return DEFAULT_CONFIG;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// === CUSTOM PLUGINS ===
|
|
170
|
+
export interface CustomPluginResult {
|
|
171
|
+
plugins: unknown[];
|
|
172
|
+
pluginMap: Record<string, new () => unknown>;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export async function loadCustomPlugins(projectPath: string): Promise<CustomPluginResult> {
|
|
176
|
+
const pluginsDir = join(projectPath, '.grafema', 'plugins');
|
|
177
|
+
if (!existsSync(pluginsDir)) {
|
|
178
|
+
return { plugins: [], pluginMap: {} };
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const customPlugins: unknown[] = [];
|
|
182
|
+
const pluginMap: Record<string, new () => unknown> = {};
|
|
183
|
+
|
|
184
|
+
try {
|
|
185
|
+
const files = readdirSync(pluginsDir).filter(
|
|
186
|
+
(f) => f.endsWith('.js') || f.endsWith('.mjs')
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
for (const file of files) {
|
|
190
|
+
try {
|
|
191
|
+
const pluginPath = join(pluginsDir, file);
|
|
192
|
+
const pluginUrl = pathToFileURL(pluginPath).href;
|
|
193
|
+
const module = await import(pluginUrl);
|
|
194
|
+
|
|
195
|
+
const PluginClass = module.default || module[file.replace(/\.(m?js)$/, '')];
|
|
196
|
+
if (PluginClass && typeof PluginClass === 'function') {
|
|
197
|
+
const pluginName = PluginClass.name || file.replace(/\.(m?js)$/, '');
|
|
198
|
+
customPlugins.push(new PluginClass());
|
|
199
|
+
pluginMap[pluginName] = PluginClass;
|
|
200
|
+
log(`[Grafema MCP] Loaded custom plugin: ${pluginName} from ${file}`);
|
|
201
|
+
}
|
|
202
|
+
} catch (err) {
|
|
203
|
+
log(`[Grafema MCP] Failed to load plugin ${file}: ${(err as Error).message}`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
} catch (err) {
|
|
207
|
+
log(`[Grafema MCP] Error loading custom plugins: ${(err as Error).message}`);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return { plugins: customPlugins, pluginMap };
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// === PLUGIN INSTANTIATION ===
|
|
214
|
+
export function createPlugins(
|
|
215
|
+
pluginNames: string[],
|
|
216
|
+
customPluginMap: Record<string, new () => unknown> = {}
|
|
217
|
+
): unknown[] {
|
|
218
|
+
const plugins: unknown[] = [];
|
|
219
|
+
const availablePlugins: Record<string, PluginFactory> = {
|
|
220
|
+
...BUILTIN_PLUGINS,
|
|
221
|
+
...Object.fromEntries(
|
|
222
|
+
Object.entries(customPluginMap).map(([name, PluginClass]) => [
|
|
223
|
+
name,
|
|
224
|
+
() => new PluginClass(),
|
|
225
|
+
])
|
|
226
|
+
),
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
for (const name of pluginNames) {
|
|
230
|
+
const factory = availablePlugins[name];
|
|
231
|
+
if (factory) {
|
|
232
|
+
plugins.push(factory());
|
|
233
|
+
log(`[Grafema MCP] Enabled plugin: ${name}`);
|
|
234
|
+
} else {
|
|
235
|
+
log(`[Grafema MCP] Warning: Unknown plugin ${name}`);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return plugins;
|
|
240
|
+
}
|
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Tool Definitions
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { DEFAULT_LIMIT, MAX_LIMIT } from './utils.js';
|
|
6
|
+
|
|
7
|
+
interface SchemaProperty {
|
|
8
|
+
type: string;
|
|
9
|
+
description: string;
|
|
10
|
+
enum?: string[];
|
|
11
|
+
items?: { type: string };
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface ToolDefinition {
|
|
15
|
+
name: string;
|
|
16
|
+
description: string;
|
|
17
|
+
inputSchema: {
|
|
18
|
+
type: 'object';
|
|
19
|
+
properties: Record<string, SchemaProperty>;
|
|
20
|
+
required?: string[];
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const TOOLS: ToolDefinition[] = [
|
|
25
|
+
{
|
|
26
|
+
name: 'query_graph',
|
|
27
|
+
description: `Execute a Datalog query on the code graph.
|
|
28
|
+
|
|
29
|
+
Available predicates:
|
|
30
|
+
- node(Id, Type) - match nodes by type
|
|
31
|
+
- edge(Src, Dst, Type) - match edges
|
|
32
|
+
- attr(Id, Name, Value) - match node attributes (name, file, line, etc.)
|
|
33
|
+
|
|
34
|
+
NODE TYPES:
|
|
35
|
+
- MODULE, FUNCTION, METHOD, CLASS, VARIABLE, PARAMETER
|
|
36
|
+
- CALL, METHOD_CALL, CALL_SITE
|
|
37
|
+
- http:route, http:request, db:query, socketio:emit, socketio:on
|
|
38
|
+
|
|
39
|
+
EDGE TYPES:
|
|
40
|
+
- CONTAINS, CALLS, DEPENDS_ON, ASSIGNED_FROM, INSTANCE_OF, PASSES_ARGUMENT
|
|
41
|
+
|
|
42
|
+
EXAMPLES:
|
|
43
|
+
violation(X) :- node(X, "MODULE").
|
|
44
|
+
violation(X) :- node(X, "FUNCTION"), attr(X, "file", "src/api.js").
|
|
45
|
+
violation(X) :- node(X, "CALL"), \\+ edge(X, _, "CALLS").`,
|
|
46
|
+
inputSchema: {
|
|
47
|
+
type: 'object',
|
|
48
|
+
properties: {
|
|
49
|
+
query: {
|
|
50
|
+
type: 'string',
|
|
51
|
+
description: 'Datalog query. Must define violation/1 predicate for results.',
|
|
52
|
+
},
|
|
53
|
+
limit: {
|
|
54
|
+
type: 'number',
|
|
55
|
+
description: `Max results to return (default: ${DEFAULT_LIMIT}, max: ${MAX_LIMIT})`,
|
|
56
|
+
},
|
|
57
|
+
offset: {
|
|
58
|
+
type: 'number',
|
|
59
|
+
description: 'Skip first N results for pagination (default: 0)',
|
|
60
|
+
},
|
|
61
|
+
explain: {
|
|
62
|
+
type: 'boolean',
|
|
63
|
+
description: 'Show step-by-step query execution to debug empty results',
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
required: ['query'],
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
name: 'find_calls',
|
|
71
|
+
description: `Find all calls to a specific function or method.
|
|
72
|
+
Returns call sites with file locations and whether they're resolved.`,
|
|
73
|
+
inputSchema: {
|
|
74
|
+
type: 'object',
|
|
75
|
+
properties: {
|
|
76
|
+
name: {
|
|
77
|
+
type: 'string',
|
|
78
|
+
description: 'Function or method name to find calls for',
|
|
79
|
+
},
|
|
80
|
+
className: {
|
|
81
|
+
type: 'string',
|
|
82
|
+
description: 'Optional: class name for method calls',
|
|
83
|
+
},
|
|
84
|
+
limit: {
|
|
85
|
+
type: 'number',
|
|
86
|
+
description: `Max results (default: ${DEFAULT_LIMIT}, max: ${MAX_LIMIT})`,
|
|
87
|
+
},
|
|
88
|
+
offset: {
|
|
89
|
+
type: 'number',
|
|
90
|
+
description: 'Skip first N results (default: 0)',
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
required: ['name'],
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
name: 'find_nodes',
|
|
98
|
+
description: `Find nodes in the graph by type, name, or file.`,
|
|
99
|
+
inputSchema: {
|
|
100
|
+
type: 'object',
|
|
101
|
+
properties: {
|
|
102
|
+
type: {
|
|
103
|
+
type: 'string',
|
|
104
|
+
description: 'Node type (e.g., FUNCTION, CLASS, MODULE)',
|
|
105
|
+
},
|
|
106
|
+
name: {
|
|
107
|
+
type: 'string',
|
|
108
|
+
description: 'Node name pattern',
|
|
109
|
+
},
|
|
110
|
+
file: {
|
|
111
|
+
type: 'string',
|
|
112
|
+
description: 'File path pattern',
|
|
113
|
+
},
|
|
114
|
+
limit: {
|
|
115
|
+
type: 'number',
|
|
116
|
+
description: `Max results (default: ${DEFAULT_LIMIT}, max: ${MAX_LIMIT})`,
|
|
117
|
+
},
|
|
118
|
+
offset: {
|
|
119
|
+
type: 'number',
|
|
120
|
+
description: 'Skip first N results (default: 0)',
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
name: 'trace_alias',
|
|
127
|
+
description: `Trace an alias chain to find the original source.
|
|
128
|
+
For code like: const alias = obj.method; alias();
|
|
129
|
+
This traces "alias" back to "obj.method".`,
|
|
130
|
+
inputSchema: {
|
|
131
|
+
type: 'object',
|
|
132
|
+
properties: {
|
|
133
|
+
variableName: {
|
|
134
|
+
type: 'string',
|
|
135
|
+
description: 'Variable name to trace',
|
|
136
|
+
},
|
|
137
|
+
file: {
|
|
138
|
+
type: 'string',
|
|
139
|
+
description: 'File path where the variable is defined',
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
required: ['variableName', 'file'],
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
name: 'trace_dataflow',
|
|
147
|
+
description: `Trace data flow from/to a variable or expression.`,
|
|
148
|
+
inputSchema: {
|
|
149
|
+
type: 'object',
|
|
150
|
+
properties: {
|
|
151
|
+
source: {
|
|
152
|
+
type: 'string',
|
|
153
|
+
description: 'Variable or node ID to trace from',
|
|
154
|
+
},
|
|
155
|
+
file: {
|
|
156
|
+
type: 'string',
|
|
157
|
+
description: 'File path',
|
|
158
|
+
},
|
|
159
|
+
direction: {
|
|
160
|
+
type: 'string',
|
|
161
|
+
description: 'forward, backward, or both (default: forward)',
|
|
162
|
+
enum: ['forward', 'backward', 'both'],
|
|
163
|
+
},
|
|
164
|
+
max_depth: {
|
|
165
|
+
type: 'number',
|
|
166
|
+
description: 'Maximum trace depth (default: 10)',
|
|
167
|
+
},
|
|
168
|
+
limit: {
|
|
169
|
+
type: 'number',
|
|
170
|
+
description: `Max results (default: ${DEFAULT_LIMIT})`,
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
required: ['source'],
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
name: 'check_invariant',
|
|
178
|
+
description: `Check a code invariant using a Datalog rule.
|
|
179
|
+
Returns violations if the invariant is broken.`,
|
|
180
|
+
inputSchema: {
|
|
181
|
+
type: 'object',
|
|
182
|
+
properties: {
|
|
183
|
+
rule: {
|
|
184
|
+
type: 'string',
|
|
185
|
+
description: 'Datalog rule defining violation/1',
|
|
186
|
+
},
|
|
187
|
+
description: {
|
|
188
|
+
type: 'string',
|
|
189
|
+
description: 'Human-readable description',
|
|
190
|
+
},
|
|
191
|
+
limit: {
|
|
192
|
+
type: 'number',
|
|
193
|
+
description: `Max violations (default: ${DEFAULT_LIMIT})`,
|
|
194
|
+
},
|
|
195
|
+
offset: {
|
|
196
|
+
type: 'number',
|
|
197
|
+
description: 'Skip first N violations (default: 0)',
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
required: ['rule'],
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
name: 'discover_services',
|
|
205
|
+
description: `Discover services in the project without full analysis.`,
|
|
206
|
+
inputSchema: {
|
|
207
|
+
type: 'object',
|
|
208
|
+
properties: {},
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
name: 'analyze_project',
|
|
213
|
+
description: `Run full analysis on the project or a specific service.`,
|
|
214
|
+
inputSchema: {
|
|
215
|
+
type: 'object',
|
|
216
|
+
properties: {
|
|
217
|
+
service: {
|
|
218
|
+
type: 'string',
|
|
219
|
+
description: 'Optional: analyze only this service',
|
|
220
|
+
},
|
|
221
|
+
force: {
|
|
222
|
+
type: 'boolean',
|
|
223
|
+
description: 'Force re-analysis even if already analyzed',
|
|
224
|
+
},
|
|
225
|
+
index_only: {
|
|
226
|
+
type: 'boolean',
|
|
227
|
+
description: 'Only index modules, skip full analysis',
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
name: 'get_analysis_status',
|
|
234
|
+
description: `Get the current analysis status and progress.`,
|
|
235
|
+
inputSchema: {
|
|
236
|
+
type: 'object',
|
|
237
|
+
properties: {},
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
name: 'get_stats',
|
|
242
|
+
description: `Get graph statistics: node and edge counts by type.`,
|
|
243
|
+
inputSchema: {
|
|
244
|
+
type: 'object',
|
|
245
|
+
properties: {},
|
|
246
|
+
},
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
name: 'get_schema',
|
|
250
|
+
description: `Get the graph schema: available node and edge types.`,
|
|
251
|
+
inputSchema: {
|
|
252
|
+
type: 'object',
|
|
253
|
+
properties: {
|
|
254
|
+
type: {
|
|
255
|
+
type: 'string',
|
|
256
|
+
description: 'nodes, edges, or all (default: all)',
|
|
257
|
+
enum: ['nodes', 'edges', 'all'],
|
|
258
|
+
},
|
|
259
|
+
},
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
// Guarantee tools
|
|
263
|
+
{
|
|
264
|
+
name: 'create_guarantee',
|
|
265
|
+
description: `Create a new code guarantee.
|
|
266
|
+
|
|
267
|
+
Two types supported:
|
|
268
|
+
1. Datalog-based: Uses rule field with Datalog query (violation/1)
|
|
269
|
+
2. Contract-based: Uses type + schema for JSON validation
|
|
270
|
+
|
|
271
|
+
Examples:
|
|
272
|
+
- Datalog: name="no-eval" rule="violation(X) :- node(X, \"CALL\"), attr(X, \"name\", \"eval\")."
|
|
273
|
+
- Contract: name="orders" type="guarantee:queue" priority="critical" schema={...}`,
|
|
274
|
+
inputSchema: {
|
|
275
|
+
type: 'object',
|
|
276
|
+
properties: {
|
|
277
|
+
name: {
|
|
278
|
+
type: 'string',
|
|
279
|
+
description: 'Unique name for the guarantee',
|
|
280
|
+
},
|
|
281
|
+
// Datalog-based fields
|
|
282
|
+
rule: {
|
|
283
|
+
type: 'string',
|
|
284
|
+
description: 'Datalog rule defining violation/1 (for Datalog-based guarantees)',
|
|
285
|
+
},
|
|
286
|
+
severity: {
|
|
287
|
+
type: 'string',
|
|
288
|
+
description: 'Severity for Datalog guarantees: error, warning, or info',
|
|
289
|
+
enum: ['error', 'warning', 'info'],
|
|
290
|
+
},
|
|
291
|
+
// Contract-based fields
|
|
292
|
+
type: {
|
|
293
|
+
type: 'string',
|
|
294
|
+
description: 'Guarantee type for contract-based: guarantee:queue, guarantee:api, guarantee:permission',
|
|
295
|
+
enum: ['guarantee:queue', 'guarantee:api', 'guarantee:permission'],
|
|
296
|
+
},
|
|
297
|
+
priority: {
|
|
298
|
+
type: 'string',
|
|
299
|
+
description: 'Priority level: critical, important, observed, tracked',
|
|
300
|
+
enum: ['critical', 'important', 'observed', 'tracked'],
|
|
301
|
+
},
|
|
302
|
+
status: {
|
|
303
|
+
type: 'string',
|
|
304
|
+
description: 'Lifecycle status: discovered, reviewed, active, changing, deprecated',
|
|
305
|
+
enum: ['discovered', 'reviewed', 'active', 'changing', 'deprecated'],
|
|
306
|
+
},
|
|
307
|
+
owner: {
|
|
308
|
+
type: 'string',
|
|
309
|
+
description: 'Owner of the guarantee (team or person)',
|
|
310
|
+
},
|
|
311
|
+
schema: {
|
|
312
|
+
type: 'object',
|
|
313
|
+
description: 'JSON Schema for contract-based validation',
|
|
314
|
+
},
|
|
315
|
+
condition: {
|
|
316
|
+
type: 'string',
|
|
317
|
+
description: 'Condition expression for the guarantee',
|
|
318
|
+
},
|
|
319
|
+
description: {
|
|
320
|
+
type: 'string',
|
|
321
|
+
description: 'Human-readable description',
|
|
322
|
+
},
|
|
323
|
+
governs: {
|
|
324
|
+
type: 'array',
|
|
325
|
+
items: { type: 'string' },
|
|
326
|
+
description: 'Node IDs that this guarantee governs',
|
|
327
|
+
},
|
|
328
|
+
},
|
|
329
|
+
required: ['name'],
|
|
330
|
+
},
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
name: 'list_guarantees',
|
|
334
|
+
description: `List all defined guarantees.`,
|
|
335
|
+
inputSchema: {
|
|
336
|
+
type: 'object',
|
|
337
|
+
properties: {},
|
|
338
|
+
},
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
name: 'check_guarantees',
|
|
342
|
+
description: `Check all guarantees or specific ones.`,
|
|
343
|
+
inputSchema: {
|
|
344
|
+
type: 'object',
|
|
345
|
+
properties: {
|
|
346
|
+
names: {
|
|
347
|
+
type: 'array',
|
|
348
|
+
items: { type: 'string' },
|
|
349
|
+
description: 'List of guarantee names to check (omit to check all)',
|
|
350
|
+
},
|
|
351
|
+
},
|
|
352
|
+
},
|
|
353
|
+
},
|
|
354
|
+
{
|
|
355
|
+
name: 'delete_guarantee',
|
|
356
|
+
description: `Delete a guarantee by name.`,
|
|
357
|
+
inputSchema: {
|
|
358
|
+
type: 'object',
|
|
359
|
+
properties: {
|
|
360
|
+
name: {
|
|
361
|
+
type: 'string',
|
|
362
|
+
description: 'Name of guarantee to delete',
|
|
363
|
+
},
|
|
364
|
+
},
|
|
365
|
+
required: ['name'],
|
|
366
|
+
},
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
name: 'get_coverage',
|
|
370
|
+
description: `Get analysis coverage for a path.`,
|
|
371
|
+
inputSchema: {
|
|
372
|
+
type: 'object',
|
|
373
|
+
properties: {
|
|
374
|
+
path: {
|
|
375
|
+
type: 'string',
|
|
376
|
+
description: 'Path to check coverage for',
|
|
377
|
+
},
|
|
378
|
+
depth: {
|
|
379
|
+
type: 'number',
|
|
380
|
+
description: 'Directory depth to report (default: 2)',
|
|
381
|
+
},
|
|
382
|
+
},
|
|
383
|
+
},
|
|
384
|
+
},
|
|
385
|
+
{
|
|
386
|
+
name: 'get_documentation',
|
|
387
|
+
description: `Get documentation about Grafema usage.`,
|
|
388
|
+
inputSchema: {
|
|
389
|
+
type: 'object',
|
|
390
|
+
properties: {
|
|
391
|
+
topic: {
|
|
392
|
+
type: 'string',
|
|
393
|
+
description: 'Topic: queries, types, guarantees, or overview',
|
|
394
|
+
},
|
|
395
|
+
},
|
|
396
|
+
},
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
name: 'report_issue',
|
|
400
|
+
description: `Report a bug or issue with Grafema to GitHub.
|
|
401
|
+
|
|
402
|
+
Use this tool when you encounter:
|
|
403
|
+
- Unexpected errors or crashes
|
|
404
|
+
- Incorrect analysis results
|
|
405
|
+
- Missing features that should exist
|
|
406
|
+
- Documentation issues
|
|
407
|
+
|
|
408
|
+
The tool will create a GitHub issue automatically if GITHUB_TOKEN is configured.
|
|
409
|
+
If not configured, it will return a pre-formatted issue template that the user
|
|
410
|
+
can manually submit at https://github.com/Disentinel/grafema/issues/new
|
|
411
|
+
|
|
412
|
+
IMPORTANT: Always ask the user for permission before reporting an issue.
|
|
413
|
+
Include relevant context: error messages, file paths, query used, etc.`,
|
|
414
|
+
inputSchema: {
|
|
415
|
+
type: 'object',
|
|
416
|
+
properties: {
|
|
417
|
+
title: {
|
|
418
|
+
type: 'string',
|
|
419
|
+
description: 'Brief issue title (e.g., "Query returns empty results for FUNCTION nodes")',
|
|
420
|
+
},
|
|
421
|
+
description: {
|
|
422
|
+
type: 'string',
|
|
423
|
+
description: 'Detailed description of the issue',
|
|
424
|
+
},
|
|
425
|
+
context: {
|
|
426
|
+
type: 'string',
|
|
427
|
+
description: 'Relevant context: error messages, queries, file paths, etc.',
|
|
428
|
+
},
|
|
429
|
+
labels: {
|
|
430
|
+
type: 'array',
|
|
431
|
+
items: { type: 'string' },
|
|
432
|
+
description: 'Labels: bug, enhancement, documentation, question',
|
|
433
|
+
},
|
|
434
|
+
},
|
|
435
|
+
required: ['title', 'description'],
|
|
436
|
+
},
|
|
437
|
+
},
|
|
438
|
+
];
|