@papyruslabsai/seshat-mcp 0.20.0 → 0.20.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Papyrus Labs
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,87 @@
1
+ # Seshat — structural code intelligence for AI agents
2
+
3
+ **Your agent reconstructs your codebase from likelihood. Seshat compiles it.**
4
+
5
+ Seshat turns a repository into a typed symbol graph (every function, class, route, and
6
+ table, with its *real* dependency edges, data flow, and constraints) and serves it to your
7
+ agent over MCP. So before your agent edits a function, it can ask what will actually break
8
+ instead of guessing.
9
+
10
+ Backed by a compiled intermediate representation, not text search or embeddings: if Seshat
11
+ says a function has three callers, it has exactly three.
12
+
13
+ ## Why
14
+
15
+ AI coding agents are fast but structurally blind. When an agent changes one function, it
16
+ often cannot see everything that depends on it, so it silently breaks callers it never
17
+ looked at. A large share of AI-introduced regressions come from exactly this. Seshat gives
18
+ the agent the map.
19
+
20
+ ## Try it first (no install, no login)
21
+
22
+ Paste any public repo at **https://seshat.papyruslabs.ai/try** and watch it trace what a
23
+ change would break.
24
+
25
+ ## Install
26
+
27
+ ```
28
+ npx -y @papyruslabsai/seshat-mcp setup <your-key>
29
+ ```
30
+
31
+ Get a free key at **https://seshat.papyruslabs.ai** (first extraction is free). The setup
32
+ command writes your MCP config and stores the key.
33
+
34
+ Or configure manually (Claude Code, Cursor, or any MCP client):
35
+
36
+ ```json
37
+ {
38
+ "mcpServers": {
39
+ "seshat": {
40
+ "command": "npx",
41
+ "args": ["-y", "@papyruslabsai/seshat-mcp"],
42
+ "env": { "SESHAT_API_KEY": "your-key" }
43
+ }
44
+ }
45
+ }
46
+ ```
47
+
48
+ ## Tools
49
+
50
+ Point your agent at a repo with `sync_project`, then it investigates the way a senior
51
+ engineer does: orient, trace, verify.
52
+
53
+ **Orient**
54
+ - `list_projects` — what is synced
55
+ - `list_modules` — how the codebase is organized, by layer or module
56
+ - `query_entities` — find functions, classes, and routes by name, layer, or module
57
+ - `find_entry_points` — routes, exports, and the public API surface
58
+
59
+ **Investigate a symbol**
60
+ - `get_entity` — signature, callers, callees, data flow, side effects, and tables touched
61
+ - `get_dependencies` — the real call chain, callers and callees
62
+ - `get_blast_radius` — everything that breaks if you change it, transitively
63
+ - `get_data_flow` — what a function reads, returns, and mutates
64
+ - `get_optimal_context` — the minimal, ranked set of files to read before editing
65
+ - `find_by_constraint` — every function that touches a given table (or carries a given trait)
66
+ - `find_dead_code` — unreachable symbols, safe to delete
67
+
68
+ Every answer comes from the compiled graph and discloses the coverage behind it.
69
+
70
+ > History and cross-cutting audit tools (hotspots, co-change clusters, lineage, topology,
71
+ > semantic clones) are being hardened and will be added to this list as they land.
72
+
73
+ ## Privacy
74
+
75
+ Analysis runs in the Papyrus Labs cloud, by design: the compiled graph is the product's
76
+ moat, and keeping extraction server-side is how that stays protected. Public repos are
77
+ cloned from GitHub; private repos require you to connect your GitHub account. Source is
78
+ processed to build the graph and cached to serve queries. See the privacy policy at
79
+ seshat.papyruslabs.ai.
80
+
81
+ ## Pricing
82
+
83
+ First extraction is free. $0.03 per query after a free tier; a typical investigation is 5
84
+ to 15 queries. Details at https://seshat.papyruslabs.ai.
85
+
86
+ MIT licensed server. Named for the goddess who kept the records, built so your agent can
87
+ read them.
package/package.json CHANGED
@@ -1,48 +1,41 @@
1
- {
2
- "name": "@papyruslabsai/seshat-mcp",
3
- "version": "0.20.0",
4
- "description": "Semantic MCP server — exposes a codebase's structure, dependencies, and constraints as queryable tools",
5
- "type": "module",
6
- "bin": {
7
- "seshat-mcp": "./dist/index.js"
8
- },
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
- },
17
- "scripts": {
18
- "build": "tsc",
19
- "dev": "tsc --watch",
20
- "start": "node dist/index.js"
21
- },
22
- "dependencies": {
23
- "@modelcontextprotocol/sdk": "^1.12.1"
24
- },
25
- "devDependencies": {
26
- "typescript": "^5.5.0",
27
- "@types/node": "^20.0.0"
28
- },
29
- "engines": {
30
- "node": ">=20"
31
- },
32
- "files": [
33
- "dist/"
34
- ],
35
- "repository": {
36
- "type": "git",
37
- "url": "https://github.com/papyruslabs-ai/seshat.git",
38
- "directory": "packages/seshat-mcp"
39
- },
40
- "keywords": [
41
- "mcp",
42
- "semantic",
43
- "code-analysis",
44
- "seshat",
45
- "static-analysis"
46
- ],
47
- "license": "MIT"
48
- }
1
+ {
2
+ "name": "@papyruslabsai/seshat-mcp",
3
+ "version": "0.20.1",
4
+ "mcpName": "ai.papyruslabs/seshat-mcp",
5
+ "description": "Structural code intelligence for AI agents: a compiled typed symbol graph over MCP.",
6
+ "type": "module",
7
+ "bin": {
8
+ "seshat-mcp": "./dist/index.js"
9
+ },
10
+ "main": "./dist/index.js",
11
+ "scripts": {
12
+ "build": "tsc",
13
+ "start": "node dist/index.js"
14
+ },
15
+ "dependencies": {
16
+ "@modelcontextprotocol/sdk": "^1.12.1"
17
+ },
18
+ "devDependencies": {
19
+ "typescript": "^5.5.0",
20
+ "@types/node": "^20.0.0"
21
+ },
22
+ "engines": {
23
+ "node": ">=20"
24
+ },
25
+ "files": [
26
+ "dist/"
27
+ ],
28
+ "repository": {
29
+ "type": "git",
30
+ "url": "https://github.com/papyruslabs-ai/seshat-mcp.git"
31
+ },
32
+ "keywords": [
33
+ "mcp",
34
+ "code-intelligence",
35
+ "structural-analysis",
36
+ "blast-radius",
37
+ "ai-agents",
38
+ "seshat"
39
+ ],
40
+ "license": "MIT"
41
+ }
@@ -1,29 +0,0 @@
1
- /**
2
- * Auto-Bootstrap — extract a .seshat/ bundle on first run.
3
- *
4
- * When the MCP server starts and finds no .seshat/_bundle.json, this module
5
- * spawns `npx @papyruslabsai/seshat-extract` to generate one on the fly.
6
- *
7
- * CI continues to regenerate the bundle on every merge to main, overwriting
8
- * the bootstrap result. The bootstrap only runs when .seshat/ doesn't exist.
9
- *
10
- * Env vars:
11
- * SESHAT_BOOTSTRAP_TIMEOUT — spawn timeout in ms (default: 120000)
12
- */
13
- export interface BootstrapResult {
14
- success: boolean;
15
- entityCount: number;
16
- languages: string[];
17
- durationMs: number;
18
- error?: string;
19
- }
20
- /**
21
- * Auto-bootstrap: extract a .seshat/ bundle for the given project directory.
22
- *
23
- * Spawns `npx @papyruslabsai/seshat-extract <dir> <dir>/.seshat <name>`
24
- * and waits for it to complete.
25
- *
26
- * @param projectDir - Absolute path to the project root
27
- * @returns Bootstrap result with entity count, languages, duration, and any error
28
- */
29
- export declare function bootstrap(projectDir: string): Promise<BootstrapResult>;
package/dist/bootstrap.js DELETED
@@ -1,190 +0,0 @@
1
- /**
2
- * Auto-Bootstrap — extract a .seshat/ bundle on first run.
3
- *
4
- * When the MCP server starts and finds no .seshat/_bundle.json, this module
5
- * spawns `npx @papyruslabsai/seshat-extract` to generate one on the fly.
6
- *
7
- * CI continues to regenerate the bundle on every merge to main, overwriting
8
- * the bootstrap result. The bootstrap only runs when .seshat/ doesn't exist.
9
- *
10
- * Env vars:
11
- * SESHAT_BOOTSTRAP_TIMEOUT — spawn timeout in ms (default: 120000)
12
- */
13
- import { spawn } from 'child_process';
14
- import fs from 'fs';
15
- import path from 'path';
16
- import { execSync } from 'child_process';
17
- // Project marker files — presence of any means "this is a code project"
18
- const PROJECT_MARKERS = [
19
- '.git',
20
- 'package.json',
21
- 'go.mod',
22
- 'Cargo.toml',
23
- 'pyproject.toml',
24
- 'pom.xml',
25
- 'build.gradle',
26
- 'Makefile',
27
- 'CMakeLists.txt',
28
- ];
29
- /**
30
- * Check if a directory looks like a code project.
31
- */
32
- function isCodeProject(dir) {
33
- for (const marker of PROJECT_MARKERS) {
34
- const markerPath = path.join(dir, marker);
35
- if (fs.existsSync(markerPath))
36
- return true;
37
- }
38
- // Also check for *.sln files (C# solutions)
39
- try {
40
- const entries = fs.readdirSync(dir);
41
- if (entries.some(e => e.endsWith('.sln')))
42
- return true;
43
- }
44
- catch { }
45
- return false;
46
- }
47
- /**
48
- * Infer a project name from the directory.
49
- * Priority: package.json name → git remote → directory basename
50
- */
51
- function inferProjectName(dir) {
52
- // 1. Try package.json
53
- try {
54
- const pkgPath = path.join(dir, 'package.json');
55
- const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
56
- if (pkg.name) {
57
- return pkg.name.replace(/^@[^/]+\//, '');
58
- }
59
- }
60
- catch { }
61
- // 2. Try git remote
62
- try {
63
- const remote = execSync('git remote get-url origin', {
64
- cwd: dir, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe']
65
- }).trim();
66
- const match = remote.match(/\/([^/]+?)(?:\.git)?$/);
67
- if (match)
68
- return match[1];
69
- }
70
- catch { }
71
- // 3. Fallback: directory basename
72
- return path.basename(dir);
73
- }
74
- /**
75
- * Auto-bootstrap: extract a .seshat/ bundle for the given project directory.
76
- *
77
- * Spawns `npx @papyruslabsai/seshat-extract <dir> <dir>/.seshat <name>`
78
- * and waits for it to complete.
79
- *
80
- * @param projectDir - Absolute path to the project root
81
- * @returns Bootstrap result with entity count, languages, duration, and any error
82
- */
83
- export async function bootstrap(projectDir) {
84
- const startTime = Date.now();
85
- const timeoutMs = parseInt(process.env.SESHAT_BOOTSTRAP_TIMEOUT || '120000', 10);
86
- // Sanity checks
87
- if (!isCodeProject(projectDir)) {
88
- return {
89
- success: false,
90
- entityCount: 0,
91
- languages: [],
92
- durationMs: Date.now() - startTime,
93
- error: `${projectDir} does not appear to be a code project (no package.json, .git, go.mod, etc.)`,
94
- };
95
- }
96
- const seshatDir = path.join(projectDir, '.seshat');
97
- const projectName = inferProjectName(projectDir);
98
- process.stderr.write(`[seshat-mcp] Auto-bootstrap: extracting ${projectName} from ${projectDir}\n`);
99
- return new Promise((resolve) => {
100
- // On Windows, npx is npx.cmd and must run through shell.
101
- // shell: true concatenates args into a command string, so paths with
102
- // spaces must be double-quoted to survive CMD parsing.
103
- const isWindows = process.platform === 'win32';
104
- const npxCmd = isWindows ? 'npx.cmd' : 'npx';
105
- const q = (s) => isWindows && s.includes(' ') ? `"${s}"` : s;
106
- const child = spawn(npxCmd, ['-y', '@papyruslabsai/seshat-extract', q(projectDir), q(seshatDir), projectName], {
107
- cwd: projectDir,
108
- shell: isWindows, // Windows needs shell: true for .cmd files
109
- stdio: ['pipe', 'pipe', 'pipe'],
110
- timeout: timeoutMs,
111
- });
112
- let stdout = '';
113
- let stderr = '';
114
- child.stdout?.on('data', (data) => {
115
- stdout += data.toString();
116
- });
117
- child.stderr?.on('data', (data) => {
118
- const text = data.toString();
119
- stderr += text;
120
- // Forward progress to parent stderr
121
- process.stderr.write(`[seshat-extract] ${text}`);
122
- });
123
- child.on('error', (err) => {
124
- resolve({
125
- success: false,
126
- entityCount: 0,
127
- languages: [],
128
- durationMs: Date.now() - startTime,
129
- error: `Failed to spawn seshat-extract: ${err.message}`,
130
- });
131
- });
132
- child.on('close', (code) => {
133
- const durationMs = Date.now() - startTime;
134
- if (code !== 0) {
135
- resolve({
136
- success: false,
137
- entityCount: 0,
138
- languages: [],
139
- durationMs,
140
- error: `seshat-extract exited with code ${code}. stderr: ${stderr.slice(-500)}`,
141
- });
142
- return;
143
- }
144
- // Parse the JSON result from stdout
145
- try {
146
- const result = JSON.parse(stdout.trim());
147
- if (result.ok) {
148
- process.stderr.write(`[seshat-mcp] Bootstrap complete: ${result.entities} entities, ${result.languages?.join(', ')} in ${(durationMs / 1000).toFixed(1)}s\n`);
149
- resolve({
150
- success: true,
151
- entityCount: result.entities || 0,
152
- languages: result.languages || [],
153
- durationMs,
154
- });
155
- }
156
- else {
157
- resolve({
158
- success: false,
159
- entityCount: 0,
160
- languages: [],
161
- durationMs,
162
- error: result.error || 'Unknown extraction error',
163
- });
164
- }
165
- }
166
- catch {
167
- // JSON parse failed — check if bundle was written anyway
168
- const bundlePath = path.join(seshatDir, '_bundle.json');
169
- if (fs.existsSync(bundlePath)) {
170
- process.stderr.write(`[seshat-mcp] Bootstrap produced bundle but JSON result was malformed. Proceeding.\n`);
171
- resolve({
172
- success: true,
173
- entityCount: 0,
174
- languages: [],
175
- durationMs,
176
- });
177
- }
178
- else {
179
- resolve({
180
- success: false,
181
- entityCount: 0,
182
- languages: [],
183
- durationMs,
184
- error: `seshat-extract succeeded but produced no parseable result. stdout: ${stdout.slice(-200)}`,
185
- });
186
- }
187
- }
188
- });
189
- });
190
- }
package/dist/graph.d.ts DELETED
@@ -1,42 +0,0 @@
1
- /**
2
- * Call Graph Construction & Blast Radius Computation
3
- *
4
- * Computes the set of entities affected by a change:
5
- * affected(e) = ancestors(e) ∪ descendants(e) ∪ {e}
6
- *
7
- * Ancestors = all transitive callers (upstream).
8
- * Descendants = all transitive callees (downstream).
9
- */
10
- import type { JstfEntity } from './types.js';
11
- export interface CallGraph {
12
- callers: Map<string, Set<string>>;
13
- callees: Map<string, Set<string>>;
14
- entityById: Map<string, JstfEntity>;
15
- /** Reference-resolution outcomes — honest sparsity as a reported number. */
16
- resolutionStats?: {
17
- resolved: number;
18
- ambiguous: number;
19
- unknown: number;
20
- };
21
- }
22
- export interface BlastRadiusResult {
23
- affected: string[];
24
- depthMap: Record<string, number>;
25
- ancestors: string[];
26
- descendants: string[];
27
- }
28
- /**
29
- * Build a bidirectional call graph from entities' dependency data.
30
- *
31
- * Reference resolution is identity-honest: a bare-name target resolves to the
32
- * unique entity with that local name, or the unique same-file match when the
33
- * name is ambiguous repo-wide. A reference that stays ambiguous produces NO
34
- * edge — first-wins matching used to silently attribute edges to whichever
35
- * same-named entity was indexed first.
36
- */
37
- export declare function buildCallGraph(entities: JstfEntity[]): CallGraph;
38
- /**
39
- * Compute blast radius: all entities affected by changes to the given IDs.
40
- * Traverses both callers (upstream) and callees (downstream).
41
- */
42
- export declare function computeBlastRadius(graph: CallGraph, changedIds: Set<string>, maxDepth?: number): BlastRadiusResult;