mcp_probe 0.4.0 copy "mcp_probe: ^0.4.0" to clipboard
mcp_probe: ^0.4.0 copied to clipboard

Conformance checks and a test harness for Model Context Protocol (MCP) servers. Runs any MCP server over stdio and asserts on its behavior from package:test. Built on dart_mcp.

mcp_probe #

mcp_probe banner

Test harness and conformance checks for MCP servers, built on the official dart_mcp client.

There are plenty of packages for writing MCP servers. This one is for testing them. It runs any MCP server you can start as a command, over stdio, and lets you assert on its behavior from package:test:

  • McpServerHarness starts the server as a child process, performs the MCP initialize handshake, and exposes the tool, resource, and prompt APIs with a per-request timeout and guaranteed process cleanup.
  • checkServer runs a fixed set of conformance rules against a server command and returns a ConformanceReport with error, warning, and info findings, plus a toMarkdown() renderer.
  • expectToolExists, expectToolCallSucceeds, expectToolCallFails, and expectResourceExists are ready-made expectation helpers, exported from package:mcp_probe/testing.dart.

The server under test does not have to be written in Dart. Anything that speaks MCP over stdio works: a Dart script, npx -y some-server, a Python script, a compiled binary. On Windows, launch batch-file based tools such as npx by their full script name (npx.cmd), since Process.start does not resolve .cmd files by their bare name.

Diagram: the harness drives an MCP server over stdio and turns each reply into a ConformanceReport

Command line #

To check a server without writing any Dart, install the CLI and point it at the command that launches your server:

dart pub global activate mcp_probe
mcp_probe check dart run my_server.dart
mcp_probe check node build/server.js --flag

It runs the server over stdio, completes the handshake, prints each finding, and exits non-zero if any check reports an error, so it drops straight into a CI step.

Two flags make it a real gate. --format json prints the report as JSON, with a summary count per severity and the full findings list, for a pipeline or a dashboard that wants structured output. --fail-on warning (or info) lowers the bar for a non-zero exit, so a build fails on a warning, not only an error.

# Fail the build on any warning, and capture the machine-readable report.
mcp_probe check --fail-on warning --format json dart run my_server.dart > report.json
{
  "command": "dart run my_server.dart",
  "serverName": "my-server",
  "summary": { "error": 0, "warning": 1, "info": 12 },
  "findings": [
    { "severity": "info", "rule": "initialize/handshake", "message": "server answered the initialize request" }
  ]
}

Using the harness in tests #

Add mcp_probe as a dev dependency next to test:

dart pub add dev:mcp_probe dev:test

Then start your server once per suite and assert on it:

import 'package:mcp_probe/mcp_probe.dart';
import 'package:mcp_probe/testing.dart';
import 'package:test/test.dart';

void main() {
  late McpServerHarness harness;

  setUpAll(() async {
    harness = await McpServerHarness.start(
      'dart',
      args: ['run', 'bin/my_server.dart'],
    );
  });

  tearDownAll(() => harness.shutdown());

  test('exposes the search tool', () async {
    await expectToolExists(harness, 'search');
  });

  test('search returns results', () async {
    final result = await expectToolCallSucceeds(
      harness,
      'search',
      arguments: {'query': 'dart'},
    );
    expect(result.content, isNotEmpty);
  });
}

Every request made through the harness is bounded by the timeout given to start, so a server that stops answering fails the test with a TimeoutException instead of hanging the suite. shutdown closes the connection and then makes sure the process is dead, escalating to SIGTERM and SIGKILL if the server does not exit on its own.

The harness wraps the common APIs. For anything else (resource subscriptions, progress notifications, completions), the underlying dart_mcp ServerConnection is available as harness.connection.

The expectation helpers live in the separate package:mcp_probe/testing.dart entrypoint because they depend on package:test. The harness and the conformance checks do not use it, though the package still lists test as a dependency for that entrypoint.

Conformance checks #

checkServer starts the server, runs every rule, shuts the server down, and reports what it found:

test('server passes the MCP conformance checks', () async {
  final report = await checkServer('dart', args: ['run', 'bin/my_server.dart']);
  expect(report.errors, isEmpty, reason: report.toMarkdown());
});

A server that fails the handshake still produces a report instead of throwing, so batch runs over several servers do not need error handling per server.

Rules #

Rule On failure What it checks
initialize/handshake error The server answers the initialize request within the timeout.
initialize/protocol-version error The response carries a recognized MCP protocol version.
initialize/server-info-name error serverInfo.name is a non-empty string.
initialize/server-info-version error serverInfo.version is a non-empty string.
capabilities/tools-listable error A server that declares the tools capability answers tools/list.
capabilities/tools-nonempty warning A server that declares tools lists at least one tool.
tools/name error Every listed tool has a non-empty name.
tools/input-schema error Every listed tool has an inputSchema whose root is "type": "object".
tools/call-smoke error Optional, see below. Each tool answers a call at the protocol level.
capabilities/resources-listable error A server that declares resources answers resources/list.
capabilities/prompts-listable error A server that declares prompts answers prompts/list.
utilities/ping error The server answers a ping request promptly with an empty result.
jsonrpc/method-not-found error or warning An unknown method is answered with JSON-RPC error -32601.
stdio/clean-stdout error The server writes nothing but protocol messages to stdout.

Passing checks are recorded as info findings, so the report shows what was covered rather than only what failed.

About callTools #

The checks are read-only by default: lists are fetched, nothing is invoked. With callTools: true, checkServer additionally calls every listed tool once with empty arguments. Be aware of what that means: a smoke call is a real call, and whatever side effects the tool has will run. Only enable it for servers whose tools are safe to invoke blindly, or point it at a sandboxed instance. A tool that rejects the empty arguments with an in-band error (isError: true) or with JSON-RPC error -32602 (invalid params) still passes the rule, since the specification allows both; other protocol-level failures are errors.

Relationship to dart_mcp #

This package is a thin layer over the official dart_mcp client and does not reimplement any of the protocol. Requests, responses, and capability types in the public API are dart_mcp types. This release builds on dart_mcp 0.5.x, which recognizes protocol versions 2024-11-05 through 2025-11-25; servers negotiating anything else fail initialize/protocol-version.

Limits #

  • stdio is the only supported transport in this release.
  • The conformance rule set is deliberately small and covers the handshake and the declared-capability surface, not the full specification.
  • Stdout noise detection is line-based: any non-empty stdout line that does not start with { counts as noise for stdio/clean-stdout and is filtered out before it reaches the protocol parser.
0
likes
160
points
197
downloads
screenshot

Documentation

API reference

Publisher

verified publisherdeveloperyusuf.com

Weekly Downloads

Conformance checks and a test harness for Model Context Protocol (MCP) servers. Runs any MCP server over stdio and asserts on its behavior from package:test. Built on dart_mcp.

Repository (GitHub)
View/report issues

Topics

#mcp #testing #ai #server #conformance

License

MIT (license)

Dependencies

dart_mcp, json_rpc_2, test

More

Packages that depend on mcp_probe