@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 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 these tools instead of grep/Read when you need to understand code structure:
19
- - "Who calls this function?" → get_dependencies (not grep for the function name)
20
- - "What breaks if I change this?" → get_blast_radius (not guessing from imports)
21
- - "Which functions touch the database?" → find_by_constraint with DB_ACCESS
22
- - "How is this codebase organized?" → list_modules or get_topology
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
- Start with list_projects to see what's loaded. All tools are read-only and safe to call speculatively.
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 for iterative use. As you learn more about the codebase from other tools, call them again with new targets each call refines your understanding. 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.`;
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 navigate
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
- // Analyst (tier 2) — audit and analyze
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 behavior tag — AUTH (requires authentication), DB_ACCESS (touches database), THROWS (can throw), PURE (no side effects), NETWORK_IO (makes HTTP calls), VALIDATED (has input validation). Use this when you need to answer questions like "which functions write to the database?" or "what endpoints lack auth?"',
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 modules are. Returns coupling (cross-module dependencies), cohesion (within-module dependencies), and instability scores. High coupling + low cohesion = refactoring candidates. Use this when planning a refactor to find the worst offenders.',
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 useful for Rust and C++ codebases.',
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 abstract capability regardless of syntax — "fallible" (can fail), "asyncContext" (carries async state), "generator" (yields values). Use this when you need to find all code with a specific behavioral trait across the entire codebase.',
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.0',
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 tool availability breakdown
525
- const toolsByTier = {
526
- cartographer: { available: [], locked: [] },
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
- const bucket = tierAtLeast(userTier, requiredTier) ? 'available' : 'locked';
532
- toolsByTier[requiredTier][bucket].push(toolName);
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.0 connected. Structural code analysis ready.\n`);
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`);
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@papyruslabsai/seshat-mcp",
3
- "version": "0.13.1",
3
+ "version": "0.13.3",
4
4
  "description": "Semantic MCP server — exposes a codebase's structure, dependencies, and constraints as queryable tools",
5
5
  "type": "module",
6
6
  "bin": {