@papyruslabsai/seshat-mcp 0.12.2 → 0.13.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 CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * @papyruslabs/seshat-mcp — Semantic Code Analysis MCP Server (Cloud Proxy)
3
+ * @papyruslabs/seshat-mcp — Structural Code Analysis MCP Server (Cloud Proxy)
4
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.
5
+ * Declares the Seshat tool suite to Claude/AI clients. All tool calls are
6
+ * proxied to the Ptah Cloud API. Tool descriptions are written as triggers —
7
+ * they tell the LLM *when* to reach for each tool, not just what it does.
8
8
  */
9
9
  export {};
package/dist/index.js CHANGED
@@ -1,16 +1,29 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * @papyruslabs/seshat-mcp — Semantic Code Analysis MCP Server (Cloud Proxy)
3
+ * @papyruslabs/seshat-mcp — Structural Code Analysis MCP Server (Cloud Proxy)
4
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.
5
+ * Declares the Seshat tool suite to Claude/AI clients. All tool calls are
6
+ * proxied to the Ptah Cloud API. Tool descriptions are written as triggers —
7
+ * they tell the LLM *when* to reach for each tool, not just what it does.
8
8
  */
9
9
  import fs from 'fs';
10
10
  import path from 'path';
11
11
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
12
12
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
13
13
  import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
14
+ // ─── Server Instructions ─────────────────────────────────────────
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.
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
23
+
24
+ Start with list_projects to see what's loaded. All tools are read-only and safe to call speculatively.
25
+
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.`;
14
27
  const TIER_ORDER = ['cartographer', 'analyst', 'architect'];
15
28
  const TOOL_TIERS = {
16
29
  // Cartographer (free) — explore and navigate
@@ -52,41 +65,51 @@ const TIER_LABELS = {
52
65
  function tierAtLeast(userTier, requiredTier) {
53
66
  return TIER_ORDER.indexOf(userTier) >= TIER_ORDER.indexOf(requiredTier);
54
67
  }
55
- // ─── Project param definition ───
68
+ // ─── Shared Definitions ──────────────────────────────────────────
56
69
  const projectParam = {
57
70
  type: 'string',
58
71
  description: 'Project name (required in multi-project mode). Use list_projects to see available projects.',
59
72
  };
73
+ const READ_ONLY = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false };
74
+ const READ_ONLY_OPEN = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true };
60
75
  // ─── Tool Definitions ─────────────────────────────────────────────
76
+ // Descriptions are written as TRIGGERS: they tell the LLM when to
77
+ // reach for the tool, what it returns, and how it compares to built-in
78
+ // alternatives like grep/Read.
61
79
  const TOOLS = [
80
+ // ─── Always Visible ───────────────────────────────────────────────
62
81
  {
63
82
  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.',
83
+ description: 'See your current plan, available tools, and credit balance. Call this if a tool returns a tier error or you want to know what tools are available.',
65
84
  inputSchema: { type: 'object', properties: {} },
85
+ annotations: READ_ONLY_OPEN,
66
86
  },
87
+ // ─── Cartographer (Free Tier) ─────────────────────────────────────
67
88
  {
68
89
  name: 'list_projects',
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.',
90
+ description: 'Start here. Returns all synced codebases with their size, language, and project name. You need the project name for every other tool.',
70
91
  inputSchema: { type: 'object', properties: {} },
92
+ annotations: READ_ONLY_OPEN,
71
93
  },
72
94
  {
73
95
  name: 'query_entities',
74
- description: 'Search code entities by name, architectural layer (route/service/repository/component), module, or source language. Returns entity summaries with constraint tags.',
96
+ description: 'Like grep but for code structure. Find functions, classes, and routes by name, architectural layer (route/service/component), or module. Returns matching symbols with their type, file, and layer — use this instead of grep when you need to find code by what it does, not by text content.',
75
97
  inputSchema: {
76
98
  type: 'object',
77
99
  properties: {
78
100
  project: projectParam,
79
- query: { type: 'string', description: 'Search term — matches against entity name, ID, source file, and module' },
101
+ query: { type: 'string', description: 'Search term — matches against symbol name, ID, source file, and module' },
80
102
  layer: { type: 'string', description: 'Filter by architectural layer: route, controller, service, repository, utility, hook, component, schema' },
81
103
  module: { type: 'string', description: 'Filter by module (partial match)' },
82
104
  language: { type: 'string', description: 'Filter by source language: javascript, typescript, python, go, rust, etc.' },
83
105
  limit: { type: 'number', description: 'Max results to return (default: 50)' },
84
106
  },
85
107
  },
108
+ annotations: READ_ONLY_OPEN,
86
109
  },
87
110
  {
88
111
  name: 'get_entity',
89
- description: 'Get complete details for a specific code entity. Returns structure, call graph, data flow, constraints, context, ownership, type info, runtime, and logic.',
112
+ description: 'Get everything about one function or class its signature, callers, callees, data flow, constraints, and source location. Use this when you need to deeply understand a single symbol before modifying it. Returns more than reading the source file because it includes the dependency context.',
90
113
  inputSchema: {
91
114
  type: 'object',
92
115
  properties: {
@@ -95,10 +118,11 @@ const TOOLS = [
95
118
  },
96
119
  required: ['id'],
97
120
  },
121
+ annotations: READ_ONLY_OPEN,
98
122
  },
99
123
  {
100
124
  name: 'get_dependencies',
101
- description: 'Traverse the call graph for an entity. Shows callers (who calls this), callees (what this calls), and imports. Supports depth-limited traversal for exploring transitive dependencies.',
125
+ description: 'Trace who calls a function and what it calls, up to N levels deep. Use this instead of grep-for-function-name when you need the actual call chain — returns the dependency graph, not text matches. Covers callers (upstream), callees (downstream), or both.',
102
126
  inputSchema: {
103
127
  type: 'object',
104
128
  properties: {
@@ -109,10 +133,11 @@ const TOOLS = [
109
133
  },
110
134
  required: ['entity_id'],
111
135
  },
136
+ annotations: READ_ONLY_OPEN,
112
137
  },
113
138
  {
114
139
  name: 'get_data_flow',
115
- description: 'Get data flow for an entity: what data flows in (inputs/sources), what flows out (outputs/returns), and what gets mutated (database writes, state changes).',
140
+ description: 'See what data a function reads, returns, and mutates (DB writes, state changes). Use this when debugging data bugs or when you need to verify whether a function has side effects before refactoring it.',
116
141
  inputSchema: {
117
142
  type: 'object',
118
143
  properties: {
@@ -121,10 +146,11 @@ const TOOLS = [
121
146
  },
122
147
  required: ['entity_id'],
123
148
  },
149
+ annotations: READ_ONLY_OPEN,
124
150
  },
125
151
  {
126
152
  name: 'find_by_constraint',
127
- description: 'Search entities by behavioral properties. Find functions by security properties (AUTH, VALIDATED), behavioral properties (PURE, THROWS, DB_ACCESS, NETWORK_IO), or performance characteristics.',
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?"',
128
154
  inputSchema: {
129
155
  type: 'object',
130
156
  properties: {
@@ -133,10 +159,11 @@ const TOOLS = [
133
159
  },
134
160
  required: ['constraint'],
135
161
  },
162
+ annotations: READ_ONLY_OPEN,
136
163
  },
137
164
  {
138
165
  name: 'get_blast_radius',
139
- description: 'Compute the blast radius for a set of entities. Returns all transitively affected entities — both upstream (callers) and downstream (callees) — with depth annotations showing distance from the change.',
166
+ description: 'Before modifying a function, call this to see everything that could break. Returns all transitively affected symbols — both upstream callers and downstream callees — with distance from the change point. Like git log --follow but for runtime impact. Designed for repeated use: as you discover new symbols with other tools, call this again on them to expand your understanding of the affected surface.',
140
167
  inputSchema: {
141
168
  type: 'object',
142
169
  properties: {
@@ -145,10 +172,11 @@ const TOOLS = [
145
172
  },
146
173
  required: ['entity_ids'],
147
174
  },
175
+ annotations: READ_ONLY_OPEN,
148
176
  },
149
177
  {
150
178
  name: 'list_modules',
151
- description: 'List and count entities grouped by a property. See the architectural structure (by layer), module organization (by module), file distribution (by file), or language breakdown (by language).',
179
+ description: 'Get a bird\'s-eye view of how the codebase is organized. Groups all symbols by architectural layer (route/service/component), module, file, or language with counts. Use this to orient yourself in an unfamiliar codebase before diving into specifics.',
152
180
  inputSchema: {
153
181
  type: 'object',
154
182
  properties: {
@@ -156,18 +184,20 @@ const TOOLS = [
156
184
  group_by: { type: 'string', enum: ['layer', 'module', 'file', 'language'], description: 'How to group entities (default: layer)' },
157
185
  },
158
186
  },
187
+ annotations: READ_ONLY_OPEN,
159
188
  },
160
189
  {
161
190
  name: 'get_topology',
162
- description: 'Get the API topology: routes, plugins, auth patterns, and database tables. Built from architectural context and dependency analysis across all entities.',
191
+ description: 'Get the full API surface map in one call — all routes, middleware, auth patterns, and database tables. Use this when you need to understand the overall architecture without reading every file. Returns the information you\'d normally piece together from dozens of file reads.',
163
192
  inputSchema: {
164
193
  type: 'object',
165
194
  properties: { project: projectParam },
166
195
  },
196
+ annotations: READ_ONLY_OPEN,
167
197
  },
168
198
  {
169
199
  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.',
200
+ description: 'Before working on a function, call this to get the most relevant related code ranked by importance and fitted to a token budget. Returns a prioritized reading list of symbols you should understand — better than guessing which files to open. Designed for iterative use: call it on your target, read the top results, then call it again on any surprising dependencies to build a complete picture.',
171
201
  inputSchema: {
172
202
  type: 'object',
173
203
  properties: {
@@ -178,11 +208,12 @@ const TOOLS = [
178
208
  },
179
209
  required: ['target_entity'],
180
210
  },
211
+ annotations: READ_ONLY_OPEN,
181
212
  },
182
213
  // ─── Analyst Tools (Tier 2) ───────────────────────────────────────
183
214
  {
184
215
  name: 'find_dead_code',
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.',
216
+ description: 'Find functions that nothing calls. Walks the call graph from all entry points (routes, exports, tests) and flags symbols that are unreachable. Use this during cleanup or before a release to find safe deletion candidates.',
186
217
  inputSchema: {
187
218
  type: 'object',
188
219
  properties: {
@@ -190,18 +221,20 @@ const TOOLS = [
190
221
  include_tests: { type: 'boolean', description: 'Include test entities in dead code results (default: false)' },
191
222
  },
192
223
  },
224
+ annotations: READ_ONLY_OPEN,
193
225
  },
194
226
  {
195
227
  name: 'find_layer_violations',
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).',
228
+ description: 'Find places where the architecture is broken a database repository calling a route handler, a utility importing a component. Returns every backward or skip-layer dependency that violates clean architecture.',
197
229
  inputSchema: {
198
230
  type: 'object',
199
231
  properties: { project: projectParam },
200
232
  },
233
+ annotations: READ_ONLY_OPEN,
201
234
  },
202
235
  {
203
236
  name: 'get_coupling_metrics',
204
- description: 'Compute coupling, cohesion, and instability metrics for modules or layers. Analyzes dependency edges between and within groups. High coupling + low cohesion = candidates for refactoring.',
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.',
205
238
  inputSchema: {
206
239
  type: 'object',
207
240
  properties: {
@@ -209,26 +242,29 @@ const TOOLS = [
209
242
  group_by: { type: 'string', enum: ['module', 'layer'], description: 'Group entities by module or layer (default: module)' },
210
243
  },
211
244
  },
245
+ annotations: READ_ONLY_OPEN,
212
246
  },
213
247
  {
214
248
  name: 'get_auth_matrix',
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.',
249
+ description: 'Audit authentication coverage. Shows which API routes and controllers require auth and which don\'t, plus inconsistencies like database access without auth checks. Use this for security reviews.',
216
250
  inputSchema: {
217
251
  type: 'object',
218
252
  properties: { project: projectParam },
219
253
  },
254
+ annotations: READ_ONLY_OPEN,
220
255
  },
221
256
  {
222
257
  name: 'find_error_gaps',
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.',
258
+ description: 'Find crash risks: functions that throw or have network/DB side effects whose callers don\'t catch errors. Returns the specific caller→callee pairs where exceptions can propagate unhandled. Use this before shipping to find missing error handling.',
224
259
  inputSchema: {
225
260
  type: 'object',
226
261
  properties: { project: projectParam },
227
262
  },
263
+ annotations: READ_ONLY_OPEN,
228
264
  },
229
265
  {
230
266
  name: 'get_test_coverage',
231
- description: 'Compute semantic test coverage: which production entities are exercised by test entities via the call graph. Optionally weight uncovered entities by blast radius to prioritize what to test first.',
267
+ description: 'See which production functions are actually exercised by tests via the call graph — semantic coverage, not line coverage. Optionally ranks uncovered functions by blast radius so you know which missing tests are riskiest.',
232
268
  inputSchema: {
233
269
  type: 'object',
234
270
  properties: {
@@ -236,20 +272,23 @@ const TOOLS = [
236
272
  weight_by_blast_radius: { type: 'boolean', description: 'Rank uncovered entities by blast radius to prioritize testing (default: false, slower)' },
237
273
  },
238
274
  },
275
+ annotations: READ_ONLY_OPEN,
239
276
  },
240
277
  {
241
278
  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.',
279
+ description: 'Find architectural boundary leaks where framework-agnostic code imports framework-specific code. Use this when separating core logic from framework dependencies.',
243
280
  inputSchema: { type: 'object', properties: { project: projectParam } },
281
+ annotations: READ_ONLY_OPEN,
244
282
  },
245
283
  {
246
284
  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.',
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.',
248
286
  inputSchema: { type: 'object', properties: { project: projectParam } },
287
+ annotations: READ_ONLY_OPEN,
249
288
  },
250
289
  {
251
290
  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.',
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.',
253
292
  inputSchema: {
254
293
  type: 'object',
255
294
  properties: {
@@ -258,27 +297,30 @@ const TOOLS = [
258
297
  },
259
298
  required: ['trait'],
260
299
  },
300
+ annotations: READ_ONLY_OPEN,
261
301
  },
262
302
  {
263
303
  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.',
304
+ description: 'Find places where public/API code directly accesses private internals, bypassing the intended abstraction boundary. Use this during API design reviews or before extracting a module.',
265
305
  inputSchema: { type: 'object', properties: { project: projectParam } },
306
+ annotations: READ_ONLY_OPEN,
266
307
  },
267
308
  {
268
309
  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.',
310
+ description: 'Find duplicated logic across the codebase. Normalizes variable names and compares code structure to catch identical algorithms in different files even across different languages. Use this before a DRY refactor.',
270
311
  inputSchema: {
271
312
  type: 'object',
272
313
  properties: {
273
314
  project: projectParam,
274
- min_complexity: { type: 'number', description: 'Minimum number of logic expressions required to constitute a match (default: 5).' },
315
+ min_complexity: { type: 'number', description: 'Minimum logic expressions to count as a match (default: 5)' },
275
316
  },
276
317
  },
318
+ annotations: READ_ONLY_OPEN,
277
319
  },
278
320
  // ─── Architect Tools (Tier 3) ─────────────────────────────────────
279
321
  {
280
322
  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.',
323
+ description: 'Before starting a code change, estimate how many tokens it will consume. Computes the blast radius, sums source tokens across all affected symbols, and projects total burn including iteration cycles. Use this to check if a task fits your context budget before committing to it.',
282
324
  inputSchema: {
283
325
  type: 'object',
284
326
  properties: {
@@ -288,10 +330,11 @@ const TOOLS = [
288
330
  },
289
331
  required: ['target_entities'],
290
332
  },
333
+ annotations: READ_ONLY_OPEN,
291
334
  },
292
335
  {
293
336
  name: 'simulate_mutation',
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.',
337
+ description: 'Simulate a hypothetical code change without writing anything. Propose adding or removing constraints/traits on a symbol and see which other symbols would break and what fixes they\'d need. Use this to plan a change before making it.',
295
338
  inputSchema: {
296
339
  type: 'object',
297
340
  properties: {
@@ -315,10 +358,11 @@ const TOOLS = [
315
358
  },
316
359
  required: ['entity_id', 'mutation'],
317
360
  },
361
+ annotations: READ_ONLY_OPEN,
318
362
  },
319
363
  {
320
364
  name: 'create_symbol',
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.',
365
+ description: 'Register a planned new symbol in the graph before it exists on disk. This lets simulate_mutation and conflict_matrix reason about code you haven\'t written yet. Nothing is written to disk.',
322
366
  inputSchema: {
323
367
  type: 'object',
324
368
  properties: {
@@ -330,23 +374,25 @@ const TOOLS = [
330
374
  },
331
375
  required: ['id', 'source_file'],
332
376
  },
377
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
333
378
  },
334
379
  {
335
380
  name: 'diff_bundle',
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.',
381
+ description: 'Compare a worktree against the loaded project to see what changed structurally — not a line diff, but which symbols were added, removed, or had their signatures/dependencies changed. Use this after making changes to verify the structural impact.',
337
382
  inputSchema: {
338
383
  type: 'object',
339
384
  properties: {
340
385
  project: projectParam,
341
- worktree_path: { type: 'string', description: 'Absolute path to the worktree or branch checkout to compare against the loaded project' },
386
+ worktree_path: { type: 'string', description: 'Absolute path to the worktree or branch checkout to compare' },
342
387
  include_unchanged: { type: 'boolean', description: 'Include unchanged entities in the output (default: false)' },
343
388
  },
344
389
  required: ['worktree_path'],
345
390
  },
391
+ annotations: READ_ONLY_OPEN,
346
392
  },
347
393
  {
348
394
  name: 'conflict_matrix',
349
- description: 'Given multiple tasks, classify every task pair into conflict tiers to determine parallelization safety. Tier 1 (different files, safe), Tier 2 (same file, different symbols, safe via surgical splicing), Tier 3 (same symbol, MUST sequence to maintain splice integrity). Returns a pairwise matrix and a suggested execution plan.',
395
+ description: 'Given multiple planned tasks, check which ones can run in parallel safely. Classifies every task pair: Tier 1 (different files, safe), Tier 2 (same file different symbols, careful), Tier 3 (same symbol, must sequence). Returns a matrix and suggested execution order.',
350
396
  inputSchema: {
351
397
  type: 'object',
352
398
  properties: {
@@ -363,23 +409,25 @@ const TOOLS = [
363
409
  },
364
410
  required: ['id', 'entity_ids'],
365
411
  },
366
- description: 'Array of tasks to check for conflicts. Each task specifies which entities it will modify.',
412
+ description: 'Array of tasks to check for conflicts.',
367
413
  },
368
414
  },
369
415
  required: ['tasks'],
370
416
  },
417
+ annotations: READ_ONLY_OPEN,
371
418
  },
372
419
  {
373
420
  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.',
421
+ description: 'Find every function that reads from or writes to a specific database table, state object, or data source. Use this when you need to understand all the code paths that touch a particular data store.',
375
422
  inputSchema: {
376
423
  type: 'object',
377
424
  properties: {
378
425
  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").' },
426
+ target_name: { type: 'string', description: 'The name of the database table, state object, or data source (e.g., "users", "auth_token").' },
380
427
  },
381
428
  required: ['target_name'],
382
429
  },
430
+ annotations: READ_ONLY_OPEN,
383
431
  },
384
432
  ];
385
433
  // ─── Project Resolution (Fallback) ────────────────────────────────
@@ -411,17 +459,17 @@ function getCloudUrl(path) {
411
459
  // ─── Server Setup ─────────────────────────────────────────────────
412
460
  async function main() {
413
461
  const server = new Server({
414
- name: 'seshat-mcp-cloud-proxy',
415
- version: '0.12.2',
462
+ name: 'seshat',
463
+ version: '0.13.0',
416
464
  }, {
417
465
  capabilities: { tools: {} },
466
+ instructions: SERVER_INSTRUCTIONS,
418
467
  });
419
- // ─── #3: Dynamic ListTools with tier annotations ──────────────
468
+ // ─── Dynamic ListTools only expose tools the user can access ──
420
469
  server.setRequestHandler(ListToolsRequestSchema, async () => {
421
470
  const apiKey = process.env.SESHAT_API_KEY;
422
471
  let userTier = 'cartographer';
423
- let credits = 0;
424
- // Attempt to fetch account status from the cloud for tier-aware descriptions
472
+ // Fetch account status to determine which tools to expose
425
473
  if (apiKey) {
426
474
  try {
427
475
  const res = await fetch(getCloudUrl('/api/mcp/account'), {
@@ -431,39 +479,32 @@ async function main() {
431
479
  if (res.ok) {
432
480
  const account = await res.json();
433
481
  userTier = account.tier || 'cartographer';
434
- credits = account.credits || 0;
435
482
  }
436
483
  }
437
484
  catch {
438
- // If the account endpoint is unavailable, fall back to showing all tools unlabeled
485
+ // If unavailable, default to cartographer (free tier tools only)
439
486
  }
440
487
  }
441
- const annotatedTools = TOOLS.map((tool) => {
488
+ // Only return tools the user's tier can actually use
489
+ const visibleTools = TOOLS.filter((tool) => {
442
490
  const requiredTier = TOOL_TIERS[tool.name];
443
491
  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
- };
492
+ return true; // get_account_status — always visible
493
+ return tierAtLeast(userTier, requiredTier);
453
494
  });
454
- return { tools: annotatedTools };
495
+ return { tools: visibleTools };
455
496
  });
456
- // ─── CallTool handler with #1 (_meta piggyback) and #2 (get_account_status) ──
497
+ // ─── CallTool handler ──────────────────────────────────────────
457
498
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
458
499
  const { name, arguments: args } = request.params;
459
500
  const apiKey = process.env.SESHAT_API_KEY;
460
501
  if (!apiKey) {
461
502
  return {
462
- content: [{ type: 'text', text: JSON.stringify({ error: 'SESHAT_API_KEY environment variable is required to use Ptah Cloud tools.' }, null, 2) }],
503
+ content: [{ type: 'text', text: JSON.stringify({ error: 'SESHAT_API_KEY environment variable is required. Get your free key at https://ptah.papyruslabs.ai' }, null, 2) }],
463
504
  isError: true,
464
505
  };
465
506
  }
466
- // ─── #2: get_account_status tool ──────────────────────────────
507
+ // ─── get_account_status ──────────────────────────────────────
467
508
  if (name === 'get_account_status') {
468
509
  try {
469
510
  const res = await fetch(getCloudUrl('/api/mcp/account'), {
@@ -533,9 +574,18 @@ async function main() {
533
574
  };
534
575
  }
535
576
  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.
577
+ // Separate _meta into assistant-only content so it doesn't clutter
578
+ // the user-visible response. The LLM still sees it for context.
579
+ if (result._meta) {
580
+ const meta = result._meta;
581
+ delete result._meta;
582
+ return {
583
+ content: [
584
+ { type: 'text', text: JSON.stringify(result, null, 2) },
585
+ { type: 'text', text: JSON.stringify({ _meta: meta }), annotations: { audience: ['assistant'], priority: 0.2 } },
586
+ ],
587
+ };
588
+ }
539
589
  return {
540
590
  content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
541
591
  };
@@ -549,7 +599,7 @@ async function main() {
549
599
  });
550
600
  const transport = new StdioServerTransport();
551
601
  await server.connect(transport);
552
- process.stderr.write(`Seshat Cloud Proxy started. Routing requests to Ptah API...\n`);
602
+ process.stderr.write(`Seshat MCP v0.13.0 connected. Structural code analysis ready.\n`);
553
603
  }
554
604
  main().catch((err) => {
555
605
  process.stderr.write(`Fatal: ${err.message}\n`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@papyruslabsai/seshat-mcp",
3
- "version": "0.12.2",
3
+ "version": "0.13.0",
4
4
  "description": "Semantic MCP server — exposes a codebase's structure, dependencies, and constraints as queryable tools",
5
5
  "type": "module",
6
6
  "bin": {