Skip to main content
Glama

ripple

Know what breaks before you change it. ripple is an MCP server that gives a coding agent (Claude Code, Cursor, anything that speaks MCP) the call graph of a Python repository, so the agent can ask "who calls this, and which tests reach it?" before it edits, and run just those tests after.

Why

A coding agent dropped into unfamiliar code edits blind. It greps for a function name, sees a few hits, and changes the signature. The callers that reach it through an import alias, a self. call in a subclass or a re-export in a package __init__ don't match the grep, and the first sign of trouble is a failing test, or a failure in production. ripple answers the structural question directly and says plainly where static analysis runs out.

Related MCP server: repo-atlas-mcp

Quickstart

Requires Python 3.10+ and uv.

uv tool install git+https://github.com/xtelos/ripple   # puts `ripple` on your PATH

Claude Code, from your project directory:

claude mcp add ripple -- ripple serve --repo "$PWD"

Cursor, in .cursor/mcp.json:

{
  "mcpServers": {
    "ripple": { "command": "ripple", "args": ["serve", "--repo", "${workspaceFolder}"] }
  }
}

Or from a terminal: ripple index ., then ripple impact price_order.

The five tools

Tool

What it answers

Read-only

callers(symbol)

Who calls this directly, with the file:line of each call site

yes

callees(symbol)

What this calls, plus the calls ripple could not resolve and why

yes

impact(symbol, depth=5, limit=100)

Every transitive caller grouped by file, the tests that reach it listed separately as pytest node ids, and same-named call sites ripple could not rule out

yes

path(source, target)

The shortest call chain from one symbol to another

yes

check(tests=None)

Runs the test command (default python -m pytest -q, using the repo's .venv if present), or just the given tests, and returns pass/fail, the summary line and the first failures, never the full log

no, it runs code

A symbol can be a full dotted path (shop.pricing.price_order), a suffix (OrderService.place_order) or a bare name (price_order). An ambiguous name returns the candidates rather than picking one.

The four graph tools advertise readOnlyHint: true, which lets a client run them without prompting. check is marked destructive instead, because a test suite can do anything its tests do.

impact lists at most limit callers and limit tests (100 by default, up to 1000 over MCP). When it found more, it says so: truncated is true and omitted_callers / omitted_tests give the counts (the CLI prints "... and N more callers not listed"). Each test comes with its pytest node id (tests/test_x.py::test_y, or tests/test_x.py::TestZ::test_y for a method), found by pytest's default collection rules: test* functions at module level in test_*.py and *_test.py files, test* methods of Test* classes that have no __init__, and every test* method of a unittest.TestCase subclass whatever its name. A class runs the test methods it inherits, so a method defined on a base is listed once for each class that runs it, under that class's node id (TestChild::test_shared). Pass those node ids to check to run just those tests. Helpers in test files that are not tests themselves are listed with the other callers, since they may need updating.

check(tests=[...]) accepts only test file paths and node ids inside the repository. Anything that starts with - or @ (pytest reads options from an @file) or points outside the repo is refused, so a caller cannot slip in a pytest option (--basetemp=DIR deletes DIR). Output is read from a temporary file and the timeout covers the whole run, so a test that leaves a process running in the background cannot hold up the result. The summary and each failure or output line check returns are cut to 240 characters, ending in [truncated N chars] when something was cut.

Example, trimmed:

$ ripple impact tax_for --repo bench/fixtures/shop
impact of shop.tax.tax_for (depth 5): 6 callers
  shop/pricing.py
    shop.pricing.price_order  line 8  (1 hop)
  shop/service.py
    shop.service.OrderService.place_order  line 17  (2 hops)
    shop.service.OrderService.reprice  line 26  (2 hops)
tests that reach it (3):
  tests/test_pricing.py::test_percent_discount_and_tax
  ...

$ ripple check tests/test_pricing.py::test_percent_discount_and_tax --repo bench/fixtures/shop
{
  "passed": true,
  "summary": "1 passed in 0.01s",
  ...

How it works

Two passes over the standard library ast module, no other parser.

  1. Collect (ripple/indexer.py): each file is read on its own. For every module, class and function it records what names are bound (defs, imports, assignments and what they suggest about a value's class) and every call site.

  2. Resolve (ripple/resolver.py): with the whole repository in view, each call site either becomes an edge, because the source proves its target, or is kept as unresolved with a reason. ripple never guesses. "There is only one method named save, so it must be that one" is exactly the kind of answer that looks right and is wrong in the cases an agent cannot check.

What counts as proof: names defined or imported in scope; import x, from x import y, aliases, relative imports and package re-exports; self.method() through base classes; super(); calling a class (edge to its __init__); and method calls on values whose class is known from a constructor call, a type annotation, a return annotation or a self.attr assigned in the class. When a method is called on self or a typed value, subclass overrides are linked too (class hierarchy analysis).

The index is cached outside your repo (RIPPLE_CACHE_DIR if set, else $XDG_CACHE_HOME/ripple or ~/.cache/ripple) and keyed on every file's mtime and size, so each query re-checks the files and rebuilds only when something changed. On a 105-file package (pydantic) a cold index took 0.8 s and a cached load 0.02 s on my machine.

Benchmark

python -m bench scores ripple against hand-written ground truth: 39 caller, callee and impact queries over three small fixture apps in bench/fixtures (an order and pricing service, a notes CLI, a web handler layer). CI fails if any number drops below bench/baseline.json.

Case

Queries

Exact

Precision

Recall

Imports, aliases, re-exports

11

11

1.000

1.000

Methods, super(), constructors

6

6

1.000

1.000

Typed receivers

5

5

1.000

1.000

Callees

3

2

1.000

0.750

Decorators

4

2

0.667

0.500

Dynamic (getattr, dispatch tables, untyped values)

6

0

1.000

0.125

Impact (transitive)

4

1

1.000

0.459

Overall

39

27

0.985

0.670

How to read this honestly:

  • The truth was written before the code. The fixtures and every expected answer were committed before any indexer code existed (see the first commit), and the dynamic cases were included on purpose to show where static analysis stops.

  • The truth is checked against runtime. bench/trace.py runs each fixture's own test suite under a profiler, records the calls that actually happen, and tests/test_ground_truth.py fails if any expected answer disagrees with that trace. The tracer shares no code with ripple.

  • It is small. 39 queries over three fixtures I wrote, each query aimed at one resolution rule or one known limit. It shows which rules work, not what recall looks like on your codebase, where more of the code is likely to be untyped and dynamic.

  • The one false positive is a design choice. ripple treats a decorated function as called directly, so it reports a test that calls hello() as a caller of hello. At runtime the test calls the decorator's wrapper, which calls hello.

Agent eval

The benchmark checks that ripple's answers are right. The eval checks whether a coding agent does better with them: ten refactoring tasks, each run by Claude Code in headless mode, once with a ripple MCP server and once without, three times each.

  • The tasks (eval/tasks/) are renames and signature changes where grep is noisy or incomplete: a method named total on four classes, a function reached only through a package re-export alias, an override in another package, a getattr lookup that neither grep nor ripple can see. Seven run over eval/fixtures/ledger, a small invoicing app written for the eval, and three reuse the benchmark fixtures. Each task's why field says what makes it hard.

  • Grading. An edit counts as a success when the fixture's own tests pass and still number at least the task's minimum (min_tests), and a hidden test the agent never sees passes too. The hidden test checks that the change was made and that every caller still works. tests/test_eval.py shows each check fails on the untouched fixture and passes on a reference solution, and CI runs it.

  • Same conditions. Both modes get the same prompt, model, tools and permissions (edit files, run pytest). The MCP server is the only difference. Each run starts from a fresh copy under /tmp, in a directory whose name says nothing about the task or mode, and leaves out the user's own settings, hooks and auto-memory. A with-ripple run whose ripple server fails to connect is marked invalid and left out of the numbers.

.venv/bin/python -m eval.run                   # 10 tasks x 2 modes x 3 repeats, into eval/results/
.venv/bin/python -m eval.run --tasks 04-ledger-rename-invoice-total --repeats 1 --out /tmp/smoke.json

Each batch writes eval/results/<date>-<time>-<id>.json (unless --out says otherwise), a .md table and a -diffs/ directory beside it, and refuses to overwrite an earlier batch's files.

It calls the claude CLI on whatever account it is logged in to, so it never runs in CI.

Results

First full run, 2026-09-26, Claude Sonnet 5, 60 runs (eval/results/2026-09-26.json):

Mode

Success

Median input tokens

Median output tokens

Median turns

Median wall time

with ripple

30/30

145,461

3,372

19.5

25.2 s

without

30/30

129,108

3,211

15.0

22.9 s

ripple did not help on these tasks. Both modes solved every task every time, so there is no difference in success to measure, and with ripple the agent spent about 13% more input tokens and four or five more turns, mostly on loading and calling the tools (78 ripple calls across the 30 runs).

The likely reason is the tasks, not the tool: the fixtures are a few hundred lines, small enough that the agent can grep and read everything it needs, which puts both modes at the ceiling. The cases where a call graph should matter (a large unfamiliar codebase, callers the agent never opens) are exactly the ones this first task set does not have. The next version of the eval needs tasks over bigger, real repositories, where the without-ripple agent has a real chance to miss a caller.

In this run each arm's working directory name included its condition label (such as --with-ripple--), which the agent can see; that is fixed now, and the result is unlikely to depend on it since both arms scored 30/30.

Known limits

Static analysis of a dynamic language misses things. ripple does not follow:

  • method calls on values of unknown type: unannotated parameters, loop variables, results of functions without a return annotation (reported as unresolved, with the reason)

  • getattr, dict or registry dispatch, callbacks passed as arguments, functools.partial

  • decorator wrappers (the decorated function is treated as called directly)

  • calls on the result of a call (make().run()), property access, __call__, metaclasses, monkeypatching, importlib, exec

  • code imported under a name that differs from its path in the repo (modules are named by their path from the repo root, with a leading src/ dropped). Two files that get the same name (tests/test_x.py and src/tests/test_x.py) share one set of symbol ids, so a name defined in both is one symbol; ripple index warns when that happens and names the files.

  • directories that hold dependencies or build output: hidden directories, __pycache__, node_modules, site-packages, venv and any directory with a pyvenv.cfg anywhere, and build, dist and env at the repo root only. ripple index lists the skipped directories, except hidden ones and __pycache__.

  • custom pytest collection settings (python_files, python_functions, python_classes): test node ids follow pytest's defaults. ripple sees only the repo's own classes, so a class counts as a unittest.TestCase subclass when it has a base from outside the repo whose name ends in TestCase (unittest.TestCase, django.test.TestCase), and an __init__ inherited from outside the repo is not seen.

Override edges can over-approximate: a subclass override is linked even if that subclass never reaches the call site. Method lookup walks bases depth-first, which matches Python's C3 order except in diamond-shaped hierarchies.

To keep the blind spots visible, callees lists every unresolved call with its reason, and impact lists unresolved call sites that use the same name as the target under possible_missed_callers. A call counts as "code outside the repo" only when it must be: a method missing from a class's repo bases is inherited from an external base such as Exception, unless a base could not be resolved (class Cached(make_base())) or a subclass defines the method, and then it is unresolved too. Subscripted bases such as Repo[int] or Generic[T] resolve to the class they subscript.

Next

  • TypeScript, the other language agents spend most of their time in.

Development

uv venv && uv pip install -e ".[dev]"
.venv/bin/python -m pytest -q      # unit tests, plus the ground truth against its runtime trace
.venv/bin/python -m bench          # the benchmark; --write-baseline after a deliberate change

MIT licensed. Copyright (c) 2026 Dylan Fodor.

Available Tools

5 tools
calleesA
Read-onlyIdempotent

List what a function or method calls: resolved callees with call sites, plus the calls ripple could not resolve statically and why.

NameRequiredDescriptionDefault
symbolYes

TDQS

A3.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, idempotentHint and openWorldHint=false, so the safety profile is covered. The description adds genuine context beyond that by disclosing that results include call sites and that unresolved calls are returned with reasons, which shapes how an agent interprets the output. It says nothing about result volume or pagination.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single sentence with zero waste, front-loading the core action and then the secondary payoff of unresolved-call diagnostics.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With one parameter and full annotation coverage the tool is simple enough that the description is nearly sufficient, and it does explain what comes back despite having no output schema. The unresolved gap is symbol formatting and whether results are recursive or single-level, which an agent needs before calling.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Only one parameter exists and schema description coverage is 0%, so the schema gives no help. The description partially compensates by establishing that the symbol is a function or method, but it never states the expected format (bare name vs qualified 'Class.method') which matters greatly for a code-analysis lookup.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource: list what a function or method calls, returning resolved callees with call sites plus unresolved calls. That directionality makes it distinguishable from the sibling 'callers' without opening a schema, though no sibling is named explicitly.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is implied rather than stated: the outgoing-call framing tells an agent this is the inverse of 'callers', so the direction choice is inferable. There is no explicit when-to-use, when-not-to-use, or named alternative such as 'callers' or 'impact'.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

callersA
Read-onlyIdempotent

List the functions and methods that directly call a symbol, with the file:line of each call site. symbol: dotted path, Class.method suffix, or bare name.

NameRequiredDescriptionDefault
symbolYes

TDQS

A3.9/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, idempotentHint, and closed-world scope, so safety needs no restating. The description adds real behavioral context by disclosing the return content (file:line of each call site) and the scope limit ('directly', not transitive), which is not in any structured field.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, no filler, with the core action and return shape front-loaded and the parameter guidance following. Every clause earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a one-parameter read-only tool with no output schema, the description covers the action, the accepted symbol syntax, and the returned data shape. It stops short of clarifying how nested or indirect calls are excluded, but nothing essential to a correct invocation is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0% and the sole parameter has no schema-level description, so the description must compensate — and it does, spelling out accepted forms: dotted path, Class.method suffix, or bare name. That is meaningful semantics beyond the bare 'string' type.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (list) and resource (functions/methods that directly call a symbol) plus what is returned (file:line per call site). The word 'directly' implicitly separates it from transitive-analysis siblings like impact, but no sibling is named explicitly.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is only implied: an agent can infer this is the incoming-reference query versus callees (outgoing), but the description never states when to choose this over callees, impact, or path. No exclusions or prerequisites are given.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

checkA
Destructive

Run the repository's test command (default: python -m pytest -q) and return pass or fail, the summary line and the first failing tests, never the full log. tests: optional pytest node ids or test paths inside the repo to run instead of the whole suite, e.g. the node_id of each test impact listed. Options (anything starting with '-') are refused.

NameRequiredDescriptionDefault
testsNo

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare that this is not read-only and is destructive, so the description does not need to repeat that safety profile. It adds useful behavioral detail beyond annotations: the default command, that output is limited to pass/fail plus summary and first failures, never the full log, and that options starting with '-' are refused. It still does not explain possible test side effects, but the major behavioral contract is present.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the primary action and output contract, then explains the only parameter. Every sentence adds necessary information: default command, return shape, subset behavior, and option restriction.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With one optional parameter, no output schema, and annotations covering safety, the description is complete enough to invoke the tool correctly. It explains the default command, how to run a subset, what is returned, and what input is refused.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must carry parameter meaning. It does this well: `tests` is optional, accepts pytest node ids or test paths inside the repo, and runs those instead of the whole suite, with an example linking to impact-listed tests. It also clarifies that CLI-style options are refused.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource: run the repository's test command. It clearly distinguishes this tool from the static-analysis siblings (callers, callees, impact, path) by specifying test execution and pass/fail reporting.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It explains the optional `tests` argument as a way to run a subset instead of the full suite, and gives the concrete use case of passing node ids from `impact`. It does not explicitly state when not to use the tool or compare it against all alternatives, but the context is clear.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

impactA
Read-onlyIdempotent

Answer 'what breaks if I change this?': every transitive caller of a symbol up to depth hops, grouped by file, with the tests that reach it listed separately (each with the pytest node_id to pass to check). At most limit callers and limit tests are listed; truncated and omitted_callers / omitted_tests say how many more were found. Also lists unresolved call sites with the same name, which static analysis cannot rule out.

NameRequiredDescriptionDefault
depthNo
limitNo
symbolYes

TDQS

A4.3/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the readOnlyHint/openWorldHint/idempotent annotations, it discloses substantial behavior: transitive traversal, grouping by file, tests listed separately, limit/truncation semantics with omitted_callers/omitted_tests counts, and unresolved call sites static analysis cannot rule out.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the motivating question, then dense but informative clauses. The single run-on sentence is slightly long, but nearly every clause carries operational meaning.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, and the description covers the return shape thoroughly: grouped-by-file callers, separate test list with node_ids, truncation fields, and unresolved call site listings. An agent knows what it will get back.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 0% schema coverage the description must compensate, and it does for all three: 'depth hops', 'at most limit callers and limit tests', and the symbol being queried. It stops short of format guidance for symbol (e.g., qualified name) and valid ranges/defaults.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific question ('what breaks if I change this?'), resource (transitive callers of a symbol), and scope (up to depth hops), plus tests reached. 'Transitive' implicitly separates it from the direct-callers sibling, but it doesn't name 'callers' as the alternative for shallow queries.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The 'what breaks if I change this?' framing gives clear context for when to reach for it, and it points at 'check' via the pytest node_id follow-up. No explicit exclusions or 'use X instead for Y' guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

pathA
Read-onlyIdempotent

Find the shortest call chain from one symbol to another, e.g. from a test or entry point to the function you are changing. Returns each hop with the file:line it is called from.

NameRequiredDescriptionDefault
sourceYes
targetYes

TDQS

A3.5/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, idempotentHint and openWorldHint=false, so the safety profile is covered. The description adds return-shape context ('each hop with the file:line it is called from'), which is genuine value, but it omits failure behavior when no chain exists and whether traversal follows calls or references — gaps annotations do not fill.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, zero filler: the operation and its scope come first, the return format second. Every clause carries information an agent needs.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Read-only, two-parameter query with no output schema, so the description bears moderate load. It explains the result shape but not the no-path case, symbol format, or which direction the chain is walked — the last being essential for interpreting the result correctly among graph siblings.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the schema contributes only the names 'source' and 'target'. The description compensates partially by framing them as symbols at the ends of a call chain, but leaves symbol syntax (fully-qualified name vs file:line) and directionality (which end is traversed from) unspecified.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource — 'find the shortest call chain from one symbol to another' — which is meaningfully distinct from the one-directional siblings (callers/callees) and the blast-radius-oriented 'impact'. It stops short of naming those siblings explicitly, so the differentiation is inferential rather than stated.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The example 'from a test or entry point to the function you are changing' supplies a concrete usage scenario, which is real guidance. However, it never states when to prefer callers/callees/impact instead, nor any precondition (e.g. symbols must already exist in the index).

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 5 tool updatesv0.1.0
    • First observedcallees
    • First observedcallers
    • First observedcheck
    • First observedimpact
    • First observedpath

TDQS

A4/5.0

Scored across 5 tools

Disambiguation5/5

Each tool answers a distinct query: direct callers, direct callees, transitive impact, shortest call chain, and test execution. The only near-overlap is callers vs. impact, but the descriptions explicitly distinguish direct vs. depth-limited transitive results, and direction (callers vs. callees) is spelled out.

Naming Consistency4/5

All five names are short, single-token lowercase words with no mixed casing or prefix noise, which reads as a deliberate convention. Minor deviation: callers/callees/path/impact are nouns while check is a verb, but the set is still highly predictable.

Tool Count5/5

Five tools is tightly scoped for a call-graph analysis server, and each maps to a real workflow step (navigate down, navigate up, assess blast radius, trace a route, verify with tests). Nothing feels redundant or padded.

Completeness4/5

The forward/reverse/path/verify lifecycle is covered, including unresolved call sites and test node_ids, which is unusually thorough. The main gap is symbol discovery: there is no search, list_symbols, or definition tool, so an agent must already know the exact symbol path or rely on external grep.

Maintenance

ActivitySlowing
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    A
    maintenance
    Provides a dependency graph of any local repository with tools for change impact, transitive dependents, health audits, and more, enabling AI coding agents to see structure and refactor safely.
    4,912 npm
    4
    MIT
  • A
    license
    A
    quality
    D
    maintenance
    Provides a graph-based code analysis to coding agents, enabling tools to trace call dependencies, assess change impact, and identify hotspots using tree-sitter and Neo4j.
    10
    3 npm
    MIT
  • A
    license
    B
    quality
    B
    maintenance
    Enables AI agents to index local Python repositories and retrieve code intelligence through semantic search, caller lookups, hybrid context expansion, and visualization.
    5
    MIT
  • F
    license
    Not graded
    quality
    C
    maintenance
    Enables coding agents to query a local-first code intelligence graph of Python repositories—covering functions, classes, modules, and their relationships—via MCP, supporting subgraph retrieval, caller lookup, and impact analysis without re-reading the codebase.
    -