test-inventory-mcp
Summary: An MCP server that answers questions about a project's tests by statically reading test files across nine runners — it never runs code, writes files or opens network connections.
test_summary— counts tests by state (active, skip, fixme, todo, expectedToFail, notLoaded), tag and annotation, plus the config files read and their projects;groupBybreaks counts down per file, tag or project.find_tests— lists matching tests with chosenfields, filtered bystates,tags,grep,projects,only,withoutReason, orrelatedchanged files (tests a change can affect).get_tests— returns every key of up to 20 tests by id: location, describe path, state and where it is set, skip condition/reason, tags, comments, hooks, timeout/retries and their source, fixtures, steps, projects, dependency list and hashes; unknown ids go tonotFound.scan_diagnostics— lists scan problems bylevels,codesorpath, e.g. files that failed to parse or values that are approximate, to explain missing or partial tests.Scoping options — every scan tool accepts
directory,framework,patternsandexpandCasesto target folders, override the runner or globs, and expand.each/.for/loop cases into individual tests.Paged JSON answers — results come 50 at a time (up to 200 via
limit) withnextCursor/cursor,total,scanErrorsandhint; answers stay small and unknown arguments are refused rather than ignored.Read-only and sandboxed by convention — annotations mark it read-only, closed-world and idempotent; paths outside the project are refused, and answers should be treated as project content, not instructions.
Reads Vitest test files via test-inventory so AI agents can discover tests, count tests by state/tag/annotation, find skipped tests and reasons, retrieve test details, and inspect scan diagnostics without running the tests.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@test-inventory-mcp which tests are skipped in my Playwright project and why?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
test-inventory-mcp
An MCP server for test-inventory: AI agents ask which tests a project has, which are skipped and why, and which tests a change can affect, without running them.
It reads Playwright, Vitest, Jest, Mocha, Jasmine, Cypress, WebdriverIO, node:test and Bun test files with
test-inventory and answers from their source code. The running server executes no project code, writes no files and
opens no network connections. Installation through npx may download packages. Answers are passed to the MCP
client; its model provider and privacy settings determine what happens to that text next.
Setup
Node.js 22.12 or newer. The client starts the server with npx and talks to it over standard input and output. The
server scans the test files your test runner's config runs, with the runner --framework names or, without
it, the one the project uses. Globs pick the files instead, as on test-inventory's command line.
Claude Code, in the project folder. --scope project writes .mcp.json, for everyone who works on the project:
claude mcp add --scope project test-inventory -- npx -y test-inventory-mcp@0.9 --framework playwrightCursor, in .cursor/mcp.json:
{
"mcpServers": {
"test-inventory": {
"command": "npx",
"args": ["-y", "test-inventory-mcp@0.9", "--framework", "playwright"]
}
}
}VS Code, in .vscode/mcp.json:
{
"servers": {
"test-inventory": {
"type": "stdio",
"command": "npx",
"args": ["-y", "test-inventory-mcp@0.9", "--framework", "playwright"]
}
}
}Without --framework, as in a configuration shared by every project, each call scans with the runner the project
uses. In a project with several runners the agent passes framework, as the tools tell it, and patterns when it
wants other files than the configs run.
The project is the folder given with --root, otherwise the one Claude Code names in CLAUDE_PROJECT_DIR, otherwise
the folder the client starts the server in.
Verify the setup
Open your client's MCP server list and confirm that
test-inventoryis connected. In Claude Code use/mcp; in VS Code run MCP: List Servers from the Command Palette; in Cursor open its MCP settings.Confirm the five tools below are available, then ask: "Use test-inventory to summarize this project's tests."
Check that
test_summaryreports the expected framework and file counts. If it reportsscanErrors, ask the agent to callscan_diagnosticsbefore trusting the counts.
Try "List tests marked fixme, with their conditions and reasons" or "Which tests may be affected by a change to pages/orders-blotter.ts?" A conditional skip is not proof a test was skipped in a run. Expected-to-fail tests run; their non-active state does not mean they are disabled. See key concepts.
Related MCP server: easy-ui-mcp
Command line
test-inventory-mcp [--framework <name>] [<glob...>] [--root <dir>]Option | Meaning |
|
|
| The test files, relative to the project, in place of those the configs run. A tool call can pass others. |
| The project. Tool calls can scan folders inside it, never outside. |
| Print the help. |
| Print the version. |
Tools
The generated tool reference lists each argument's type, default and bounds, response fields, complete request/response examples, pagination and errors. It is checked against the running server in tests.
Tool | Answers |
| Counts by state, tag and annotation, and the configs with their projects; with |
| The tests that match |
| Every key of up to 20 tests by |
| The problems the scan found, such as a file that failed to parse, by |
| Changes between saved scans named by |
A typical call sequence is test_summary, find_tests, then get_tests for a few returned ids. For example, the
arguments to find_tests can be:
{
"states": ["skip", "fixme"],
"fields": ["fullName", "state", "condition", "reason"],
"limit": 10
}The answer contains matching tests, their total, scanErrors, nextCursor and hint, plus the framework and its
version. The records have selected keys and their id. Pass a non-null nextCursor as cursor with the same other
arguments to read another page; a narrow filter returning zero tests is a valid result.
related takes the files a change touches, from the project root as git diff --name-only writes them, and keeps
the tests whose dependencies include one of them, and those that reach code the scan can't follow, as
test-inventory's --related does.
The filter uses the actual scanned folder when directory names an internal link or an absolute path inside the project.
It also keeps tests that import a changed helper elsewhere in the project, outside that scanned folder.
dependencies can list hundreds of files, so it isn't among the default fields, and get_tests returns as many
tests as fit in one answer, with the other ids in remaining. An id stays the same when a test moves to another
line, and changes when its title, describes or file change.
Every scan tool also takes directory, a folder inside the project to scan, and framework and patterns, relative to
directory, to override the command line, and expandCases, which lists each case of a .each or .for table and
each pass of a loop as its own test, with the name the runner gives it, as test-inventory's
--expand-cases does. Ids differ with it, so get_tests takes
the same expandCases as the find_tests call the ids came from. Records have the keys of test-inventory's
output format.
Unknown tool arguments are refused with an error naming the unrecognized key, so misspelled filters are not silently ignored.
Answers are JSON, sent both as structured content and as text. Lists come 50 items at a time (limit takes up to
200), and an answer stays under about 30,000 characters; passing nextCursor back as cursor gets the next page.
A single oversized record is returned alone. A final page can fit more records because it needs no next-page cursor or hint.
scanErrors counts the problems that may leave tests out, and hint says what to do next when there is something to
say.
Compare saved inventories
diff_inventories takes before and after, paths to complete saved JSON inventories inside the project, plus
limit and cursor. It returns paged changes with original records and matching evidence, the full summary,
total, nextCursor, hashesComparable, warnings and hint. It does not scan or write files. Use the same
framework, root, patterns, filters and expandCases setting for both scans. Hash comparisons and rename matching
need the exact same core version. Incomplete scans produce warnings. Zero changes returns an empty page and a hint.
Each saved file must be a regular file of at most 128 MiB. Save scans with the core CLI, without --fields or
--format jsonl; see inventory diffs. If a saved file disappears or becomes unreadable while opening it, the error names the supplied file and suggests checking its existence and readability, without exposing the resolved host path. Cancellation during these operations retains the request's cancellation reason.
Troubleshooting
Symptom | Next step |
The server is disconnected or tools are missing | Open the client's server logs. Check that the client can find Node.js 22.12 or newer and |
No files or tests were found | Check |
Several runners were detected | Pass |
The answer has | Call |
A path is refused | Keep the scanned files and dependencies inside the configured project. For an intended shared dependency, start the server at the shared parent and use |
A cursor is refused | Start the query again without |
| Obtain current ids from |
A saved scan is refused | Use complete JSON inventories, not JSON Lines or |
For incorrect source analysis, use the core's troubleshooting guide. For a server bug, include the server version, client, tool arguments, expected answer and a minimal reproduction. Remove private project data before sharing it.
Security
The server reads the test files its globs match inside the project, or without globs those the configs run, and,
for Vitest, the files that symbolic links among them point to, as Vitest does. It reads the configs as code, without
running them. It refuses a directory or pattern that reaches outside the project, including parent-directory components in brace or extglob alternatives, and refuses scan answers whose test files, configs or dependencies resolve outside it. Saved inventories must resolve inside the project too. The scanner may read a linked file before the answer is refused; this boundary is not a filesystem sandbox. It runs no code from the project, writes no files, and opens no network connections.
Answers hold text from the project's files, such as test titles, skip reasons and comments. A client should treat it as content of that project, not as instructions. The client's handling of that text is separate from the server's local operation. To report a vulnerability, see SECURITY.md.
Versioning
Tool names, their arguments and their answers are the server's API. Before 1.0, a change that breaks them raises the
minor version, so test-inventory-mcp@0.9 in the setup above gets fixes only.
License
Available Tools
5 toolsdiff_inventoriesDiff inventoriesARead-onlyIdempotent
Compares two complete saved JSON scans inside the project, without running tests or changing files. Reports additions, removals, renames, moves, code and dependency changes, states, tags and other metadata. Uses ids first, then unique body hashes and file, suite or dependency context; ambiguous matches remain added and removed. Use the same framework, root, patterns, filters and expandCases for both saved scans. Hash comparisons and rename matching are disabled across tool versions. Follow nextCursor for more changes. For Playwright, Vitest, Jest, Mocha, Jasmine, Cypress, WebdriverIO, node:test and Bun inventories. Example: {"before":"inventories/base.json","after":"inventories/head.json"}.
Inputs: after (required), before (required), limit, cursor. Returns: hint, tool, total, changes, +8 more
find_testsFind testsARead-onlyIdempotent
Lists a project's tests that match filters, read from the test files without running them: each with its id, full name, file and line, state, skip reason and condition, and tags by default. Use it for questions such as which tests are skipped and why, which tests carry @smoke, where a .only was left in, or which tests a change can affect (related, the changed files); use test_summary for counts and get_tests for every key of a few ids. projects keeps the tests one of the named projects runs, after each project's grep, leaving out tests whose projects the scan cannot tell. Filters combine with AND; a list matches any of its values. fields picks other keys of the test records (id is always there). Answers are paged: follow nextCursor, or narrow the filters. Example: {"states": ["skip"], "withoutReason": true}.
Inputs: grep, only, tags, limit, +10 more. Returns: hint, tests, total, framework, +3 more
get_testsGet testsARead-onlyIdempotent
Returns every key of the tests with the given ids, with the scan diagnostics about them, read from the test files without running them: location, describe path, state and where it is set, condition, reason, tags, annotations, comments above the test, parameters, the projects that run it, hooks around it, timeout and retries with where each is set (the test, a describe, the file or the config), fixtures, expect() counts, steps, body hash, and the files the test runs with dependencyHash. Use it after find_tests or test_summary, for the details of up to 20 tests. Ids come from find_tests and stay the same when a test moves to another line, but change when its title, describe or file changes; ids not in the current scan are returned in notFound. Example: {"ids": ["3f9a0c1d2e4b5a69"]}. An answer that would be too long returns the first tests and the other ids in remaining.
Inputs: ids (required), patterns, directory, framework, +1 more. Returns: hint, tests, notFound, framework, +4 more
scan_diagnosticsScan diagnosticsARead-onlyIdempotent
Lists the problems test-inventory found while reading the test files: level error means tests may be missing from the other answers (a file that failed to parse, an import it could not follow), or that the runner fails the file or the run (a Vitest tag the config does not define, a .only that forbidOnly fails); level warning means a value is missing or approximate (a title built at run time, a config value computed at run time, a file no config runs). They are about reading the code, not test failures. Use it when another tool reports scanErrors, or to check why a test is missing or notLoaded. Answers are paged: follow nextCursor. Example: {"levels": ["error"]}.
Inputs: path, codes, limit, cursor, +5 more. Returns: hint, total, framework, nextCursor, +2 more
test_summaryTest summaryARead-onlyIdempotent
Counts a project's tests by state (active, skip, fixme, todo, expectedToFail, notLoaded) and by tag and annotation, from the test files, without running them. Use it first, for questions such as how many tests are skipped, or which files or tags have the most skipped tests; use find_tests to list the tests themselves. states, tags and grep count only the tests they match. groupBy "file", "tag" or "project" adds the counts of each, sorted by name and paged with limit and cursor; a test with no tag is in no tag group, and one whose projects the scan cannot tell in no project group. configs lists the config files read and their projects. Example: {"states": ["skip", "fixme"], "groupBy": "file"}.
Inputs: grep, tags, limit, cursor, +7 more. Returns: hint, total, groups, configs, +5 more
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 5 tools
The summary/find/get trio covers counts vs. filtered lists vs. full records, and the descriptions explicitly route the agent between them ("use test_summary for counts and get_tests for every key of a few ids"). scan_diagnostics and diff_inventories are clearly separate operations with no overlap.
All names are snake_case two-token identifiers, which is predictable and readable. The only deviation is test_summary, which leads with a noun while the other four use a verb (find_, get_, scan_, diff_), a minor inconsistency in an otherwise uniform set.
Five tools is well within a healthy range and each one covers a distinct, substantive capability (aggregate counts, filtered listing, full record retrieval, diagnostics, historical diff). No thin wrapper tools and nothing redundant.
The surface covers the read-only inventory lifecycle well: counting, listing, drilling into records, surfacing scan errors, and comparing snapshots. One workaround gap: diff_inventories requires "complete saved JSON scans" but no tool produces or exports such a snapshot, so the diff workflow depends on an out-of-band step.
Maintenance
Related MCP Connectors
Approved test intent, reviewed Playwright automation and run evidence, inside your editor.
Stateless TS/JS compiler facts for agents: references, imports, impact. No repo index or OAuth.
Checks AI-written code for leaked keys and made-up packages, on your machine. Never uploads code.
Public AI web-readiness scanner and machine-facing observability discovery service.
Related MCP Servers
- AlicenseAqualityDmaintenanceWraps existing test frameworks (Jest, Vitest, Pytest) and exposes structured, LLM-optimized results via MCP tools with progressive disclosure and diff-aware execution.111MIT
- AlicenseNot gradedqualityBmaintenanceEnables AI agents to drive Playwright-based browser automation for UI testing, returning JSON/HTML reports with screenshots without server-side LLM or test scripts.1MIT
- AlicenseAqualityBmaintenanceAllows LLM agents to query Playwright JSON test results for run summaries and failure information.2MIT
- AlicenseAqualityBmaintenanceEnables AI agents to run, debug, and inspect Playwright end-to-end tests, returning structured pass/fail stats, failure diagnostics with actionable fix hints, live DOM inspection, selector validation, visual diffs, and flakiness verdicts instead of raw test output.111,816 npmMIT