mcptest docs GitHub

URL targets

mcptest accepts two target shapes for an MCP server: a stdio subprocess (command: [...]) or a running HTTP endpoint (url: ...). URL targets cover the deployed-server case, so they are first-class everywhere the docs talk about a "server."

This page collects six worked examples plus the supporting flags so you can pick a recipe and copy it into your mcptest.yml (or scaffold one with mcptest init --url <BASE>). All examples assume Streamable HTTP (the spec default); the runner negotiates SSE fallback automatically when the server advertises it.

For the broader secret resolution rules referenced below, see secrets-and-variables.md. For network diagnostics shown by mcptest doctor --url and the runner, see the "Layered failures" section at the bottom.

Scaffolding

mcptest init --url https://mcp.example.com/v1 writes a URL-keyed server: block and a smoke test that exercises the MCP initialize handshake. Pass --auth bearer --auth-env MCPTEST_API_TOKEN to add a bearer recipe, --auth oauth2 for the PKCE (Proof Key for Code Exchange) flow, or --auth custom-header --header-name X-Tenant-Id --header-env TENANT_ID for a custom header. Add --wait-for-ready 60s to scaffold a readiness poll under server.wait_for_ready.

Example 1: Local development (no auth)

A local HTTP server during development, no authentication required.

# yaml-language-server: $schema=https://mcptest.sh/schema/v1.json

servers:
  local:
    url: "http://localhost:8080/mcp"

tools:
  - name: "lists tools without error"
    server: local
    tool: "tools/list"
    args: {}
    expect:
      - target: "error"
        matcher:
          exact: null
        message: "tools/list should not return an error"

Run it: mcptest run tests/.

Example 2: Staging preview with a bearer token

A Vercel or Render preview URL guarded by a bearer token. The token lives in an env var so the YAML stays free of secrets.

servers:
  staging:
    url: "https://mcp-preview-abc123.vercel.app/mcp"
    auth:
      bearer_token_env: MCPTEST_STAGING_TOKEN
    wait_for_ready: 60s

Set the env var in .env (auto-loaded) or export it before running:

export MCPTEST_STAGING_TOKEN=...
mcptest run tests/

The wait_for_ready field polls initialize until the server is up, which is the common preview-deploy gotcha. The runner exits with code 3 if the budget elapses.

Example 3: Production with OAuth 2.1 + PKCE

OAuth 2.1 with PKCE and dynamic client registration is the specification-default auth flow for HTTP MCP servers. mcptest login runs the browser-based authorization once and caches an access token locally; the runner refreshes the token automatically when it expires .

servers:
  production:
    url: "https://mcp.example.com/v1"
    auth:
      oauth:
        flow: authorization_code_pkce
mcptest login --server production
mcptest run tests/

Example 4: Cloudflare Access wrapped server

A server fronted by Cloudflare Access uses a service token (the CF-Access-Client-Id plus CF-Access-Client-Secret header pair). The client id is non-sensitive; the secret lives in an env var.

servers:
  cf_protected:
    url: "https://mcp.acme.example/v1"
    headers:
      CF-Access-Client-Id: "abc123.access"
      CF-Access-Client-Secret-Env: CF_ACCESS_CLIENT_SECRET

The -Env suffix tells the runner to read the value from the environment at connect time and to redact it from logs.

Example 5: Multi-tenant SaaS routing

A multi-tenant server that dispatches by X-Tenant-Id. The tenant identifier is non-sensitive and varies per environment, so it lives in the YAML's variables: block with ${VAR} interpolation pulling from the resolved variable map.

variables:
  tenant: ${TENANT_ID}

servers:
  saas:
    url: "https://mcp.saas.example/v1"
    auth:
      bearer_token_env: SAAS_API_KEY
    headers:
      X-Tenant-Id: "${tenant}"
TENANT_ID=acme SAAS_API_KEY=... mcptest run tests/

Example 6: Enterprise self-signed cert

A server inside an enterprise network that uses a private CA. Point the runner at the bundle file with --ca-bundle (or server.http.ca_bundle in the YAML); never disable TLS verification in production.

servers:
  internal:
    url: "https://mcp.internal.acme.com/v1"
    auth:
      bearer_token_env: ACME_INTERNAL_TOKEN
    http:
      ca_bundle: /etc/ssl/certs/acme-internal-ca.pem

For a one-off local probe against a self-signed dev server (no CA bundle handy), pass --insecure-skip-verify. The runner prints a loud banner so this never sneaks into a CI job by accident.

Doctor: layered diagnostics

Before running the suite, mcptest doctor --url <BASE> walks the seven network layers and prints a single line per layer:

mcptest doctor --url https://mcp.example.com/v1
  [DNS] OK: resolved mcp.example.com to 2 address(es)
  [TCP] OK: connected to 198.51.100.42:443
  [TLS] OK: TLS handshake to mcp.example.com succeeded
  [AUTH] OK: credentials accepted by the server
  [MCP-INIT] OK: initialize handshake succeeded

On a failure the row turns FAIL and a hint: line points at the most likely next step. The runner uses the same shape for in-flight failures so a TLS error from doctor looks identical to a TLS error from a test step.

Common flags

FlagPurpose
--server-url <URL>Override every YAML server's URL for one run. Used by preview-deploy CI.
--server-auth-bearer-env <NAME>Swap the bearer env var name without editing YAML.
--header <NAME=VALUE>Send an extra literal header on every request.
--header-env <NAME=VAR>Send an env-backed header (the runner reads VAR at connect time).
--http-timeout <SECONDS>Per-request timeout, override server.http.timeout.
--connect-timeout <SECONDS>TCP connect timeout.
--ca-bundle <PATH>PEM bundle for an internal CA.
--insecure-skip-verifyDisable TLS verification (local dev only).
--wait-for-ready[=DUR]Poll initialize until success or budget elapses.

For background on the secret resolution model see secrets-and-variables.md. For the OAuth flow specifics see auth.md.

Behind a corporate proxy

Corporate networks usually tunnel outbound HTTP through a CONNECT proxy. mcptest works with that out of the box because reqwest (the HTTP client mcptest builds on) reads HTTP_PROXY, HTTPS_PROXY, and NO_PROXY from the environment at startup. The same env-var pickup applies to MCP servers (the request hitting your staging URL) and to LLM providers used by agent tests.

export HTTPS_PROXY=http://proxy.corp:8080
export NO_PROXY=localhost,.internal.example
mcptest run --config tests/mcp.yaml

When env vars are not an option, every flag below overrides the auto-pickup. They are global, so they apply to the server connection and to provider calls in the same run.

FlagPurpose
--proxy <URL>Catch-all proxy for both HTTP and HTTPS targets.
--http-proxy <URL>Plain-HTTP only. Wins over --proxy.
--https-proxy <URL>HTTPS only. Wins over --proxy.
--noproxy <HOSTLIST>Comma-separated hosts to bypass (mirrors NO_PROXY).
--no-proxyDisable every proxy, including env-var pickup.

Verify what is in effect:

mcptest doctor                       # one-line proxy summary
mcptest run --print-config           # same summary plus resolved suite

Common patterns:

# Route through the corporate proxy but go direct to localhost.
mcptest --https-proxy http://proxy.corp:8443 \
        --noproxy localhost,.internal \
        run

# A system-wide HTTPS_PROXY is set but this single run should go direct.
mcptest --no-proxy run

# Same proxy for both schemes, authenticated.
mcptest --proxy http://user:pass@proxy.corp:8080 run

Authentication in the proxy URL follows curl rules. Credentials in http://user:pass@host:port are sent automatically; an upstream that requires Negotiate or NTLM auth is not supported by reqwest.