polyglot-ast-slicer
A local call-graph context tool for AI coding agents. It parses Python and TypeScript/JavaScript with tree-sitter, builds a directed call graph, and returns byte-exact source slices over MCP. It does not execute code, makes no network calls after the first run, and keeps its index outside the repository. No code leaves your machine.
What it does
- Indexes a repository and resolves call edges from imports, receivers, annotations and constructor calls, with an explicit confidence per edge.
- Slices one symbol plus N call hops into exact byte ranges under a token budget.
- Exposes the graph through MCP and a CLI (
index,slice,blast,cycles,search,stats,impact).
Install
uvx --from polyglot-ast-slicer polyglot-ast-slicer --help
Or install the command:
uv tool install polyglot-ast-slicer
Requires Python 3.12+. The first run downloads the tree-sitter grammars, so it needs internet once; after that it runs offline. The SQLite index lives in the OS cache directory, never in the repository.
MCP configuration
serve uses --repo when given, otherwise POLYGLOT_AST_REPO, otherwise the current
working directory. A project-level Cursor config (.cursor/mcp.json) can therefore omit
the repository:
{
"mcpServers": {
"polyglot-ast-slicer": {
"command": "uvx",
"args": ["polyglot-ast-slicer", "serve"]
}
}
}
Claude Code:
claude mcp add polyglot-ast-slicer -- uvx polyglot-ast-slicer serve
Claude Desktop does not guarantee the server's working directory, so pass the repository
explicitly in claude_desktop_config.json:
{
"mcpServers": {
"polyglot-ast-slicer": {
"command": "uvx",
"args": [
"polyglot-ast-slicer", "serve",
"--repo", "/absolute/path/to/your/repo"
]
}
}
}
Worked example: Python
$ polyglot-ast-slicer slice --repo verify/fixtures/readme/python --symbol process_payment
### backend/service.py:1-7 [Ledger, Ledger.record] (data_schema, downstream_callee)
class Ledger:
def __init__(self) -> None:
self.entries: list[int] = []
def record(self, amount: int) -> int:
self.entries.append(amount)
return sum(self.entries)
### backend/service.py:10-12 [process_payment] (target)
def process_payment(amount: int, ledger: Ledger) -> int:
fee = amount // 10
return ledger.record(amount - fee)
Worked example: TypeScript
$ polyglot-ast-slicer slice --repo verify/fixtures/readme/typescript --symbol processPayment
### src/service.ts:1-8 [Ledger, Ledger.record] (data_schema, downstream_callee)
export class Ledger {
private entries: number[] = [];
record(amount: number): number {
this.entries.push(amount);
return this.entries.reduce((total, value) => total + value, 0);
}
}
### src/service.ts:10-13 [processPayment] (target)
export function processPayment(amount: number, ledger: Ledger): number {
const fee = Math.floor(amount / 10);
return ledger.record(amount - fee);
}
Results
Ground truth is Jedi for Python and the TypeScript compiler API for TS/JS. Scope is production source only (test, story and benchmark files are excluded per repository); held-out repositories were never used for tuning. Intervals are 95% Wilson score intervals. Precision counts a site as wrong when the tool reports a different project declaration; recall counts resolved sites as missed when the tool reports no edge.
| Repo | Language | Pinned SHA | Sites | Precision (95% CI) | High-conf | Recall (95% CI) | Source |
|---|---|---|---|---|---|---|---|
requests |
Python | 3c7baaa |
307 | 100.0% (95% CI 98.6%–100.0%) | 257/257 | 89.9% (95% CI 86.0%–92.8%) | main |
click |
Python | 2247b35 |
740 | 99.8% (95% CI 99.0%–100.0%) | 460/461 | 74.6% (95% CI 71.3%–77.6%) | main |
flask |
Python | 49943b5 |
348 | 100.0% (95% CI 98.2%–100.0%) | 193/193 | 60.3% (95% CI 55.1%–65.3%) | main |
werkzeug |
Python | 6604c3b |
1426 | 99.9% (95% CI 99.5%–100.0%) | 945/946 | 75.7% (95% CI 73.4%–77.9%) | held-out |
zod |
TypeScript | ca42965 |
557 | 100.0% (95% CI 99.2%–100.0%) | 417/417 | 81.1% (95% CI 77.7%–84.2%) | main |
nest |
TypeScript | 416830c |
1998 | 100.0% (95% CI 99.8%–100.0%) | 1442/1442 | 89.2% (95% CI 87.8%–90.5%) | main |
bulletproof-react |
TypeScript | 9506629 |
213 | 100.0% (95% CI 97.6%–100.0%) | 159/159 | 74.6% (95% CI 68.4%–80.0%) | main |
node-fetch |
JavaScript | 8b3320d |
89 | 100.0% (95% CI 95.6%–100.0%) | 84/84 | 94.4% (95% CI 87.5%–97.6%) | main |
express |
JavaScript | 1faf228 |
67 | 100.0% (95% CI 84.5%–100.0%) | 20/20 | 31.3% (95% CI 21.5%–43.2%) | main |
zustand |
TypeScript | 9fed2eb |
43 | 100.0% (95% CI 90.8%–100.0%) | 38/38 | 88.4% (95% CI 75.5%–94.9%) | held-out |
moment |
JavaScript | 485d9a7 |
794 | 100.0% (95% CI 99.2%–100.0%) | 507/507 | 63.9% (95% CI 60.5%–67.1%) | held-out |
| Python (all) | 2821 | 99.9% (95% CI 99.7%–100.0%) | 1855/1857 (99.9%) | 75.1% (95% CI 73.5%–76.6%) | |||
| TypeScript (all) | 2811 | 100.0% (95% CI 99.8%–100.0%) | 2056/2056 (100.0%) | 86.5% (95% CI 85.2%–87.7%) | |||
| JavaScript (all) | 950 | 100.0% (95% CI 99.4%–100.0%) | 611/611 (100.0%) | 64.4% (95% CI 61.3%–67.4%) |
Performance
Measured by verify/scale_probe.py on the development machine, one process per corpus.
Each refresh is a one-line edit to one of 30 distinct files; latency varies with file size
and disk state, so the table reports median and p95 rather than a fixed promise. Synthetic
corpora use seed 42.
| Corpus | Files | Cold ms | Warm ms | Refresh median ms | Refresh p95 ms | Slowest refresh file | Query ms | Peak RSS MB |
|---|---|---|---|---|---|---|---|---|
| synthetic-5000 | 5000 | 2063.7 | 1352.1 | 0.44 | 0.79 | pkg0012/mod001235.py (29.914 ms) |
2.0 | 107.2 |
| synthetic-20000 | 20000 | 9617.6 | 5765.1 | 0.46 | 0.57 | pkg0106/mod010611.py (1.936 ms) |
7.5 | 210.8 |
| synthetic-50000 | 50000 | 24140.5 | 14901.1 | 0.63 | 1.55 | pkg0212/mod021222.py (1.711 ms) |
17.7 | 425.0 |
| python-requests | 37 | 210.8 | 76.4 | 4.52 | 37.81 | tests/test_requests.py (254.54 ms) |
2.1 | 105.7 |
| ts-zod | 169 | 393.9 | 60.4 | 1.69 | 11.8 | deno/lib/__tests__/default.test.ts (14.059 ms) |
1.1 | 108.1 |
Slice size comparison
Default slice payload tokens (tiktoken on the exact JSON), the entry symbol's whole file, and every unique file containing its callers and callees. Thirty symbols per language, stratified as 10 per repository (seed 42). These are sizes only; they do not show that a slice is sufficient.
| Language / repo | Symbols | Payload tokens p10 / median / p90 | Entry file p10 / median / p90 | Caller+callee files p10 / median / p90 |
|---|---|---|---|---|
| python (all) | 30 | 259 / 522 / 5422 | 1254 / 4906 / 14296 | 1254 / 5430 / 31961 |
python / requests |
10 | 238 / 1990 / 5422 | 1025 / 3876 / 5953 | 1025 / 4906 / 10580 |
python / click |
10 | 264 / 643 / 2613 | 2060 / 4729 / 33701 | 2060 / 5284 / 33701 |
python / flask |
10 | 298 / 522 / 2406 | 1633 / 6154 / 8430 | 1633 / 7900 / 13256 |
| typescript (all) | 30 | 256 / 2323 / 5506 | 182 / 1032 / 39169 | 539 / 2404 / 39169 |
typescript / zod |
10 | 1284 / 3725 / 5508 | 39169 / 39169 / 39169 | 39169 / 39169 / 39169 |
typescript / nest |
10 | 256 / 672 / 2597 | 153 / 929 / 1447 | 832 / 1369 / 4591 |
typescript / bulletproof-react |
10 | 269 / 2624 / 3749 | 182 / 418 / 918 | 198 / 1834 / 2765 |
Files above 20k tokens among the sampled entry files: src/click/core.py (33,701 tokens, python), src/types.ts (39,169 tokens, typescript).
Languages
Generated from the parser extension map.
| Extension | Language | Tree-sitter grammar |
|---|---|---|
.cjs |
javascript | javascript |
.cts |
typescript | typescript |
.js |
javascript | javascript |
.jsx |
tsx | tsx |
.mjs |
javascript | javascript |
.mts |
typescript | typescript |
.py |
python | python |
.pyi |
python | python |
.ts |
typescript | typescript |
.tsx |
tsx | tsx |
What it does not do
- Dynamic dispatch and reflection.
getattr(obj, name)(),obj[name](), dependency injection, and*argsforwarding produce no edge. - Monkeypatching and runtime rewriting. Decorators, metaclasses and prototype mutation are not interpreted.
- Callbacks and higher-order dataflow. A function passed as a value is not followed when it is invoked through an unrelated parameter.
- Same-file siblings. A bare call is resolved only when the binding is unambiguous; computed member calls and unresolved receivers are left without an edge.
- Cross-language edges. Python calling into TypeScript (or the reverse) is not linked.
- Test selection.
impact_of_diffreturns changed and impacted symbols; it does not choose tests. - Express-style prototype patterns. Assigned function bodies are indexed, but prototype dispatch and dynamic property lookup are not resolved.
When another tool fits better
If you need type-aware refactoring, rename, or exhaustive reference search, use a language server such as pyright or tsserver, or a code index such as SCIP or Sourcegraph. If you need retrieval by meaning rather than by call edges, use an embedding or vector-search tool. This tool is for exact call-context retrieval.
Development
uv sync --all-extras
uv run pytest # 107 tests collected
uv run python scripts/generate_metrics.py --check
uv run ruff check .
uv run mypy polyglot_ast_slicer
Measured numbers live in METRICS.md, generated from the committed scripts and
pinned repositories. CI runs the offline --check and fails if METRICS.md or README.md drifts.
License
MIT — see LICENSE.
Metadata
Release files for polyglot-ast-slicer 1.0.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| polyglot_ast_slicer-1.0.1.tar.gz | 625.4 kB | Without provenance | Details for polyglot_ast_slicer-1.0.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| polyglot_ast_slicer-1.0.1-py3-none-any.whl | Python 3 | none | any | Details for polyglot_ast_slicer-1.0.1-py3-none-any.whl |
Total release size: 673.5 kB
Release files / polyglot_ast_slicer-1.0.1.tar.gz
| Download URL | polyglot_ast_slicer-1.0.1.tar.gz |
|---|---|
| Size | 625.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1d6ad5219ebcb1edfaa59b40a6ce8c508d262caccdb8b41d1351e9a415eae73c
|
|
BLAKE2b-256 checksum How to use checksums |
1e239d0d23116225cde8a8a3c9fccc8ac4d09488b3cc3ba4fbe6548029d64fe2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.16 {"installer":{"name":"uv","version":"0.11.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / polyglot_ast_slicer-1.0.1-py3-none-any.whl
| Download URL | polyglot_ast_slicer-1.0.1-py3-none-any.whl |
|---|---|
| Size | 48.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b9d6c5103df118bf2bec943fb7f81b1ef19ce147497577d67aefb9d56b22293a
|
|
BLAKE2b-256 checksum How to use checksums |
2a5cb85a9cbec58f9e767f8e9b484b46335d5d66577f7881f7b7c01ac82b4c78
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.16 {"installer":{"name":"uv","version":"0.11.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Security for polyglot-ast-slicer 1.0.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.