@papyruslabsai/seshat-mcp 0.13.1 → 0.13.3
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.js +58 -35
- package/dist/tools/functors.js +13 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -13,20 +13,29 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
|
|
|
13
13
|
import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
14
14
|
// ─── Server Instructions ─────────────────────────────────────────
|
|
15
15
|
// Sent to the LLM at connection time. This is the "first contact" pitch.
|
|
16
|
-
const SERVER_INSTRUCTIONS = `Seshat provides structural code analysis backed by a compiled intermediate representation — not heuristic guesses or text search. Every function, class, and route in the synced codebase has been extracted into a typed symbol graph with dependency edges, data flow, constraints, and architectural layer tags.
|
|
16
|
+
const SERVER_INSTRUCTIONS = `Seshat provides structural code analysis backed by a compiled intermediate representation — not heuristic guesses or text search. Every function, class, and route in the synced codebase has been extracted into a typed symbol graph with dependency edges, data flow, constraints, and architectural layer tags. Results are precise and complete — if Seshat says a function has 3 callers, it has exactly 3 callers.
|
|
17
17
|
|
|
18
|
-
Use
|
|
19
|
-
- "
|
|
20
|
-
- "
|
|
21
|
-
- "
|
|
22
|
-
- "
|
|
18
|
+
Use Seshat tools instead of grep/Read when you need to understand code structure. Each tool maps to a question you're already asking:
|
|
19
|
+
- "What projects are loaded?" → list_projects
|
|
20
|
+
- "How is the codebase organized?" → list_modules
|
|
21
|
+
- "What's the full API surface?" → get_topology
|
|
22
|
+
- "Find functions by name or layer" → query_entities
|
|
23
|
+
- "Deep-dive a single function" → get_entity
|
|
24
|
+
- "Who calls this / what does it call?" → get_dependencies
|
|
25
|
+
- "What breaks if I change this?" → get_blast_radius
|
|
26
|
+
- "What data does this read/write/mutate?" → get_data_flow
|
|
27
|
+
- "Which functions touch the DB / require auth / throw?" → find_by_constraint
|
|
28
|
+
- "Which endpoints require auth and which don't?" → get_auth_matrix
|
|
29
|
+
- "Where is sensitive data exposed without protection?" → find_exposure_leaks
|
|
30
|
+
- "What should I read before modifying X?" → get_optimal_context
|
|
31
|
+
- "What tier am I on / what tools are available?" → get_account_status
|
|
23
32
|
|
|
24
|
-
|
|
33
|
+
All tools are read-only and safe to call speculatively — there is no cost to trying them.
|
|
25
34
|
|
|
26
|
-
get_blast_radius and get_optimal_context are designed
|
|
27
|
-
const TIER_ORDER = ['cartographer', 'analyst', 'architect'];
|
|
35
|
+
get_blast_radius and get_optimal_context are designed to be called iteratively. Start with any entity, then feed discovered entities back in to expand your understanding. Each round reveals new structure that informs where to look next. When answering "what does this system do?" questions, a few rounds of blast_radius → get_entity → blast_radius on the newly discovered symbols will build a complete picture faster than reading files.`;
|
|
36
|
+
const TIER_ORDER = ['cartographer', 'analyst', 'architect', 'founder'];
|
|
28
37
|
const TOOL_TIERS = {
|
|
29
|
-
// Cartographer (free) — explore and
|
|
38
|
+
// Cartographer (free) — explore, navigate, and assess security surface
|
|
30
39
|
list_projects: 'cartographer',
|
|
31
40
|
query_entities: 'cartographer',
|
|
32
41
|
get_entity: 'cartographer',
|
|
@@ -37,17 +46,17 @@ const TOOL_TIERS = {
|
|
|
37
46
|
list_modules: 'cartographer',
|
|
38
47
|
get_topology: 'cartographer',
|
|
39
48
|
get_optimal_context: 'cartographer',
|
|
40
|
-
|
|
49
|
+
get_auth_matrix: 'cartographer',
|
|
50
|
+
find_exposure_leaks: 'cartographer',
|
|
51
|
+
// Analyst (tier 2) — audit and diagnose
|
|
41
52
|
find_dead_code: 'analyst',
|
|
42
53
|
find_layer_violations: 'analyst',
|
|
43
54
|
get_coupling_metrics: 'analyst',
|
|
44
|
-
get_auth_matrix: 'analyst',
|
|
45
55
|
find_error_gaps: 'analyst',
|
|
46
56
|
get_test_coverage: 'analyst',
|
|
47
57
|
find_runtime_violations: 'analyst',
|
|
48
58
|
find_ownership_violations: 'analyst',
|
|
49
59
|
query_traits: 'analyst',
|
|
50
|
-
find_exposure_leaks: 'analyst',
|
|
51
60
|
find_semantic_clones: 'analyst',
|
|
52
61
|
// Architect (tier 3) — simulate, estimate, and act
|
|
53
62
|
estimate_task_cost: 'architect',
|
|
@@ -61,6 +70,7 @@ const TIER_LABELS = {
|
|
|
61
70
|
cartographer: 'Cartographer (Free)',
|
|
62
71
|
analyst: 'Analyst (Tier 2)',
|
|
63
72
|
architect: 'Architect (Tier 3)',
|
|
73
|
+
founder: 'Founder (All Access)',
|
|
64
74
|
};
|
|
65
75
|
function tierAtLeast(userTier, requiredTier) {
|
|
66
76
|
return TIER_ORDER.indexOf(userTier) >= TIER_ORDER.indexOf(requiredTier);
|
|
@@ -150,7 +160,7 @@ const TOOLS = [
|
|
|
150
160
|
},
|
|
151
161
|
{
|
|
152
162
|
name: 'find_by_constraint',
|
|
153
|
-
description: 'Find every function with a specific
|
|
163
|
+
description: 'Find every function with a specific syntactic constraint tag — AUTH (requires authentication), DB_ACCESS (touches database), THROWS (explicit throw statement), PURE (no side effects), NETWORK_IO (makes HTTP calls), VALIDATED (has input validation). Constraints are extracted from source syntax, not inferred. Use this when you need to answer "which functions write to the database?" or "what endpoints lack auth?" For semantic/behavioral properties (e.g., "can fail transitively"), use query_traits instead.',
|
|
154
164
|
inputSchema: {
|
|
155
165
|
type: 'object',
|
|
156
166
|
properties: {
|
|
@@ -234,7 +244,7 @@ const TOOLS = [
|
|
|
234
244
|
},
|
|
235
245
|
{
|
|
236
246
|
name: 'get_coupling_metrics',
|
|
237
|
-
description: 'Measure how tangled
|
|
247
|
+
description: 'Measure how tangled your code is. Returns coupling (cross-boundary dependencies), cohesion (within-group dependencies), and instability scores. High coupling + low cohesion = refactoring candidates. Start with group_by: "layer" for the architectural health view ("are my controllers more coupled than my services?"), then drill into group_by: "module" for specific hotspots.',
|
|
238
248
|
inputSchema: {
|
|
239
249
|
type: 'object',
|
|
240
250
|
properties: {
|
|
@@ -276,19 +286,19 @@ const TOOLS = [
|
|
|
276
286
|
},
|
|
277
287
|
{
|
|
278
288
|
name: 'find_runtime_violations',
|
|
279
|
-
description: 'Find architectural boundary leaks where framework-agnostic code imports framework-specific code. Use this when separating core logic from framework dependencies.',
|
|
289
|
+
description: 'Find architectural boundary leaks where framework-agnostic code imports framework-specific code. Use this when separating core logic from framework dependencies. Returns 0 for most JS/Python codebases — a non-zero result indicates a serious boundary violation worth investigating.',
|
|
280
290
|
inputSchema: { type: 'object', properties: { project: projectParam } },
|
|
281
291
|
annotations: READ_ONLY_OPEN,
|
|
282
292
|
},
|
|
283
293
|
{
|
|
284
294
|
name: 'find_ownership_violations',
|
|
285
|
-
description: 'Find memory and lifecycle issues — entities with complex ownership, unsafe blocks, escaping references, or illegal mutability on borrowed data. Most
|
|
295
|
+
description: 'Find memory and lifecycle issues — entities with complex ownership, unsafe blocks, escaping references, or illegal mutability on borrowed data. Returns 0 for most JS/Python codebases — a non-zero result in those languages indicates a serious boundary violation worth investigating. Most detailed results for Rust and C++.',
|
|
286
296
|
inputSchema: { type: 'object', properties: { project: projectParam } },
|
|
287
297
|
annotations: READ_ONLY_OPEN,
|
|
288
298
|
},
|
|
289
299
|
{
|
|
290
300
|
name: 'query_traits',
|
|
291
|
-
description: 'Find functions by
|
|
301
|
+
description: 'Find functions by inferred behavioral trait — "fallible" (can fail, including transitively via callees that throw), "asyncContext" (carries async state), "generator" (yields values). Traits are semantic properties inferred from the call graph, not just syntax. Use this when you need to find all code with a specific capability. For syntactic tags (explicit throw statements, DB access), use find_by_constraint instead.',
|
|
292
302
|
inputSchema: {
|
|
293
303
|
type: 'object',
|
|
294
304
|
properties: {
|
|
@@ -460,7 +470,7 @@ function getCloudUrl(path) {
|
|
|
460
470
|
async function main() {
|
|
461
471
|
const server = new Server({
|
|
462
472
|
name: 'seshat',
|
|
463
|
-
version: '0.13.
|
|
473
|
+
version: '0.13.3',
|
|
464
474
|
}, {
|
|
465
475
|
capabilities: { tools: {} },
|
|
466
476
|
instructions: SERVER_INSTRUCTIONS,
|
|
@@ -521,24 +531,37 @@ async function main() {
|
|
|
521
531
|
const account = await res.json();
|
|
522
532
|
const userTier = account.tier || 'cartographer';
|
|
523
533
|
const credits = account.credits || 0;
|
|
524
|
-
// Build
|
|
525
|
-
const
|
|
526
|
-
|
|
527
|
-
analyst: { available: [], locked: [] },
|
|
528
|
-
architect: { available: [], locked: [] },
|
|
529
|
-
};
|
|
534
|
+
// Build response: lead with what you CAN do, not what you can't
|
|
535
|
+
const availableTools = [];
|
|
536
|
+
const upgradeTeaser = {};
|
|
530
537
|
for (const [toolName, requiredTier] of Object.entries(TOOL_TIERS)) {
|
|
531
|
-
|
|
532
|
-
|
|
538
|
+
if (tierAtLeast(userTier, requiredTier)) {
|
|
539
|
+
availableTools.push(toolName);
|
|
540
|
+
}
|
|
541
|
+
else {
|
|
542
|
+
if (!upgradeTeaser[TIER_LABELS[requiredTier]]) {
|
|
543
|
+
upgradeTeaser[TIER_LABELS[requiredTier]] = [];
|
|
544
|
+
}
|
|
545
|
+
upgradeTeaser[TIER_LABELS[requiredTier]].push(toolName);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
const response = {
|
|
549
|
+
tier: userTier,
|
|
550
|
+
tier_label: TIER_LABELS[userTier],
|
|
551
|
+
ptah_credits: credits,
|
|
552
|
+
your_tools: availableTools,
|
|
553
|
+
tool_count: `${availableTools.length} tools available`,
|
|
554
|
+
};
|
|
555
|
+
// Only mention upgrades if there are locked tools, and frame positively
|
|
556
|
+
if (Object.keys(upgradeTeaser).length > 0) {
|
|
557
|
+
const totalLocked = Object.values(upgradeTeaser).reduce((sum, t) => sum + t.length, 0);
|
|
558
|
+
response.upgrades_available = {
|
|
559
|
+
summary: `${totalLocked} additional diagnostic and simulation tools available with a tier upgrade — find dead code, coupling hotspots, test gaps, layer violations, and simulate changes before making them.`,
|
|
560
|
+
url: 'https://ptah.papyruslabs.ai/settings/billing',
|
|
561
|
+
};
|
|
533
562
|
}
|
|
534
563
|
return {
|
|
535
|
-
content: [{ type: 'text', text: JSON.stringify(
|
|
536
|
-
tier: userTier,
|
|
537
|
-
tier_label: TIER_LABELS[userTier],
|
|
538
|
-
ptah_credits: credits,
|
|
539
|
-
tools: toolsByTier,
|
|
540
|
-
upgrade_url: 'https://ptah.papyruslabs.ai/settings/billing',
|
|
541
|
-
}, null, 2) }],
|
|
564
|
+
content: [{ type: 'text', text: JSON.stringify(response, null, 2) }],
|
|
542
565
|
};
|
|
543
566
|
}
|
|
544
567
|
catch (err) {
|
|
@@ -599,7 +622,7 @@ async function main() {
|
|
|
599
622
|
});
|
|
600
623
|
const transport = new StdioServerTransport();
|
|
601
624
|
await server.connect(transport);
|
|
602
|
-
process.stderr.write(`Seshat MCP v0.13.
|
|
625
|
+
process.stderr.write(`Seshat MCP v0.13.1 connected. Structural code analysis ready.\n`);
|
|
603
626
|
}
|
|
604
627
|
main().catch((err) => {
|
|
605
628
|
process.stderr.write(`Fatal: ${err.message}\n`);
|
package/dist/tools/functors.js
CHANGED
|
@@ -135,6 +135,15 @@ export function findDeadCode(args, loader) {
|
|
|
135
135
|
if (!include_tests) {
|
|
136
136
|
deadEntities = deadEntities.filter((e) => entityLayer(e) !== 'test');
|
|
137
137
|
}
|
|
138
|
+
// Separate schemas — they're often registered dynamically (e.g., fastify.addSchema())
|
|
139
|
+
// and appear "dead" from a static call graph but are alive at runtime.
|
|
140
|
+
const SCHEMA_TYPES = new Set(['schema', 'typealias', 'interface', 'type']);
|
|
141
|
+
const schemaEntities = deadEntities.filter((e) => {
|
|
142
|
+
const type = (typeof e.struct === 'string' ? e.struct : e.struct?.type || '').toLowerCase();
|
|
143
|
+
const layer = entityLayer(e);
|
|
144
|
+
return SCHEMA_TYPES.has(type) || layer === 'schema';
|
|
145
|
+
});
|
|
146
|
+
deadEntities = deadEntities.filter((e) => !schemaEntities.includes(e));
|
|
138
147
|
// Group by layer for overview
|
|
139
148
|
const byLayer = new Map();
|
|
140
149
|
for (const e of deadEntities) {
|
|
@@ -148,6 +157,10 @@ export function findDeadCode(args, loader) {
|
|
|
148
157
|
deadCount: deadEntities.length,
|
|
149
158
|
deadByLayer: Object.fromEntries([...byLayer.entries()].sort((a, b) => b[1] - a[1])),
|
|
150
159
|
dead: deadEntities.slice(0, 100).map(entitySummary),
|
|
160
|
+
...(schemaEntities.length > 0 ? {
|
|
161
|
+
schemas_excluded: schemaEntities.length,
|
|
162
|
+
_note_schemas: `${schemaEntities.length} schema/type definitions excluded — no static references found, but schemas are often registered dynamically (e.g., fastify.addSchema()) and may be alive at runtime.`,
|
|
163
|
+
} : {}),
|
|
151
164
|
};
|
|
152
165
|
}
|
|
153
166
|
// ─── Tool: find_layer_violations ─────────────────────────────────
|
package/package.json
CHANGED