@papyruslabsai/seshat-mcp 0.12.0 → 0.12.2

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 CHANGED
@@ -1 +1,9 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @papyruslabs/seshat-mcp — Semantic Code Analysis MCP Server (Cloud Proxy)
4
+ *
5
+ * This CLI server declares the full suite of Seshat tools to Claude/AI clients,
6
+ * but executes all logic remotely on the Ptah Cloud API for zero-conflict
7
+ * collaboration and deep analytics.
8
+ */
1
9
  export {};
package/dist/index.js CHANGED
@@ -1,97 +1,73 @@
1
- !/usr/bin / env;
2
- node;
1
+ #!/usr/bin/env node
3
2
  /**
4
- * @papyruslabs/seshat-mcp — Semantic Code Analysis MCP Server
3
+ * @papyruslabs/seshat-mcp — Semantic Code Analysis MCP Server (Cloud Proxy)
5
4
  *
6
- * Exposes a codebase's structure, dependencies, and constraints as queryable
7
- * MCP tools. Reads pre-extracted analysis data from .seshat/_bundle.json.
8
- *
9
- * Multi-project mode:
10
- * Set SESHAT_PROJECTS env var to comma-separated paths or a glob pattern.
11
- * In multi-project mode, all tools require a `project` parameter.
12
- * Use `list_projects` to see available projects.
13
- *
14
- * Single-project mode (default):
15
- * When SESHAT_PROJECTS is not set, loads from CWD. No `project` param needed.
16
- *
17
- * 20 tools across 4 categories:
18
- * Discovery: list_projects, query_entities, get_entity, list_modules, get_topology
19
- * Graph: get_dependencies, get_data_flow, find_by_constraint, get_blast_radius
20
- * Analysis: find_dead_code, find_layer_violations, get_coupling_metrics,
21
- * get_auth_matrix, find_error_gaps, get_test_coverage,
22
- * get_optimal_context, estimate_task_cost, report_actual_burn
23
- * Diff: diff_bundle, conflict_matrix
24
- *
25
- * Usage:
26
- * npx @papyruslabs/seshat-mcp # single project (CWD)
27
- * SESHAT_PROJECTS=/path/a,/path/b npx @papyruslabs/seshat-mcp # multi-project
28
- * SESHAT_PROJECTS="/home/user/projects/*" npx @papyruslabs/seshat-mcp # glob
5
+ * This CLI server declares the full suite of Seshat tools to Claude/AI clients,
6
+ * but executes all logic remotely on the Ptah Cloud API for zero-conflict
7
+ * collaboration and deep analytics.
29
8
  */
30
9
  import fs from 'fs';
31
10
  import path from 'path';
32
11
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
33
12
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
34
13
  import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
35
- import { MultiLoader } from './loader.js';
36
- import { bootstrap } from './bootstrap.js';
37
- import { logTelemetry, isSupabaseConfigured } from './supabase.js';
38
- import { queryEntities, getEntity, getDependencies, getDataFlow, findByConstraint, getBlastRadius, listModules, getTopology, } from './tools/index.js';
39
- import { findDeadCode, findLayerViolations, getCouplingMetrics, getAuthMatrix, findErrorGaps, getTestCoverage, getOptimalContext, estimateTaskCost, reportActualBurn, find_runtime_violations, find_ownership_violations, query_traits, simulate_mutation, query_data_targets, find_exposure_leaks, find_semantic_clones, create_symbol, } from './tools/functors.js';
40
- import { diffBundle, conflictMatrix, } from './tools/diff.js';
41
- // ─── Project Discovery ───────────────────────────────────────────
42
- /**
43
- * Discover project directories from SESHAT_PROJECTS env var.
44
- *
45
- * - Not set → [process.cwd()] (backward compat)
46
- * - Comma-separated paths → resolve each
47
- * - Glob pattern (contains *) → scan parent dir for subdirs with .seshat/_bundle.json
48
- */
49
- function discoverProjects() {
50
- const env = process.env.SESHAT_PROJECTS;
51
- if (!env)
52
- return [process.cwd()];
53
- const trimmed = env.trim();
54
- // Glob pattern: /path/to/projects/*
55
- if (trimmed.includes('*')) {
56
- const globDir = trimmed.replace(/\/?\*$/, '');
57
- const resolved = path.resolve(globDir);
58
- if (!fs.existsSync(resolved)) {
59
- process.stderr.write(`SESHAT_PROJECTS glob dir not found: ${resolved}\n`);
60
- return [process.cwd()];
61
- }
62
- const dirs = [];
63
- for (const entry of fs.readdirSync(resolved, { withFileTypes: true })) {
64
- if (!entry.isDirectory())
65
- continue;
66
- const candidate = path.join(resolved, entry.name);
67
- const bundlePath = path.join(candidate, '.seshat', '_bundle.json');
68
- if (fs.existsSync(bundlePath)) {
69
- dirs.push(candidate);
70
- }
71
- }
72
- if (dirs.length === 0) {
73
- process.stderr.write(`SESHAT_PROJECTS: no dirs with .seshat/_bundle.json found in ${resolved}\n`);
74
- return [process.cwd()];
75
- }
76
- return dirs;
77
- }
78
- // Comma-separated paths
79
- return trimmed.split(',').map(p => path.resolve(p.trim())).filter(p => p.length > 0);
14
+ const TIER_ORDER = ['cartographer', 'analyst', 'architect'];
15
+ const TOOL_TIERS = {
16
+ // Cartographer (free) explore and navigate
17
+ list_projects: 'cartographer',
18
+ query_entities: 'cartographer',
19
+ get_entity: 'cartographer',
20
+ get_dependencies: 'cartographer',
21
+ get_data_flow: 'cartographer',
22
+ find_by_constraint: 'cartographer',
23
+ get_blast_radius: 'cartographer',
24
+ list_modules: 'cartographer',
25
+ get_topology: 'cartographer',
26
+ get_optimal_context: 'cartographer',
27
+ // Analyst (tier 2) — audit and analyze
28
+ find_dead_code: 'analyst',
29
+ find_layer_violations: 'analyst',
30
+ get_coupling_metrics: 'analyst',
31
+ get_auth_matrix: 'analyst',
32
+ find_error_gaps: 'analyst',
33
+ get_test_coverage: 'analyst',
34
+ find_runtime_violations: 'analyst',
35
+ find_ownership_violations: 'analyst',
36
+ query_traits: 'analyst',
37
+ find_exposure_leaks: 'analyst',
38
+ find_semantic_clones: 'analyst',
39
+ // Architect (tier 3) — simulate, estimate, and act
40
+ estimate_task_cost: 'architect',
41
+ simulate_mutation: 'architect',
42
+ create_symbol: 'architect',
43
+ diff_bundle: 'architect',
44
+ conflict_matrix: 'architect',
45
+ query_data_targets: 'architect',
46
+ };
47
+ const TIER_LABELS = {
48
+ cartographer: 'Cartographer (Free)',
49
+ analyst: 'Analyst (Tier 2)',
50
+ architect: 'Architect (Tier 3)',
51
+ };
52
+ function tierAtLeast(userTier, requiredTier) {
53
+ return TIER_ORDER.indexOf(userTier) >= TIER_ORDER.indexOf(requiredTier);
80
54
  }
81
- // ─── Project param definition (injected into all tool schemas) ───
55
+ // ─── Project param definition ───
82
56
  const projectParam = {
83
57
  type: 'string',
84
58
  description: 'Project name (required in multi-project mode). Use list_projects to see available projects.',
85
59
  };
86
60
  // ─── Tool Definitions ─────────────────────────────────────────────
87
61
  const TOOLS = [
62
+ {
63
+ name: 'get_account_status',
64
+ description: 'Check your Ptah account: current tier, Ptah Credits balance, and which tools are available or locked. Call this to see what you can do and what upgrades are available.',
65
+ inputSchema: { type: 'object', properties: {} },
66
+ },
88
67
  {
89
68
  name: 'list_projects',
90
69
  description: 'List all loaded projects with entity counts, languages, and metadata. Call this first to see what projects are available, then pass the project name to other tools.',
91
- inputSchema: {
92
- type: 'object',
93
- properties: {},
94
- },
70
+ inputSchema: { type: 'object', properties: {} },
95
71
  },
96
72
  {
97
73
  name: 'query_entities',
@@ -100,26 +76,11 @@ const TOOLS = [
100
76
  type: 'object',
101
77
  properties: {
102
78
  project: projectParam,
103
- query: {
104
- type: 'string',
105
- description: 'Search term matches against entity name, ID, source file, and module',
106
- },
107
- layer: {
108
- type: 'string',
109
- description: 'Filter by architectural layer: route, controller, service, repository, utility, hook, component, schema',
110
- },
111
- module: {
112
- type: 'string',
113
- description: 'Filter by module (partial match)',
114
- },
115
- language: {
116
- type: 'string',
117
- description: 'Filter by source language: javascript, typescript, python, go, rust, etc.',
118
- },
119
- limit: {
120
- type: 'number',
121
- description: 'Max results to return (default: 50)',
122
- },
79
+ query: { type: 'string', description: 'Search term — matches against entity name, ID, source file, and module' },
80
+ layer: { type: 'string', description: 'Filter by architectural layer: route, controller, service, repository, utility, hook, component, schema' },
81
+ module: { type: 'string', description: 'Filter by module (partial match)' },
82
+ language: { type: 'string', description: 'Filter by source language: javascript, typescript, python, go, rust, etc.' },
83
+ limit: { type: 'number', description: 'Max results to return (default: 50)' },
123
84
  },
124
85
  },
125
86
  },
@@ -130,10 +91,7 @@ const TOOLS = [
130
91
  type: 'object',
131
92
  properties: {
132
93
  project: projectParam,
133
- id: {
134
- type: 'string',
135
- description: 'Entity ID or name',
136
- },
94
+ id: { type: 'string', description: 'Entity ID or name' },
137
95
  },
138
96
  required: ['id'],
139
97
  },
@@ -145,19 +103,9 @@ const TOOLS = [
145
103
  type: 'object',
146
104
  properties: {
147
105
  project: projectParam,
148
- entity_id: {
149
- type: 'string',
150
- description: 'Entity ID or name',
151
- },
152
- direction: {
153
- type: 'string',
154
- enum: ['callers', 'callees', 'both'],
155
- description: 'Which direction to traverse (default: both)',
156
- },
157
- depth: {
158
- type: 'number',
159
- description: 'How many levels deep to traverse (default: 2)',
160
- },
106
+ entity_id: { type: 'string', description: 'Entity ID or name' },
107
+ direction: { type: 'string', enum: ['callers', 'callees', 'both'], description: 'Which direction to traverse (default: both)' },
108
+ depth: { type: 'number', description: 'How many levels deep to traverse (default: 2)' },
161
109
  },
162
110
  required: ['entity_id'],
163
111
  },
@@ -169,10 +117,7 @@ const TOOLS = [
169
117
  type: 'object',
170
118
  properties: {
171
119
  project: projectParam,
172
- entity_id: {
173
- type: 'string',
174
- description: 'Entity ID or name',
175
- },
120
+ entity_id: { type: 'string', description: 'Entity ID or name' },
176
121
  },
177
122
  required: ['entity_id'],
178
123
  },
@@ -184,10 +129,7 @@ const TOOLS = [
184
129
  type: 'object',
185
130
  properties: {
186
131
  project: projectParam,
187
- constraint: {
188
- type: 'string',
189
- description: 'Constraint tag to search for: AUTH, VALIDATED, PURE, THROWS, DB_ACCESS, NETWORK_IO, IMP, etc.',
190
- },
132
+ constraint: { type: 'string', description: 'Constraint tag to search for: AUTH, VALIDATED, PURE, THROWS, DB_ACCESS, NETWORK_IO, IMP, etc.' },
191
133
  },
192
134
  required: ['constraint'],
193
135
  },
@@ -199,11 +141,7 @@ const TOOLS = [
199
141
  type: 'object',
200
142
  properties: {
201
143
  project: projectParam,
202
- entity_ids: {
203
- type: 'array',
204
- items: { type: 'string' },
205
- description: 'Array of entity IDs or names to compute blast radius for',
206
- },
144
+ entity_ids: { type: 'array', items: { type: 'string' }, description: 'Array of entity IDs or names to compute blast radius for' },
207
145
  },
208
146
  required: ['entity_ids'],
209
147
  },
@@ -215,25 +153,33 @@ const TOOLS = [
215
153
  type: 'object',
216
154
  properties: {
217
155
  project: projectParam,
218
- group_by: {
219
- type: 'string',
220
- enum: ['layer', 'module', 'file', 'language'],
221
- description: 'How to group entities (default: layer)',
222
- },
156
+ group_by: { type: 'string', enum: ['layer', 'module', 'file', 'language'], description: 'How to group entities (default: layer)' },
223
157
  },
224
158
  },
225
159
  },
226
160
  {
227
161
  name: 'get_topology',
228
162
  description: 'Get the API topology: routes, plugins, auth patterns, and database tables. Built from architectural context and dependency analysis across all entities.',
163
+ inputSchema: {
164
+ type: 'object',
165
+ properties: { project: projectParam },
166
+ },
167
+ },
168
+ {
169
+ name: 'get_optimal_context',
170
+ description: 'Compute the optimal set of related entities to include in an LLM context window for a target entity. Uses greedy knapsack: maximizes relevance per token. Returns entities ranked by relevance with token estimates.',
229
171
  inputSchema: {
230
172
  type: 'object',
231
173
  properties: {
232
174
  project: projectParam,
175
+ target_entity: { type: 'string', description: 'Entity ID or name to build context around' },
176
+ max_tokens: { type: 'number', description: 'Token budget for the context window (default: 8000)' },
177
+ strategy: { type: 'string', enum: ['bfs', 'blast_radius'], description: 'Traversal strategy: bfs (faster, local neighborhood) or blast_radius (full affected set)' },
233
178
  },
179
+ required: ['target_entity'],
234
180
  },
235
181
  },
236
- // ─── Analysis Tools ─────────────────────────────────────────────
182
+ // ─── Analyst Tools (Tier 2) ───────────────────────────────────────
237
183
  {
238
184
  name: 'find_dead_code',
239
185
  description: 'Find unreachable entities (dead code candidates). BFS from entry points (routes, exported functions, tests, plugins) through the call graph. Entities not reachable from any entry point are flagged.',
@@ -241,21 +187,16 @@ const TOOLS = [
241
187
  type: 'object',
242
188
  properties: {
243
189
  project: projectParam,
244
- include_tests: {
245
- type: 'boolean',
246
- description: 'Include test entities in dead code results (default: false)',
247
- },
190
+ include_tests: { type: 'boolean', description: 'Include test entities in dead code results (default: false)' },
248
191
  },
249
192
  },
250
193
  },
251
194
  {
252
195
  name: 'find_layer_violations',
253
- description: 'Detect architectural layer violations in the call graph. Finds backward calls (lower layer calling higher layer, e.g. repository → route) and skip-layer calls (jumping over multiple layers). Uses architectural layer classification.',
196
+ description: 'Detect architectural layer violations in the call graph. Finds backward calls (lower layer calling higher layer, e.g. repository → route) and skip-layer calls (jumping over multiple layers).',
254
197
  inputSchema: {
255
198
  type: 'object',
256
- properties: {
257
- project: projectParam,
258
- },
199
+ properties: { project: projectParam },
259
200
  },
260
201
  },
261
202
  {
@@ -265,11 +206,7 @@ const TOOLS = [
265
206
  type: 'object',
266
207
  properties: {
267
208
  project: projectParam,
268
- group_by: {
269
- type: 'string',
270
- enum: ['module', 'layer'],
271
- description: 'Group entities by module or layer (default: module)',
272
- },
209
+ group_by: { type: 'string', enum: ['module', 'layer'], description: 'Group entities by module or layer (default: module)' },
273
210
  },
274
211
  },
275
212
  },
@@ -278,9 +215,7 @@ const TOOLS = [
278
215
  description: 'Analyze authentication coverage across all API-facing entities. Shows which routes/controllers have auth requirements and which don\'t. Detects inconsistencies like DB access without auth.',
279
216
  inputSchema: {
280
217
  type: 'object',
281
- properties: {
282
- project: projectParam,
283
- },
218
+ properties: { project: projectParam },
284
219
  },
285
220
  },
286
221
  {
@@ -288,9 +223,7 @@ const TOOLS = [
288
223
  description: 'Find error handling gaps: entities that throw or have network/db side effects whose callers lack try/catch. These are crash risk points where exceptions can propagate unhandled.',
289
224
  inputSchema: {
290
225
  type: 'object',
291
- properties: {
292
- project: projectParam,
293
- },
226
+ properties: { project: projectParam },
294
227
  },
295
228
  },
296
229
  {
@@ -300,143 +233,70 @@ const TOOLS = [
300
233
  type: 'object',
301
234
  properties: {
302
235
  project: projectParam,
303
- weight_by_blast_radius: {
304
- type: 'boolean',
305
- description: 'Rank uncovered entities by blast radius to prioritize testing (default: false, slower)',
306
- },
236
+ weight_by_blast_radius: { type: 'boolean', description: 'Rank uncovered entities by blast radius to prioritize testing (default: false, slower)' },
307
237
  },
308
238
  },
309
239
  },
310
240
  {
311
- name: 'get_optimal_context',
312
- description: 'Compute the optimal set of related entities to include in an LLM context window for a target entity. Uses greedy knapsack: maximizes relevance per token. Returns entities ranked by relevance with token estimates.',
313
- inputSchema: {
314
- type: 'object',
315
- properties: {
316
- project: projectParam,
317
- target_entity: {
318
- type: 'string',
319
- description: 'Entity ID or name to build context around',
320
- },
321
- max_tokens: {
322
- type: 'number',
323
- description: 'Token budget for the context window (default: 8000)',
324
- },
325
- strategy: {
326
- type: 'string',
327
- enum: ['bfs', 'blast_radius'],
328
- description: 'Traversal strategy: bfs (faster, local neighborhood) or blast_radius (full affected set)',
329
- },
330
- },
331
- required: ['target_entity'],
332
- },
241
+ name: 'find_runtime_violations',
242
+ description: 'Analyze the call graph for runtime environment leaks. Finds architectural boundary issues where framework-agnostic code improperly imports framework-specific code.',
243
+ inputSchema: { type: 'object', properties: { project: projectParam } },
333
244
  },
334
245
  {
335
- name: 'estimate_task_cost',
336
- description: 'Estimate token cost of a code change BEFORE starting work. Computes blast radius, sums source token counts across affected entities, and projects total token burn including iteration cycles. Call this before planning to check if a task fits within your token budget.',
337
- inputSchema: {
338
- type: 'object',
339
- properties: {
340
- project: projectParam,
341
- target_entities: {
342
- type: 'array',
343
- items: { type: 'string' },
344
- description: 'Entity IDs or names that will be modified',
345
- },
346
- context_budget: {
347
- type: 'number',
348
- description: 'LLM context window token budget (default: 200000)',
349
- },
350
- },
351
- required: ['target_entities'],
352
- },
246
+ name: 'find_ownership_violations',
247
+ description: 'Analyze the codebase for memory and lifecycle constraints. Flags entities with complex ownership rules, unsafe blocks, escaping boundaries, or illegal mutability patterns on borrowed references.',
248
+ inputSchema: { type: 'object', properties: { project: projectParam } },
353
249
  },
354
250
  {
355
- name: 'report_actual_burn',
356
- description: 'Close the calibration feedback loop: report actual token usage against a prior prediction from estimate_task_cost. Computes drift (actual - predicted) / predicted. Also supports listing recent predictions with aggregate calibration stats, or abandoning predictions for cancelled tasks.',
251
+ name: 'query_traits',
252
+ description: 'Search the codebase for specific functional capabilities. Allows you to find entities by their abstract traits (e.g., "fallible", "asyncContext", "generator") regardless of their structural syntax.',
357
253
  inputSchema: {
358
254
  type: 'object',
359
255
  properties: {
360
- prediction_id: {
361
- type: 'string',
362
- description: 'The prediction ID returned by estimate_task_cost. Required for complete/abandon actions.',
363
- },
364
- actual_input_tokens: {
365
- type: 'number',
366
- description: 'Actual input tokens consumed (from LLM usage metadata).',
367
- },
368
- actual_output_tokens: {
369
- type: 'number',
370
- description: 'Actual output tokens consumed (from LLM usage metadata).',
371
- },
372
- actual_total_tokens: {
373
- type: 'number',
374
- description: 'Actual total tokens consumed (input + output).',
375
- },
376
- model: {
377
- type: 'string',
378
- description: 'Model used (e.g. claude-opus-4-6, claude-sonnet-4-6).',
379
- },
380
- action: {
381
- type: 'string',
382
- enum: ['complete', 'abandon', 'list'],
383
- description: 'Action: "complete" reports actuals (default), "abandon" marks prediction as cancelled, "list" shows recent predictions with calibration stats.',
384
- },
385
256
  project: projectParam,
386
- notes: {
387
- type: 'string',
388
- description: 'Optional notes about the task outcome.',
389
- },
257
+ trait: { type: 'string', description: 'The trait or capability to search for (e.g., "fallible", "asyncContext")' },
390
258
  },
259
+ required: ['trait'],
391
260
  },
392
261
  },
393
- // ─── Analysis & Impact Tools ───────────────────────────────────
394
262
  {
395
- name: 'find_runtime_violations',
396
- description: 'Analyze the call graph for runtime environment leaks. Finds architectural boundary issues where framework-agnostic code improperly imports framework-specific code (e.g. pure logic calling React hooks) or where incompatible frameworks mix directly.',
397
- inputSchema: {
398
- type: 'object',
399
- properties: {
400
- project: projectParam,
401
- },
402
- },
263
+ name: 'find_exposure_leaks',
264
+ description: 'Analyze the call graph for architectural visibility leaks. Flags paths where a "public" or "api" entity directly accesses a "private" internal entity.',
265
+ inputSchema: { type: 'object', properties: { project: projectParam } },
403
266
  },
404
267
  {
405
- name: 'find_ownership_violations',
406
- description: 'Analyze the codebase for memory and lifecycle constraints. Flags entities with complex ownership rules, unsafe blocks, escaping boundaries, or illegal mutability patterns on borrowed references.',
268
+ name: 'find_semantic_clones',
269
+ description: 'Analyze the codebase for duplicated logic blocks. Normalizes variables and compares code structure to identify identical algorithms written across different files or even different languages.',
407
270
  inputSchema: {
408
271
  type: 'object',
409
272
  properties: {
410
273
  project: projectParam,
274
+ min_complexity: { type: 'number', description: 'Minimum number of logic expressions required to constitute a match (default: 5).' },
411
275
  },
412
276
  },
413
277
  },
278
+ // ─── Architect Tools (Tier 3) ─────────────────────────────────────
414
279
  {
415
- name: 'query_traits',
416
- description: 'Search the codebase for specific functional capabilities. Allows you to find entities by their abstract traits (e.g., "fallible", "asyncContext", "generator") regardless of their structural syntax.',
280
+ name: 'estimate_task_cost',
281
+ description: 'Estimate token cost of a code change BEFORE starting work. Computes blast radius, sums source token counts across affected entities, and projects total token burn including iteration cycles. Call this before planning to check if a task fits within your token budget.',
417
282
  inputSchema: {
418
283
  type: 'object',
419
284
  properties: {
420
285
  project: projectParam,
421
- trait: {
422
- type: 'string',
423
- description: 'The trait or capability to search for (e.g., "fallible", "asyncContext")',
424
- },
286
+ target_entities: { type: 'array', items: { type: 'string' }, description: 'Entity IDs or names that will be modified' },
287
+ context_budget: { type: 'number', description: 'LLM context window token budget (default: 200000)' },
425
288
  },
426
- required: ['trait'],
289
+ required: ['target_entities'],
427
290
  },
428
291
  },
429
292
  {
430
293
  name: 'simulate_mutation',
431
- description: 'The Impact Simulator. Proposes a hypothetical change to an entity (like adding a "fallible" or "auth" requirement) and simulates the architectural fallout upstream and downstream. Returns a blueprint of exactly which other entities will break and what fixes they require.',
294
+ description: 'The Impact Simulator. Proposes a hypothetical change to an entity and simulates the architectural fallout upstream and downstream. Returns a blueprint of exactly which other entities will break and what fixes they require.',
432
295
  inputSchema: {
433
296
  type: 'object',
434
297
  properties: {
435
298
  project: projectParam,
436
- entity_id: {
437
- type: 'string',
438
- description: 'The target entity to analyze.',
439
- },
299
+ entity_id: { type: 'string', description: 'The target entity to analyze.' },
440
300
  mutation: {
441
301
  type: 'object',
442
302
  description: 'The hypothetical change to apply.',
@@ -456,73 +316,21 @@ const TOOLS = [
456
316
  required: ['entity_id', 'mutation'],
457
317
  },
458
318
  },
459
- {
460
- name: 'query_data_targets',
461
- description: 'Search the codebase for data interactions to find all entities that read or write to a specific database table, state object, or data source. This acts as a reverse-index for data flow, essential for planning migrations or state refactors.',
462
- inputSchema: {
463
- type: 'object',
464
- properties: {
465
- project: projectParam,
466
- target_name: {
467
- type: 'string',
468
- description: 'The name of the database table, state object, or data source to query (e.g., "users", "auth_token").',
469
- },
470
- },
471
- required: ['target_name'],
472
- },
473
- },
474
- {
475
- name: 'find_exposure_leaks',
476
- description: 'Analyze the call graph for architectural visibility leaks. Flags paths where a "public" or "api" entity directly accesses a "private" internal entity, potentially leaking sensitive data or bypassing internal service boundaries.',
477
- inputSchema: {
478
- type: 'object',
479
- properties: {
480
- project: projectParam,
481
- },
482
- },
483
- },
484
- {
485
- name: 'find_semantic_clones',
486
- description: 'Analyze the codebase for duplicated logic blocks. Normalizes variables and compares code structure to identify identical algorithms written across different files or even different languages.',
487
- inputSchema: {
488
- type: 'object',
489
- properties: {
490
- project: projectParam,
491
- min_complexity: {
492
- type: 'number',
493
- description: 'Minimum number of logic expressions required to constitute a match (default: 5).',
494
- },
495
- },
496
- },
497
- },
498
319
  {
499
320
  name: 'create_symbol',
500
- description: 'Register a new code symbol in the architectural graph. This does not write to disk, but allows the Impact Simulator and Conflict Matrix to reason about a symbol that is pending creation. Essential for planning new features that introduce new files or modules.',
321
+ description: 'Register a new code symbol in the architectural graph. This does not write to disk, but allows the Impact Simulator and Conflict Matrix to reason about a symbol that is pending creation.',
501
322
  inputSchema: {
502
323
  type: 'object',
503
324
  properties: {
504
325
  project: projectParam,
505
- id: {
506
- type: 'string',
507
- description: 'The unique ID or name for the new symbol.',
508
- },
509
- source_file: {
510
- type: 'string',
511
- description: 'The relative path to the file where this symbol will be created.',
512
- },
513
- layer: {
514
- type: 'string',
515
- description: 'The architectural layer (e.g., "service", "route", "component").',
516
- },
517
- description: {
518
- type: 'string',
519
- description: 'Brief description of the symbol\'s purpose.',
520
- },
326
+ id: { type: 'string', description: 'The unique ID or name for the new symbol.' },
327
+ source_file: { type: 'string', description: 'The relative path to the file where this symbol will be created.' },
328
+ layer: { type: 'string', description: 'The architectural layer (e.g., "service", "route", "component").' },
329
+ description: { type: 'string', description: 'Brief description of the symbol\'s purpose.' },
521
330
  },
522
331
  required: ['id', 'source_file'],
523
332
  },
524
333
  },
525
- // ─── Diff Tools ─────────────────────────────────────────────────
526
334
  {
527
335
  name: 'diff_bundle',
528
336
  description: 'Compare code structure between a worktree and the loaded project. Shows which symbols were added, removed, or modified — not a line diff, but a structural diff showing changed signatures, dependencies, and implementation logic.',
@@ -530,14 +338,8 @@ const TOOLS = [
530
338
  type: 'object',
531
339
  properties: {
532
340
  project: projectParam,
533
- worktree_path: {
534
- type: 'string',
535
- description: 'Absolute path to the worktree or branch checkout to compare against the loaded project',
536
- },
537
- include_unchanged: {
538
- type: 'boolean',
539
- description: 'Include unchanged entities in the output (default: false)',
540
- },
341
+ worktree_path: { type: 'string', description: 'Absolute path to the worktree or branch checkout to compare against the loaded project' },
342
+ include_unchanged: { type: 'boolean', description: 'Include unchanged entities in the output (default: false)' },
541
343
  },
542
344
  required: ['worktree_path'],
543
345
  },
@@ -554,24 +356,10 @@ const TOOLS = [
554
356
  items: {
555
357
  type: 'object',
556
358
  properties: {
557
- id: {
558
- type: 'string',
559
- description: 'Unique task identifier (e.g. "add-dark-mode")',
560
- },
561
- entity_ids: {
562
- type: 'array',
563
- items: { type: 'string' },
564
- description: 'Symbol names or IDs that this task will modify',
565
- },
566
- dimensions: {
567
- type: 'array',
568
- items: { type: 'string' },
569
- description: 'Optional: Code scopes this task will modify. (Legacy support, no longer required for Tier 2 safety).',
570
- },
571
- expand_blast_radius: {
572
- type: 'boolean',
573
- description: 'Include transitively affected symbols in the conflict check (default: false)',
574
- },
359
+ id: { type: 'string', description: 'Unique task identifier (e.g. "add-dark-mode")' },
360
+ entity_ids: { type: 'array', items: { type: 'string' }, description: 'Symbol names or IDs that this task will modify' },
361
+ dimensions: { type: 'array', items: { type: 'string' }, description: 'Optional: Code scopes this task will modify.' },
362
+ expand_blast_radius: { type: 'boolean', description: 'Include transitively affected symbols in the conflict check (default: false)' },
575
363
  },
576
364
  required: ['id', 'entity_ids'],
577
365
  },
@@ -581,230 +369,187 @@ const TOOLS = [
581
369
  required: ['tasks'],
582
370
  },
583
371
  },
372
+ {
373
+ name: 'query_data_targets',
374
+ description: 'Search the codebase for data interactions to find all entities that read or write to a specific database table, state object, or data source.',
375
+ inputSchema: {
376
+ type: 'object',
377
+ properties: {
378
+ project: projectParam,
379
+ target_name: { type: 'string', description: 'The name of the database table, state object, or data source to query (e.g., "users", "auth_token").' },
380
+ },
381
+ required: ['target_name'],
382
+ },
383
+ },
584
384
  ];
585
- // ─── Server Setup ─────────────────────────────────────────────────
586
- async function main() {
587
- // Discover and load projects
588
- const projectDirs = discoverProjects();
589
- let loader = new MultiLoader(projectDirs);
590
- try {
591
- loader.load();
385
+ // ─── Project Resolution (Fallback) ────────────────────────────────
386
+ function resolveProjectName() {
387
+ // If SESHAT_PROJECTS is set to a single name, use it
388
+ if (process.env.SESHAT_PROJECTS && !process.env.SESHAT_PROJECTS.includes(',') && !process.env.SESHAT_PROJECTS.includes('*')) {
389
+ return path.basename(process.env.SESHAT_PROJECTS);
592
390
  }
593
- catch (err) {
594
- process.stderr.write(`Warning: ${err.message}\n`);
391
+ // Fallback to reading local manifest if it exists
392
+ const manifestPath = path.join(process.cwd(), '.seshat', 'manifest.json');
393
+ if (fs.existsSync(manifestPath)) {
394
+ try {
395
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
396
+ if (manifest.projectName)
397
+ return manifest.projectName;
398
+ }
399
+ catch { }
595
400
  }
596
- // Auto-bootstrap: if no projects loaded, try to extract from CWD
597
- if (!loader.isLoaded() && !process.env.SESHAT_PROJECTS) {
598
- const cwd = process.cwd();
599
- const seshatDir = path.join(cwd, '.seshat');
600
- // Only bootstrap when .seshat/ doesn't exist at all (not corruption)
601
- if (!fs.existsSync(seshatDir)) {
602
- process.stderr.write(`[seshat-mcp] No .seshat/ found attempting auto-bootstrap...\n`);
603
- const result = await bootstrap(cwd);
604
- if (result.success) {
605
- // Re-create loader and retry
606
- loader = new MultiLoader([cwd]);
607
- try {
608
- loader.load();
609
- process.stderr.write(`[seshat-mcp] Auto-bootstrap succeeded: ${loader.totalEntities()} entities loaded\n`);
610
- }
611
- catch (err) {
612
- process.stderr.write(`[seshat-mcp] Auto-bootstrap produced files but load failed: ${err.message}\n`);
401
+ // Ultimate fallback to directory name
402
+ return path.basename(process.cwd());
403
+ }
404
+ // ─── Cloud API helper ─────────────────────────────────────────────
405
+ function getCloudUrl(path) {
406
+ const base = process.env.PTAH_CLOUD_URL || 'https://api.papyruslabs.ai';
407
+ // If PTAH_CLOUD_URL includes a full path (legacy), strip it back to the base
408
+ const baseUrl = base.replace(/\/api\/mcp\/execute\/?$/, '');
409
+ return `${baseUrl}${path}`;
410
+ }
411
+ // ─── Server Setup ─────────────────────────────────────────────────
412
+ async function main() {
413
+ const server = new Server({
414
+ name: 'seshat-mcp-cloud-proxy',
415
+ version: '0.12.2',
416
+ }, {
417
+ capabilities: { tools: {} },
418
+ });
419
+ // ─── #3: Dynamic ListTools with tier annotations ──────────────
420
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
421
+ const apiKey = process.env.SESHAT_API_KEY;
422
+ let userTier = 'cartographer';
423
+ let credits = 0;
424
+ // Attempt to fetch account status from the cloud for tier-aware descriptions
425
+ if (apiKey) {
426
+ try {
427
+ const res = await fetch(getCloudUrl('/api/mcp/account'), {
428
+ method: 'GET',
429
+ headers: { 'x-api-key': apiKey },
430
+ });
431
+ if (res.ok) {
432
+ const account = await res.json();
433
+ userTier = account.tier || 'cartographer';
434
+ credits = account.credits || 0;
613
435
  }
614
436
  }
615
- else {
616
- process.stderr.write(`[seshat-mcp] Auto-bootstrap failed: ${result.error || 'unknown error'}\n`);
617
- process.stderr.write(`[seshat-mcp] Starting with 0 entities. Run extraction manually or push to trigger CI.\n`);
437
+ catch {
438
+ // If the account endpoint is unavailable, fall back to showing all tools unlabeled
618
439
  }
619
440
  }
620
- }
621
- initTools(loader);
622
- // Build server name
623
- const projectNames = loader.getProjectNames();
624
- const totalEntities = loader.totalEntities();
625
- const isMulti = loader.isMultiProject();
626
- let serverLabel;
627
- if (!loader.isLoaded()) {
628
- serverLabel = 'seshat-mcp (no projects loaded)';
629
- }
630
- else if (isMulti) {
631
- serverLabel = `seshat-mcp (${projectNames.length} projects, ${totalEntities} entities)`;
632
- }
633
- else {
634
- const manifest = loader.getManifest();
635
- serverLabel = `seshat-mcp (${manifest?.projectName || projectNames[0] || 'unknown'})`;
636
- }
637
- const server = new Server({
638
- name: serverLabel,
639
- version: '0.5.0',
640
- }, {
641
- capabilities: {
642
- tools: {},
643
- },
441
+ const annotatedTools = TOOLS.map((tool) => {
442
+ const requiredTier = TOOL_TIERS[tool.name];
443
+ if (!requiredTier)
444
+ return tool; // get_account_status — always available
445
+ const hasAccess = tierAtLeast(userTier, requiredTier);
446
+ if (hasAccess)
447
+ return tool;
448
+ // Annotate locked tools with tier requirement
449
+ return {
450
+ ...tool,
451
+ description: `[LOCKED requires ${TIER_LABELS[requiredTier]}] ${tool.description}`,
452
+ };
453
+ });
454
+ return { tools: annotatedTools };
644
455
  });
645
- // List available tools
646
- server.setRequestHandler(ListToolsRequestSchema, async () => ({
647
- tools: TOOLS,
648
- }));
649
- // Handle tool calls
456
+ // ─── CallTool handler with #1 (_meta piggyback) and #2 (get_account_status) ──
650
457
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
651
458
  const { name, arguments: args } = request.params;
652
- const startTime = Date.now();
653
- try {
654
- if (!loader.isLoaded()) {
459
+ const apiKey = process.env.SESHAT_API_KEY;
460
+ if (!apiKey) {
461
+ return {
462
+ content: [{ type: 'text', text: JSON.stringify({ error: 'SESHAT_API_KEY environment variable is required to use Ptah Cloud tools.' }, null, 2) }],
463
+ isError: true,
464
+ };
465
+ }
466
+ // ─── #2: get_account_status tool ──────────────────────────────
467
+ if (name === 'get_account_status') {
468
+ try {
469
+ const res = await fetch(getCloudUrl('/api/mcp/account'), {
470
+ method: 'GET',
471
+ headers: { 'x-api-key': apiKey },
472
+ });
473
+ if (!res.ok) {
474
+ const errorText = await res.text();
475
+ return {
476
+ content: [{ type: 'text', text: JSON.stringify({ error: `Failed to fetch account status (${res.status}): ${errorText}` }, null, 2) }],
477
+ isError: true,
478
+ };
479
+ }
480
+ const account = await res.json();
481
+ const userTier = account.tier || 'cartographer';
482
+ const credits = account.credits || 0;
483
+ // Build tool availability breakdown
484
+ const toolsByTier = {
485
+ cartographer: { available: [], locked: [] },
486
+ analyst: { available: [], locked: [] },
487
+ architect: { available: [], locked: [] },
488
+ };
489
+ for (const [toolName, requiredTier] of Object.entries(TOOL_TIERS)) {
490
+ const bucket = tierAtLeast(userTier, requiredTier) ? 'available' : 'locked';
491
+ toolsByTier[requiredTier][bucket].push(toolName);
492
+ }
655
493
  return {
656
- content: [{
657
- type: 'text',
658
- text: JSON.stringify({
659
- error: 'No projects loaded. Ensure .seshat/_bundle.json exists or set SESHAT_PROJECTS env var.',
660
- }, null, 2),
661
- }],
494
+ content: [{ type: 'text', text: JSON.stringify({
495
+ tier: userTier,
496
+ tier_label: TIER_LABELS[userTier],
497
+ ptah_credits: credits,
498
+ tools: toolsByTier,
499
+ upgrade_url: 'https://ptah.papyruslabs.ai/settings/billing',
500
+ }, null, 2) }],
662
501
  };
663
502
  }
664
- let result;
665
- switch (name) { // Meta
666
- case 'list_projects':
667
- result = {
668
- multiProject: isMulti,
669
- projects: loader.getProjectInfo(),
670
- totalEntities,
671
- hint: isMulti
672
- ? 'Pass the project name as the "project" parameter to all other tools.'
673
- : 'Single project mode — the "project" parameter is optional.',
674
- };
675
- break;
676
- // Core tools
677
- case 'query_entities':
678
- result = queryEntities(args);
679
- break;
680
- case 'get_entity':
681
- result = getEntity(args);
682
- break;
683
- case 'get_dependencies':
684
- result = getDependencies(args);
685
- break;
686
- case 'get_data_flow':
687
- result = getDataFlow(args);
688
- break;
689
- case 'find_by_constraint':
690
- result = findByConstraint(args);
691
- break;
692
- case 'get_blast_radius':
693
- result = getBlastRadius(args);
694
- break;
695
- case 'list_modules':
696
- result = listModules(args);
697
- break;
698
- case 'get_topology':
699
- result = getTopology(args);
700
- break;
701
- // Analysis Tools
702
- case 'find_dead_code':
703
- result = findDeadCode(args);
704
- break;
705
- case 'find_layer_violations':
706
- result = findLayerViolations(args);
707
- break;
708
- case 'get_coupling_metrics':
709
- result = getCouplingMetrics(args);
710
- break;
711
- case 'get_auth_matrix':
712
- result = getAuthMatrix(args);
713
- break;
714
- case 'find_error_gaps':
715
- result = findErrorGaps(args);
716
- break;
717
- case 'get_test_coverage':
718
- result = getTestCoverage(args);
719
- break;
720
- case 'get_optimal_context':
721
- result = getOptimalContext(args);
722
- break;
723
- case 'estimate_task_cost':
724
- result = await estimateTaskCost(args);
725
- break;
726
- case 'report_actual_burn':
727
- result = await reportActualBurn(args);
728
- break;
729
- // Analysis & Impact Tools
730
- case 'find_runtime_violations':
731
- result = find_runtime_violations(args);
732
- break;
733
- case 'find_ownership_violations':
734
- result = find_ownership_violations(args);
735
- break;
736
- case 'query_traits':
737
- result = query_traits(args);
738
- break;
739
- case 'simulate_mutation':
740
- result = simulate_mutation(args);
741
- break;
742
- case 'query_data_targets':
743
- result = query_data_targets(args);
744
- break;
745
- case 'find_exposure_leaks':
746
- result = find_exposure_leaks(args);
747
- break;
748
- case 'find_semantic_clones':
749
- result = find_semantic_clones(args);
750
- break;
751
- case 'create_symbol':
752
- result = create_symbol(args);
753
- break;
754
- // Diff Tools
755
- case 'diff_bundle':
756
- result = await diffBundle(args);
757
- break;
758
- case 'conflict_matrix':
759
- result = conflictMatrix(args);
760
- break;
761
- default:
762
- result = { error: `Unknown tool: ${name}` };
503
+ catch (err) {
504
+ return {
505
+ content: [{ type: 'text', text: JSON.stringify({ error: `Account status check failed: ${err.message}` }, null, 2) }],
506
+ isError: true,
507
+ };
763
508
  }
764
- // Log telemetry for the tool execution
765
- if (isSupabaseConfigured() && name !== 'list_projects') {
766
- const executionMs = Date.now() - startTime;
767
- const projectHash = args && typeof args === 'object' && 'project' in args
768
- ? String(args.project)
769
- : loader.getProjectNames()[0] || 'unknown';
770
- // Fire-and-forget telemetry log
771
- logTelemetry({
772
- tool_name: name,
773
- project_hash: projectHash,
774
- execution_ms: executionMs,
775
- // user_id will be added by the Ptah API gateway if passing through there,
776
- // or stay null if running strictly local CLI without an injected token.
777
- }).catch(() => { });
509
+ }
510
+ // Determine the project hash for this workspace
511
+ const project_hash = (args && typeof args === 'object' && 'project' in args)
512
+ ? String(args.project)
513
+ : resolveProjectName();
514
+ try {
515
+ // Proxy the tool call to the cloud
516
+ const res = await fetch(getCloudUrl('/api/mcp/execute'), {
517
+ method: 'POST',
518
+ headers: {
519
+ 'Content-Type': 'application/json',
520
+ 'x-api-key': apiKey
521
+ },
522
+ body: JSON.stringify({
523
+ tool: name,
524
+ project_hash,
525
+ args
526
+ })
527
+ });
528
+ if (!res.ok) {
529
+ const errorText = await res.text();
530
+ return {
531
+ content: [{ type: 'text', text: JSON.stringify({ error: `Cloud API Error (${res.status}): ${errorText}` }, null, 2) }],
532
+ isError: true
533
+ };
778
534
  }
535
+ const result = await res.json();
536
+ // ─── #1: _meta piggyback — append account context to every response ──
537
+ // The cloud API includes _meta in its response when available.
538
+ // If it doesn't, we pass through the result as-is.
779
539
  return {
780
- content: [{
781
- type: 'text',
782
- text: JSON.stringify(result, null, 2),
783
- }],
540
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
784
541
  };
785
542
  }
786
543
  catch (err) {
787
544
  return {
788
- content: [{
789
- type: 'text',
790
- text: JSON.stringify({
791
- error: `Tool ${name} failed: ${err.message}`,
792
- }, null, 2),
793
- }],
545
+ content: [{ type: 'text', text: JSON.stringify({ error: `Cloud proxy failed: ${err.message}` }, null, 2) }],
794
546
  isError: true,
795
547
  };
796
548
  }
797
549
  });
798
- // Start stdio transport
799
550
  const transport = new StdioServerTransport();
800
551
  await server.connect(transport);
801
- if (isMulti) {
802
- process.stderr.write(`Seshat MCP server started: ${projectNames.length} projects (${totalEntities} entities) — ${projectNames.join(', ')}\n`);
803
- }
804
- else {
805
- const manifest = loader.getManifest();
806
- process.stderr.write(`Seshat MCP server started: ${manifest?.projectName || 'unknown'} (${totalEntities} entities)\n`);
807
- }
552
+ process.stderr.write(`Seshat Cloud Proxy started. Routing requests to Ptah API...\n`);
808
553
  }
809
554
  main().catch((err) => {
810
555
  process.stderr.write(`Fatal: ${err.message}\n`);
@@ -7,6 +7,7 @@
7
7
  * These tools compare TWO entity sets (base vs branch, or task vs task),
8
8
  * unlike the single-bundle queries in index.ts and functors.ts.
9
9
  */
10
+ import type { ProjectLoader } from '../types.js';
10
11
  export declare function diffBundle(args: {
11
12
  worktree_path: string;
12
13
  project?: string;
@@ -5,7 +5,7 @@
5
5
  * Each tool computes derived insights (dead code, layer violations,
6
6
  * coupling metrics, auth coverage, etc.) from the entity data.
7
7
  */
8
- import type { JstfEntity } from '../types.js';
8
+ import type { JstfEntity, ProjectLoader } from '../types.js';
9
9
  /**
10
10
  * Estimate the token cost of loading an entity's source code into an LLM context.
11
11
  * Uses real sourceTokens from the extraction pipeline when available (v0.3.2+),
@@ -16,19 +16,19 @@ export declare function findDeadCode(args: {
16
16
  include_tests?: boolean;
17
17
  project?: string;
18
18
  }, loader: ProjectLoader): unknown;
19
- export declare function findLayerViolations(args?: {
19
+ export declare function findLayerViolations(args: {
20
20
  project?: string;
21
- }): unknown;
21
+ }, loader: ProjectLoader): unknown;
22
22
  export declare function getCouplingMetrics(args: {
23
23
  group_by?: 'module' | 'layer';
24
24
  project?: string;
25
25
  }, loader: ProjectLoader): unknown;
26
- export declare function getAuthMatrix(args?: {
26
+ export declare function getAuthMatrix(args: {
27
27
  project?: string;
28
- }): unknown;
29
- export declare function findErrorGaps(args?: {
28
+ }, loader: ProjectLoader): unknown;
29
+ export declare function findErrorGaps(args: {
30
30
  project?: string;
31
- }): unknown;
31
+ }, loader: ProjectLoader): unknown;
32
32
  export declare function getTestCoverage(args: {
33
33
  weight_by_blast_radius?: boolean;
34
34
  project?: string;
@@ -66,12 +66,12 @@ export declare function reportActualBurn(args: {
66
66
  project?: string;
67
67
  notes?: string;
68
68
  }, loader: ProjectLoader): Promise<unknown>;
69
- export declare function find_runtime_violations(args?: {
69
+ export declare function find_runtime_violations(args: {
70
70
  project?: string;
71
- }): unknown;
72
- export declare function find_ownership_violations(args?: {
71
+ }, loader: ProjectLoader): unknown;
72
+ export declare function find_ownership_violations(args: {
73
73
  project?: string;
74
- }): unknown;
74
+ }, loader: ProjectLoader): unknown;
75
75
  export declare function query_traits(args: {
76
76
  trait: string;
77
77
  project?: string;
@@ -91,13 +91,13 @@ export declare function query_data_targets(args: {
91
91
  target_name: string;
92
92
  project?: string;
93
93
  }, loader: ProjectLoader): unknown;
94
- export declare function find_exposure_leaks(args?: {
94
+ export declare function find_exposure_leaks(args: {
95
95
  project?: string;
96
- }): unknown;
97
- export declare function find_semantic_clones(args?: {
96
+ }, loader: ProjectLoader): unknown;
97
+ export declare function find_semantic_clones(args: {
98
98
  project?: string;
99
99
  min_complexity?: number;
100
- }): unknown;
100
+ }, loader: ProjectLoader): unknown;
101
101
  export declare function create_symbol(args: {
102
102
  id: string;
103
103
  source_file: string;
@@ -151,7 +151,7 @@ export function findDeadCode(args, loader) {
151
151
  };
152
152
  }
153
153
  // ─── Tool: find_layer_violations ─────────────────────────────────
154
- export function findLayerViolations(args) {
154
+ export function findLayerViolations(args, loader) {
155
155
  const projErr = validateProject(args?.project, loader);
156
156
  if (projErr)
157
157
  return { error: projErr };
@@ -278,7 +278,7 @@ export function getCouplingMetrics(args, loader) {
278
278
  };
279
279
  }
280
280
  // ─── Tool: get_auth_matrix ───────────────────────────────────────
281
- export function getAuthMatrix(args) {
281
+ export function getAuthMatrix(args, loader) {
282
282
  const projErr = validateProject(args?.project, loader);
283
283
  if (projErr)
284
284
  return { error: projErr };
@@ -335,7 +335,7 @@ export function getAuthMatrix(args) {
335
335
  };
336
336
  }
337
337
  // ─── Tool: find_error_gaps ───────────────────────────────────────
338
- export function findErrorGaps(args) {
338
+ export function findErrorGaps(args, loader) {
339
339
  const projErr = validateProject(args?.project, loader);
340
340
  if (projErr)
341
341
  return { error: projErr };
@@ -822,7 +822,7 @@ export async function reportActualBurn(args, loader) {
822
822
  };
823
823
  }
824
824
  // ─── Tool: find_runtime_violations (Runtime Context) ────────────
825
- export function find_runtime_violations(args) {
825
+ export function find_runtime_violations(args, loader) {
826
826
  const projErr = validateProject(args?.project, loader);
827
827
  if (projErr)
828
828
  return { error: projErr };
@@ -863,7 +863,7 @@ export function find_runtime_violations(args) {
863
863
  };
864
864
  }
865
865
  // ─── Tool: find_ownership_violations (Ownership & Lifetimes) ─────
866
- export function find_ownership_violations(args) {
866
+ export function find_ownership_violations(args, loader) {
867
867
  const projErr = validateProject(args?.project, loader);
868
868
  if (projErr)
869
869
  return { error: projErr };
@@ -1079,7 +1079,7 @@ export function query_data_targets(args, loader) {
1079
1079
  };
1080
1080
  }
1081
1081
  // ─── Tool: find_exposure_leaks (Architectural Visibility) ───────
1082
- export function find_exposure_leaks(args) {
1082
+ export function find_exposure_leaks(args, loader) {
1083
1083
  const projErr = validateProject(args?.project, loader);
1084
1084
  if (projErr)
1085
1085
  return { error: projErr };
@@ -1114,7 +1114,7 @@ export function find_exposure_leaks(args) {
1114
1114
  }
1115
1115
  // ─── Tool: find_semantic_clones (Logic Analysis) ────────────────
1116
1116
  import { createHash } from 'crypto';
1117
- export function find_semantic_clones(args) {
1117
+ export function find_semantic_clones(args, loader) {
1118
1118
  const projErr = validateProject(args?.project, loader);
1119
1119
  if (projErr)
1120
1120
  return { error: projErr };
@@ -7,7 +7,7 @@
7
7
  * Multi-project: when multiple projects are loaded, each tool accepts an optional
8
8
  * `project` parameter. In multi-project mode, `project` is required.
9
9
  */
10
- import type { JstfEntity } from '../types.js';
10
+ import type { JstfEntity, ProjectLoader } from '../types.js';
11
11
  import { type CallGraph } from '../graph.js';
12
12
  export declare function getGraph(project: string | undefined, loader: ProjectLoader): CallGraph;
13
13
  /**
package/package.json CHANGED
@@ -1,12 +1,19 @@
1
1
  {
2
2
  "name": "@papyruslabsai/seshat-mcp",
3
- "version": "0.12.0",
3
+ "version": "0.12.2",
4
4
  "description": "Semantic MCP server — exposes a codebase's structure, dependencies, and constraints as queryable tools",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "seshat-mcp": "./dist/index.js"
8
8
  },
9
9
  "main": "./dist/index.js",
10
+ "exports": {
11
+ ".": "./dist/index.js",
12
+ "./tools": "./dist/tools/index.js",
13
+ "./functors": "./dist/tools/functors.js",
14
+ "./diff": "./dist/tools/diff.js",
15
+ "./types": "./dist/types.js"
16
+ },
10
17
  "scripts": {
11
18
  "build": "tsc",
12
19
  "dev": "tsc --watch",