@papyruslabsai/seshat-mcp 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +29 -14
- package/dist/index.js +257 -27
- package/dist/loader.d.ts +29 -1
- package/dist/loader.js +119 -0
- package/dist/tools/functors.d.ts +34 -0
- package/dist/tools/functors.js +554 -0
- package/dist/tools/index.d.ts +35 -3
- package/dist/tools/index.js +74 -25
- package/dist/types.d.ts +36 -1
- package/package.json +35 -35
package/dist/tools/index.js
CHANGED
|
@@ -3,28 +3,51 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Each tool exposes a dimension or computation over the 9D JSTF-T coordinate space.
|
|
5
5
|
* Tools operate on the in-memory entity bundle loaded from .seshat/_bundle.json.
|
|
6
|
+
*
|
|
7
|
+
* Multi-project: when multiple projects are loaded, each tool accepts an optional
|
|
8
|
+
* `project` parameter. In multi-project mode, `project` is required.
|
|
6
9
|
*/
|
|
7
10
|
import { buildCallGraph, computeBlastRadius } from '../graph.js';
|
|
8
11
|
// Lazy-initialized shared state
|
|
9
12
|
let loader;
|
|
10
|
-
let
|
|
11
|
-
export function initTools(
|
|
12
|
-
loader =
|
|
13
|
-
|
|
13
|
+
let graphCache = new Map();
|
|
14
|
+
export function initTools(multiLoader) {
|
|
15
|
+
loader = multiLoader;
|
|
16
|
+
graphCache = new Map();
|
|
17
|
+
}
|
|
18
|
+
export function getLoader() {
|
|
19
|
+
return loader;
|
|
20
|
+
}
|
|
21
|
+
export function getGraph(project) {
|
|
22
|
+
const key = project || '__default__';
|
|
23
|
+
if (!graphCache.has(key)) {
|
|
24
|
+
graphCache.set(key, buildCallGraph(loader.getEntities(project)));
|
|
25
|
+
}
|
|
26
|
+
return graphCache.get(key);
|
|
14
27
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
28
|
+
/**
|
|
29
|
+
* Validate project param. Returns error string if invalid, null if OK.
|
|
30
|
+
* In single-project mode, project is optional (defaults to the only project).
|
|
31
|
+
* In multi-project mode, project is required.
|
|
32
|
+
*/
|
|
33
|
+
export function validateProject(project) {
|
|
34
|
+
if (!loader.isMultiProject())
|
|
35
|
+
return null; // single project, always OK
|
|
36
|
+
if (!project) {
|
|
37
|
+
return `Multiple projects loaded. You must specify a "project" parameter. Available: ${loader.getProjectNames().join(', ')}`;
|
|
38
|
+
}
|
|
39
|
+
if (!loader.hasProject(project)) {
|
|
40
|
+
return `Project "${project}" not found. Available: ${loader.getProjectNames().join(', ')}`;
|
|
18
41
|
}
|
|
19
|
-
return
|
|
42
|
+
return null;
|
|
20
43
|
}
|
|
21
44
|
// ─── Helper: Extract entity name from struct ─────────────────────
|
|
22
|
-
function entityName(e) {
|
|
45
|
+
export function entityName(e) {
|
|
23
46
|
if (typeof e.struct === 'string')
|
|
24
47
|
return e.struct;
|
|
25
48
|
return e.struct?.name || e.id || 'anonymous';
|
|
26
49
|
}
|
|
27
|
-
function entityLayer(e) {
|
|
50
|
+
export function entityLayer(e) {
|
|
28
51
|
// Use explicit layer if specific enough
|
|
29
52
|
const explicit = e.context?.layer?.toLowerCase();
|
|
30
53
|
if (explicit && explicit !== 'module' && explicit !== 'unknown')
|
|
@@ -55,11 +78,12 @@ function entityLayer(e) {
|
|
|
55
78
|
return 'test';
|
|
56
79
|
return explicit || 'other';
|
|
57
80
|
}
|
|
58
|
-
function entitySummary(e) {
|
|
81
|
+
export function entitySummary(e) {
|
|
59
82
|
const constraintTags = normalizeConstraints(e.constraints);
|
|
60
83
|
return {
|
|
61
84
|
id: e.id,
|
|
62
85
|
name: entityName(e),
|
|
86
|
+
...(e._project ? { project: e._project } : {}),
|
|
63
87
|
layer: entityLayer(e),
|
|
64
88
|
module: e.context?.module || null,
|
|
65
89
|
sourceFile: e._sourceFile || null,
|
|
@@ -70,7 +94,7 @@ function entitySummary(e) {
|
|
|
70
94
|
callCount: Array.isArray(e.edges?.calls) ? e.edges.calls.length : 0,
|
|
71
95
|
};
|
|
72
96
|
}
|
|
73
|
-
function normalizeConstraints(constraints) {
|
|
97
|
+
export function normalizeConstraints(constraints) {
|
|
74
98
|
if (!constraints)
|
|
75
99
|
return [];
|
|
76
100
|
if (Array.isArray(constraints))
|
|
@@ -124,7 +148,7 @@ function normalizeConstraints(constraints) {
|
|
|
124
148
|
* Deep search constraints — matches against raw constraint object fields too,
|
|
125
149
|
* not just normalized tags.
|
|
126
150
|
*/
|
|
127
|
-
function constraintMatches(constraints, target) {
|
|
151
|
+
export function constraintMatches(constraints, target) {
|
|
128
152
|
// First check normalized tags
|
|
129
153
|
const tags = normalizeConstraints(constraints);
|
|
130
154
|
if (tags.some(t => t.toUpperCase().includes(target.toUpperCase())))
|
|
@@ -139,8 +163,11 @@ function constraintMatches(constraints, target) {
|
|
|
139
163
|
}
|
|
140
164
|
// ─── Tool: query_entities ─────────────────────────────────────────
|
|
141
165
|
export function queryEntities(args) {
|
|
166
|
+
const projErr = validateProject(args.project);
|
|
167
|
+
if (projErr)
|
|
168
|
+
return { error: projErr };
|
|
142
169
|
const { query, layer, module, language, limit = 50 } = args;
|
|
143
|
-
let results = loader.getEntities();
|
|
170
|
+
let results = loader.getEntities(args.project);
|
|
144
171
|
if (layer) {
|
|
145
172
|
results = results.filter(e => entityLayer(e).toLowerCase() === layer.toLowerCase());
|
|
146
173
|
}
|
|
@@ -170,7 +197,10 @@ export function queryEntities(args) {
|
|
|
170
197
|
}
|
|
171
198
|
// ─── Tool: get_entity ─────────────────────────────────────────────
|
|
172
199
|
export function getEntity(args) {
|
|
173
|
-
const
|
|
200
|
+
const projErr = validateProject(args.project);
|
|
201
|
+
if (projErr)
|
|
202
|
+
return { error: projErr };
|
|
203
|
+
const entity = loader.getEntityById(args.id, args.project) || loader.getEntityByName(args.id, args.project);
|
|
174
204
|
if (!entity) {
|
|
175
205
|
return { error: `Entity not found: ${args.id}` };
|
|
176
206
|
}
|
|
@@ -178,6 +208,7 @@ export function getEntity(args) {
|
|
|
178
208
|
return {
|
|
179
209
|
id: entity.id,
|
|
180
210
|
name: entityName(entity),
|
|
211
|
+
...(entity._project ? { project: entity._project } : {}),
|
|
181
212
|
sourceFile: entity._sourceFile,
|
|
182
213
|
sourceLanguage: entity._sourceLanguage,
|
|
183
214
|
jstfFilename: entity._jstfFilename,
|
|
@@ -196,9 +227,12 @@ export function getEntity(args) {
|
|
|
196
227
|
}
|
|
197
228
|
// ─── Tool: get_dependencies ───────────────────────────────────────
|
|
198
229
|
export function getDependencies(args) {
|
|
230
|
+
const projErr = validateProject(args.project);
|
|
231
|
+
if (projErr)
|
|
232
|
+
return { error: projErr };
|
|
199
233
|
const { entity_id, direction = 'both', depth = 2 } = args;
|
|
200
|
-
const g = getGraph();
|
|
201
|
-
const entity = loader.getEntityById(entity_id) || loader.getEntityByName(entity_id);
|
|
234
|
+
const g = getGraph(args.project);
|
|
235
|
+
const entity = loader.getEntityById(entity_id, args.project) || loader.getEntityByName(entity_id, args.project);
|
|
202
236
|
if (!entity) {
|
|
203
237
|
return { error: `Entity not found: ${entity_id}` };
|
|
204
238
|
}
|
|
@@ -226,7 +260,7 @@ export function getDependencies(args) {
|
|
|
226
260
|
}
|
|
227
261
|
return result;
|
|
228
262
|
}
|
|
229
|
-
function collectTransitive(adjacency, startId, maxDepth) {
|
|
263
|
+
export function collectTransitive(adjacency, startId, maxDepth) {
|
|
230
264
|
const visited = new Set();
|
|
231
265
|
const queue = [[startId, 0]];
|
|
232
266
|
visited.add(startId);
|
|
@@ -249,7 +283,10 @@ function collectTransitive(adjacency, startId, maxDepth) {
|
|
|
249
283
|
}
|
|
250
284
|
// ─── Tool: get_data_flow ──────────────────────────────────────────
|
|
251
285
|
export function getDataFlow(args) {
|
|
252
|
-
const
|
|
286
|
+
const projErr = validateProject(args.project);
|
|
287
|
+
if (projErr)
|
|
288
|
+
return { error: projErr };
|
|
289
|
+
const entity = loader.getEntityById(args.entity_id, args.project) || loader.getEntityByName(args.entity_id, args.project);
|
|
253
290
|
if (!entity) {
|
|
254
291
|
return { error: `Entity not found: ${args.entity_id}` };
|
|
255
292
|
}
|
|
@@ -262,8 +299,11 @@ export function getDataFlow(args) {
|
|
|
262
299
|
}
|
|
263
300
|
// ─── Tool: find_by_constraint ─────────────────────────────────────
|
|
264
301
|
export function findByConstraint(args) {
|
|
302
|
+
const projErr = validateProject(args.project);
|
|
303
|
+
if (projErr)
|
|
304
|
+
return { error: projErr };
|
|
265
305
|
const target = args.constraint;
|
|
266
|
-
const results = loader.getEntities().filter(e => constraintMatches(e.constraints, target));
|
|
306
|
+
const results = loader.getEntities(args.project).filter(e => constraintMatches(e.constraints, target));
|
|
267
307
|
return {
|
|
268
308
|
constraint: args.constraint,
|
|
269
309
|
total: results.length,
|
|
@@ -272,12 +312,15 @@ export function findByConstraint(args) {
|
|
|
272
312
|
}
|
|
273
313
|
// ─── Tool: get_blast_radius ───────────────────────────────────────
|
|
274
314
|
export function getBlastRadius(args) {
|
|
275
|
-
const
|
|
315
|
+
const projErr = validateProject(args.project);
|
|
316
|
+
if (projErr)
|
|
317
|
+
return { error: projErr };
|
|
318
|
+
const g = getGraph(args.project);
|
|
276
319
|
// Resolve names to IDs
|
|
277
320
|
const resolvedIds = new Set();
|
|
278
321
|
const notFound = [];
|
|
279
322
|
for (const nameOrId of args.entity_ids) {
|
|
280
|
-
const entity = loader.getEntityById(nameOrId) || loader.getEntityByName(nameOrId);
|
|
323
|
+
const entity = loader.getEntityById(nameOrId, args.project) || loader.getEntityByName(nameOrId, args.project);
|
|
281
324
|
if (entity) {
|
|
282
325
|
resolvedIds.add(entity.id);
|
|
283
326
|
}
|
|
@@ -306,8 +349,11 @@ export function getBlastRadius(args) {
|
|
|
306
349
|
}
|
|
307
350
|
// ─── Tool: list_modules ───────────────────────────────────────────
|
|
308
351
|
export function listModules(args) {
|
|
352
|
+
const projErr = validateProject(args.project);
|
|
353
|
+
if (projErr)
|
|
354
|
+
return { error: projErr };
|
|
309
355
|
const { group_by = 'layer' } = args;
|
|
310
|
-
const entities = loader.getEntities();
|
|
356
|
+
const entities = loader.getEntities(args.project);
|
|
311
357
|
const groups = new Map();
|
|
312
358
|
for (const e of entities) {
|
|
313
359
|
let key;
|
|
@@ -339,8 +385,11 @@ export function listModules(args) {
|
|
|
339
385
|
};
|
|
340
386
|
}
|
|
341
387
|
// ─── Tool: get_topology ───────────────────────────────────────────
|
|
342
|
-
export function getTopology() {
|
|
343
|
-
const
|
|
388
|
+
export function getTopology(args) {
|
|
389
|
+
const projErr = validateProject(args?.project);
|
|
390
|
+
if (projErr)
|
|
391
|
+
return { error: projErr };
|
|
392
|
+
const topology = loader.getTopology(args?.project);
|
|
344
393
|
if (!topology) {
|
|
345
394
|
return { error: 'No topology data found. The _topology.json file may not have been generated.' };
|
|
346
395
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export interface JstfEntity {
|
|
|
11
11
|
_jstfFilename?: string;
|
|
12
12
|
_sourceFile?: string | null;
|
|
13
13
|
_sourceLanguage?: string;
|
|
14
|
+
_project?: string;
|
|
14
15
|
/** σ — Structure: function shape, signature, modifiers */
|
|
15
16
|
struct?: {
|
|
16
17
|
name?: string;
|
|
@@ -55,6 +56,7 @@ export interface JstfEntity {
|
|
|
55
56
|
target: string;
|
|
56
57
|
operation?: string;
|
|
57
58
|
}>;
|
|
59
|
+
tables?: string[];
|
|
58
60
|
sources?: unknown[];
|
|
59
61
|
returns?: unknown[];
|
|
60
62
|
[key: string]: unknown;
|
|
@@ -67,14 +69,27 @@ export interface JstfEntity {
|
|
|
67
69
|
field: string;
|
|
68
70
|
rule: string;
|
|
69
71
|
}>;
|
|
72
|
+
auth?: string[] | 'none';
|
|
73
|
+
authRequired?: boolean;
|
|
74
|
+
purity?: string;
|
|
75
|
+
throws?: boolean;
|
|
76
|
+
sideEffects?: string[];
|
|
77
|
+
errorHandling?: {
|
|
78
|
+
tryCatch?: boolean;
|
|
79
|
+
catchClause?: boolean;
|
|
80
|
+
finally?: boolean;
|
|
81
|
+
errorBoundary?: boolean;
|
|
82
|
+
};
|
|
70
83
|
[key: string]: unknown;
|
|
71
84
|
} | string[];
|
|
72
85
|
/** χ — Context: architectural position, visibility, layer */
|
|
73
86
|
context?: {
|
|
74
87
|
layer?: string;
|
|
88
|
+
layerSource?: string;
|
|
75
89
|
module?: string;
|
|
76
90
|
path?: string;
|
|
77
91
|
exposure?: string;
|
|
92
|
+
visibility?: string;
|
|
78
93
|
traffic?: string;
|
|
79
94
|
criticality?: string;
|
|
80
95
|
[key: string]: unknown;
|
|
@@ -82,7 +97,18 @@ export interface JstfEntity {
|
|
|
82
97
|
/** λ — Ownership: memory ownership, lifetimes, borrowing */
|
|
83
98
|
ownership?: Record<string, unknown>;
|
|
84
99
|
/** τ — Traits: type capabilities, bounds, markers */
|
|
85
|
-
traits?: string[] |
|
|
100
|
+
traits?: string[] | {
|
|
101
|
+
self?: {
|
|
102
|
+
asyncContext?: boolean;
|
|
103
|
+
fallible?: boolean;
|
|
104
|
+
[key: string]: unknown;
|
|
105
|
+
};
|
|
106
|
+
params?: Record<string, {
|
|
107
|
+
bounds?: string[];
|
|
108
|
+
markers?: string[];
|
|
109
|
+
}>;
|
|
110
|
+
[key: string]: unknown;
|
|
111
|
+
};
|
|
86
112
|
/** ρ — Runtime: reactive model, async platform, framework */
|
|
87
113
|
runtime?: {
|
|
88
114
|
async?: string;
|
|
@@ -120,3 +146,12 @@ export interface Manifest {
|
|
|
120
146
|
languages: string[];
|
|
121
147
|
layers: Record<string, number>;
|
|
122
148
|
}
|
|
149
|
+
export interface ProjectInfo {
|
|
150
|
+
name: string;
|
|
151
|
+
path: string;
|
|
152
|
+
entityCount: number;
|
|
153
|
+
languages: string[];
|
|
154
|
+
commitSha: string;
|
|
155
|
+
extractedAt: string;
|
|
156
|
+
layers: Record<string, number>;
|
|
157
|
+
}
|
package/package.json
CHANGED
|
@@ -1,35 +1,35 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@papyruslabsai/seshat-mcp",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Semantic MCP server — exposes a codebase's 9D JSTF-T coordinate space as queryable tools",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"bin": {
|
|
7
|
-
"seshat-mcp": "./dist/index.js"
|
|
8
|
-
},
|
|
9
|
-
"main": "./dist/index.js",
|
|
10
|
-
"scripts": {
|
|
11
|
-
"build": "tsc",
|
|
12
|
-
"dev": "tsc --watch",
|
|
13
|
-
"start": "node dist/index.js"
|
|
14
|
-
},
|
|
15
|
-
"dependencies": {
|
|
16
|
-
"@modelcontextprotocol/sdk": "^1.12.1"
|
|
17
|
-
},
|
|
18
|
-
"devDependencies": {
|
|
19
|
-
"typescript": "^5.5.0",
|
|
20
|
-
"@types/node": "^20.0.0"
|
|
21
|
-
},
|
|
22
|
-
"engines": {
|
|
23
|
-
"node": ">=20"
|
|
24
|
-
},
|
|
25
|
-
"files": [
|
|
26
|
-
"dist/"
|
|
27
|
-
],
|
|
28
|
-
"repository": {
|
|
29
|
-
"type": "git",
|
|
30
|
-
"url": "https://github.com/papyruslabs-ai/seshat.git",
|
|
31
|
-
"directory": "packages/seshat-mcp"
|
|
32
|
-
},
|
|
33
|
-
"keywords": ["mcp", "jstf", "semantic", "code-analysis", "seshat"],
|
|
34
|
-
"license": "MIT"
|
|
35
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@papyruslabsai/seshat-mcp",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Semantic MCP server — exposes a codebase's 9D JSTF-T coordinate space as queryable tools",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"seshat-mcp": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "./dist/index.js",
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"dev": "tsc --watch",
|
|
13
|
+
"start": "node dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"@modelcontextprotocol/sdk": "^1.12.1"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"typescript": "^5.5.0",
|
|
20
|
+
"@types/node": "^20.0.0"
|
|
21
|
+
},
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=20"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist/"
|
|
27
|
+
],
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "https://github.com/papyruslabs-ai/seshat.git",
|
|
31
|
+
"directory": "packages/seshat-mcp"
|
|
32
|
+
},
|
|
33
|
+
"keywords": ["mcp", "jstf", "semantic", "code-analysis", "seshat"],
|
|
34
|
+
"license": "MIT"
|
|
35
|
+
}
|