@papyruslabsai/seshat-mcp 0.14.2 → 0.15.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.
Files changed (2) hide show
  1. package/dist/index.js +41 -180
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -34,117 +34,17 @@ Use Seshat tools instead of grep/Read when you need to understand code structure
34
34
  All tools are read-only and safe to call speculatively — there is no cost to trying them.
35
35
 
36
36
  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.`;
37
- const TIER_ORDER = ['cartographer', 'pro', 'analyst', 'architect', 'founder'];
38
- const REVELATION_ORDER = ['core', 'navigate', 'security', 'quality', 'full'];
39
- // Which tools appear at each stage (cumulative each stage includes all previous)
40
- const TOOL_REVELATION = {
41
- // Always visible
42
- get_account_status: 'core',
43
- list_projects: 'core',
44
- // Core loop — the curiosity engine (available immediately)
45
- get_entity: 'core',
46
- get_blast_radius: 'core',
47
- get_dependencies: 'core',
48
- get_optimal_context: 'core',
49
- // Navigation — after agent starts exploring (revealed after ~3 queries)
50
- query_entities: 'navigate',
51
- list_modules: 'navigate',
52
- get_topology: 'navigate',
53
- get_data_flow: 'navigate',
54
- find_by_constraint: 'navigate',
55
- // Security surface — after architecture is understood (~8 queries)
56
- get_auth_matrix: 'security',
57
- find_exposure_leaks: 'security',
58
- // Quality & audit — after deep exploration (~15 queries)
59
- find_dead_code: 'quality',
60
- find_layer_violations: 'quality',
61
- get_coupling_metrics: 'quality',
62
- find_error_gaps: 'quality',
63
- get_test_coverage: 'quality',
64
- find_runtime_violations: 'quality',
65
- find_ownership_violations: 'quality',
66
- query_traits: 'quality',
67
- find_semantic_clones: 'quality',
68
- // Architect — always gated by tier, shown when quality is visible
69
- estimate_task_cost: 'full',
70
- simulate_mutation: 'full',
71
- create_symbol: 'full',
72
- diff_bundle: 'full',
73
- conflict_matrix: 'full',
74
- query_data_targets: 'full',
75
- };
76
- // Query thresholds for each stage transition
77
- const REVELATION_THRESHOLDS = {
78
- core: 0, // Immediate
79
- navigate: 3, // After a few core loop calls
80
- security: 8, // After architecture is understood
81
- quality: 15, // After deep exploration
82
- full: 25, // Power user territory
83
- };
84
- // Session state for progressive revelation
85
- let _sessionQueryCount = 0;
86
- let _currentStage = 'core';
87
- let _serverRef = null; // Stored so we can send notifications
88
- function stageAtLeast(current, required) {
89
- return REVELATION_ORDER.indexOf(current) >= REVELATION_ORDER.indexOf(required);
90
- }
91
- function checkRevelation() {
92
- for (const stage of REVELATION_ORDER) {
93
- if (_sessionQueryCount >= REVELATION_THRESHOLDS[stage]) {
94
- if (REVELATION_ORDER.indexOf(stage) > REVELATION_ORDER.indexOf(_currentStage)) {
95
- const previousStage = _currentStage;
96
- _currentStage = stage;
97
- // Notify client that tool list has changed
98
- if (_serverRef) {
99
- _serverRef.notification({ method: 'notifications/tools/list_changed' });
100
- process.stderr.write(`[Seshat] Revelation: ${previousStage} → ${stage} (${_sessionQueryCount} queries). Notified client.\n`);
101
- }
102
- }
103
- }
104
- }
105
- }
106
- const TOOL_TIERS = {
107
- // Cartographer (free) — explore, navigate, and assess security surface
108
- list_projects: 'cartographer',
109
- query_entities: 'cartographer',
110
- get_entity: 'cartographer',
111
- get_dependencies: 'cartographer',
112
- get_data_flow: 'cartographer',
113
- find_by_constraint: 'cartographer',
114
- get_blast_radius: 'cartographer',
115
- list_modules: 'cartographer',
116
- get_topology: 'cartographer',
117
- get_optimal_context: 'cartographer',
118
- get_auth_matrix: 'cartographer',
119
- find_exposure_leaks: 'cartographer',
120
- // Analyst (tier 2) — audit and diagnose
121
- find_dead_code: 'analyst',
122
- find_layer_violations: 'analyst',
123
- get_coupling_metrics: 'analyst',
124
- find_error_gaps: 'analyst',
125
- get_test_coverage: 'analyst',
126
- find_runtime_violations: 'analyst',
127
- find_ownership_violations: 'analyst',
128
- query_traits: 'analyst',
129
- find_semantic_clones: 'analyst',
130
- // Architect (tier 3) — simulate, estimate, and act
131
- estimate_task_cost: 'architect',
132
- simulate_mutation: 'architect',
133
- create_symbol: 'architect',
134
- diff_bundle: 'architect',
135
- conflict_matrix: 'architect',
136
- query_data_targets: 'architect',
137
- };
138
- const TIER_LABELS = {
139
- cartographer: 'Cartographer (Free)',
140
- pro: 'Seshat Pro',
141
- analyst: 'Seshat Shield',
142
- architect: 'Architect',
143
- founder: 'Founder (All Access)',
144
- };
145
- function tierAtLeast(userTier, requiredTier) {
146
- return TIER_ORDER.indexOf(userTier) >= TIER_ORDER.indexOf(requiredTier);
147
- }
37
+ // ─── Private Tools (Ptah IP never exposed publicly) ────────────
38
+ // These tools are reserved for the Ptah write layer. They exist in
39
+ // the codebase but are never listed, never hinted at, never mentioned.
40
+ const PRIVATE_TOOLS = new Set([
41
+ 'estimate_task_cost',
42
+ 'simulate_mutation',
43
+ 'create_symbol',
44
+ 'diff_bundle',
45
+ 'conflict_matrix',
46
+ 'query_data_targets',
47
+ ]);
148
48
  // ─── Shared Definitions ──────────────────────────────────────────
149
49
  const projectParam = {
150
50
  type: 'string',
@@ -511,6 +411,11 @@ const TOOLS = [
511
411
  annotations: READ_ONLY_OPEN,
512
412
  },
513
413
  ];
414
+ // ─── _meta Steering Hints ────────────────────────────────────────
415
+ // After each tool call, suggest the next logical tool based on what
416
+ // the agent has been doing. This replaces progressive revelation —
417
+ // all tools are visible, but hints guide the agent toward the right
418
+ // tool at the right time.
514
419
  // ─── Project Resolution (Fallback) ────────────────────────────────
515
420
  // Cache for the last project used — so tools auto-scope after list_projects
516
421
  let _lastKnownProject;
@@ -551,46 +456,15 @@ function getCloudUrl(path) {
551
456
  async function main() {
552
457
  const server = new Server({
553
458
  name: 'seshat',
554
- version: '0.14.0',
459
+ version: '0.14.2',
555
460
  }, {
556
- capabilities: { tools: { listChanged: true } },
461
+ capabilities: { tools: {} },
557
462
  instructions: SERVER_INSTRUCTIONS,
558
463
  });
559
- // Store server ref for sending notifications from revelation system
560
- _serverRef = server;
561
- // ─── Dynamic ListTools — only expose tools the user can access ──
464
+ // ─── ListTools all public tools, Architect tools hidden (Ptah IP) ──
562
465
  server.setRequestHandler(ListToolsRequestSchema, async () => {
563
- const apiKey = process.env.SESHAT_API_KEY;
564
- let userTier = 'cartographer';
565
- // Fetch account status to determine which tools to expose
566
- if (apiKey) {
567
- try {
568
- const res = await fetch(getCloudUrl('/api/mcp/account'), {
569
- method: 'GET',
570
- headers: { 'x-api-key': apiKey },
571
- });
572
- if (res.ok) {
573
- const account = await res.json();
574
- userTier = account.tier || 'cartographer';
575
- }
576
- }
577
- catch {
578
- // If unavailable, default to cartographer (free tier tools only)
579
- }
580
- }
581
- // Filter tools by BOTH tier access AND revelation stage
582
- const visibleTools = TOOLS.filter((tool) => {
583
- // Tier gate: user must have access
584
- const requiredTier = TOOL_TIERS[tool.name];
585
- if (requiredTier && !tierAtLeast(userTier, requiredTier))
586
- return false;
587
- // Revelation gate: tool must be revealed at current stage
588
- const requiredStage = TOOL_REVELATION[tool.name];
589
- if (requiredStage && !stageAtLeast(_currentStage, requiredStage))
590
- return false;
591
- return true;
592
- });
593
- process.stderr.write(`[Seshat] ListTools: stage=${_currentStage}, queries=${_sessionQueryCount}, tools=${visibleTools.length}/${TOOLS.length}\n`);
466
+ const visibleTools = TOOLS.filter((tool) => !PRIVATE_TOOLS.has(tool.name));
467
+ process.stderr.write(`[Seshat] ListTools: ${visibleTools.length} tools (${PRIVATE_TOOLS.size} private)\n`);
594
468
  return { tools: visibleTools };
595
469
  });
596
470
  // ─── CallTool handler ──────────────────────────────────────────
@@ -603,6 +477,13 @@ async function main() {
603
477
  isError: true,
604
478
  };
605
479
  }
480
+ // Block private Ptah tools from being called
481
+ if (PRIVATE_TOOLS.has(name)) {
482
+ return {
483
+ content: [{ type: 'text', text: JSON.stringify({ error: `Unknown tool: ${name}` }, null, 2) }],
484
+ isError: true,
485
+ };
486
+ }
606
487
  // ─── get_account_status ──────────────────────────────────────
607
488
  if (name === 'get_account_status') {
608
489
  try {
@@ -618,37 +499,16 @@ async function main() {
618
499
  };
619
500
  }
620
501
  const account = await res.json();
621
- const userTier = account.tier || 'cartographer';
622
- const credits = account.credits || 0;
623
- // Build response: lead with what you CAN do, not what you can't
624
- const availableTools = [];
625
- const upgradeTeaser = {};
626
- for (const [toolName, requiredTier] of Object.entries(TOOL_TIERS)) {
627
- if (tierAtLeast(userTier, requiredTier)) {
628
- availableTools.push(toolName);
629
- }
630
- else {
631
- if (!upgradeTeaser[TIER_LABELS[requiredTier]]) {
632
- upgradeTeaser[TIER_LABELS[requiredTier]] = [];
633
- }
634
- upgradeTeaser[TIER_LABELS[requiredTier]].push(toolName);
635
- }
636
- }
502
+ // List all public tools
503
+ const publicTools = TOOLS
504
+ .filter(t => !PRIVATE_TOOLS.has(t.name))
505
+ .map(t => t.name);
637
506
  const response = {
638
- tier: userTier,
639
- tier_label: TIER_LABELS[userTier],
640
- ptah_credits: credits,
641
- your_tools: availableTools,
642
- tool_count: `${availableTools.length} tools available`,
507
+ status: 'active',
508
+ tools_available: publicTools.length,
509
+ your_tools: publicTools,
510
+ dashboard: 'https://ptah.papyruslabs.ai/dashboard',
643
511
  };
644
- // Only mention upgrades if there are locked tools, and frame positively
645
- if (Object.keys(upgradeTeaser).length > 0) {
646
- const totalLocked = Object.values(upgradeTeaser).reduce((sum, t) => sum + t.length, 0);
647
- response.upgrades_available = {
648
- 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.`,
649
- url: 'https://ptah.papyruslabs.ai/settings/billing',
650
- };
651
- }
652
512
  return {
653
513
  content: [{ type: 'text', text: JSON.stringify(response, null, 2) }],
654
514
  };
@@ -702,12 +562,10 @@ async function main() {
702
562
  if (name === 'list_projects' && result.projects && result.projects.length > 0) {
703
563
  _lastKnownProject = result.projects[0].name;
704
564
  }
705
- // Progressive revelation: count queries and check for stage transitions
706
- _sessionQueryCount++;
707
- checkRevelation();
708
565
  // Separate _meta into assistant-only content so it doesn't clutter
709
566
  // the user-visible response. The LLM still sees it for context.
710
- if (result._meta) {
567
+ // Server-side _meta now includes cross-tool recommendations.
568
+ if (result._meta && Object.keys(result._meta).length > 0) {
711
569
  const meta = result._meta;
712
570
  delete result._meta;
713
571
  return {
@@ -717,6 +575,9 @@ async function main() {
717
575
  ],
718
576
  };
719
577
  }
578
+ // Strip empty _meta
579
+ if (result._meta)
580
+ delete result._meta;
720
581
  return {
721
582
  content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
722
583
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@papyruslabsai/seshat-mcp",
3
- "version": "0.14.2",
3
+ "version": "0.15.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": {