test-inventory-mcp

Discover tests, skip reasons and tests a change can affect, and compare saved inventories without running tests. Runs locally over stdio for nine JavaScript and TypeScript test runners.

Documentation

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 playwright

Cursor, 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

  1. Open your client's MCP server list and confirm that test-inventory is connected. In Claude Code use /mcp; in VS Code run MCP: List Servers from the Command Palette; in Cursor open its MCP settings.
  2. Confirm the five tools below are available, then ask: "Use test-inventory to summarize this project's tests."
  3. Check that test_summary reports the expected framework and file counts. If it reports scanErrors, ask the agent to call scan_diagnostics before 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.

Command line

test-inventory-mcp [--framework <name>] [<glob...>] [--root <dir>]
OptionMeaning
--framework <name>playwright, vitest, jest, mocha, jasmine, cypress, webdriverio, node or bun. Without it, the runner the project uses. A tool call can pass another.
<glob...>The test files, relative to the project, in place of those the configs run. A tool call can pass others.
--root <dir>The project. Tool calls can scan folders inside it, never outside.
-h, --helpPrint the help.
-v, --versionPrint 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.

ToolAnswers
test_summaryCounts by state, tag and annotation, and the configs with their projects; with groupBy, the counts of each file, tag or project.
find_testsThe tests that match states, tags, grep, related, projects, only or withoutReason, with the fields you pick.
get_testsEvery key of up to 20 tests by ids, with the scan's diagnostics about them; ids it can't find come back in notFound.
scan_diagnosticsThe problems the scan found, such as a file that failed to parse, by levels, codes or path.
diff_inventoriesChanges between saved scans named by before and after, including renames, moves and newly declared skip states.

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

SymptomNext step
The server is disconnected or tools are missingOpen the client's server logs. Check that the client can find Node.js 22.12 or newer and npx, then restart the server through the client. A client launched by a desktop app may use a different PATH from your terminal.
No files or tests were foundCheck --root, directory, the framework and the config's selected files. Try explicit patterns, such as ["tests/**/*.spec.ts"]. Call scan_diagnostics for details.
Several runners were detectedPass framework on the call, or set --framework in the server configuration. Scan each runner separately.
The answer has scanErrorsCall scan_diagnostics and resolve the errors before treating the inventory as complete.
A path is refusedKeep the scanned files and dependencies inside the configured project. For an intended shared dependency, start the server at the shared parent and use directory to select a package.
A cursor is refusedStart the query again without cursor; keep filters, directory and case expansion unchanged when requesting later pages. Files may have changed between calls.
get_tests cannot find an idObtain current ids from find_tests. Keep its directory, framework, patterns and expandCases settings.
A saved scan is refusedUse complete JSON inventories, not JSON Lines or --fields output. Keep both files inside the project and below the size limit.

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

MIT