MCP Release

Documentation

Overview

MCP Release checks a remote MCP server. It verifies the protocol handshake, discovers available tools, validates their schemas, and checks network configuration. It does not invoke tools or require credentials.

Results are structured findings classified as PASS, WARNING, or FAIL. Findings can be exported as JSON or Markdown.

Quick start

  1. Open mcprelease.dev in a browser.
  2. Enter a public HTTPS MCP endpoint URL and click Run Release Check.
  3. Review the findings report. Download it as JSON or Markdown if needed.

To try without a real server, use the demo endpoint: https://mcp-release-fixture.vercel.app/mcp

Command-line interface

The CLI supports public, private, staging, localhost, and authenticated MCP endpoints. Credentials stay in your environment and are never sent to the web app or stored.

Install

npm install -g @mcp-release/cli
mcp-release --version
mcp-release check https://your-mcp-server.example.com/mcp

Run without installing

npx -y @mcp-release/cli check https://your-mcp-server.example.com/mcp

Local stdio server

The CLI can also validate MCP servers that communicate over stdin/stdout (spawned processes). Validation runs entirely locally; no data is sent to any remote service.

# Spawn a local server and validate over stdin/stdout
mcp-release check --stdio --command "npx -y my-mcp-server"

# With a working directory
mcp-release check --stdio --command "node dist/server.js" --cwd ./packages/my-server

For auth options (bearer tokens, custom headers, localhost), see Private and authenticated servers below.

Web checker endpoint requirements

  • Must use HTTPS. HTTP endpoints are rejected before any connection.
  • Must be publicly reachable. Private IP ranges, loopback, link-local, and cloud-metadata addresses are blocked.
  • Must not contain embedded credentials. For example, https://user:secret@host/mcp is rejected.
  • The web checker does not accept, forward, or store credentials. It connects as an unauthenticated client.

What is checked

Protocol

  • MCP initialization handshake
  • Protocol version negotiation
  • Transport response codes and headers

Tool schemas

  • Tool names (non-empty, valid characters)
  • Tool descriptions (present and non-empty)
  • inputSchema: valid JSON Schema, compilable by Ajv
  • outputSchema (validated if present)
  • Duplicate tool names

Network safety (HTTP/SSE)

  • SSRF protection (RFC 1918, loopback, link-local blocked)
  • DNS pinning (pre-resolve, validate IP, pin TCP connection)
  • Redirect chain validation (up to 3 hops)
  • HTTPS enforcement across all redirects

Stdio transport (CLI / GitHub Action)

  • Non-JSON lines written to stdout (logs must go to stderr)
  • Valid JSON that is not a valid MCP message
  • Response size limit (configurable; default 10 MB)
  • Unclean shutdown (process did not exit after stdin EOF)

What is not checked

  • Tools are not invoked. No tool arguments are constructed or sent.
  • The web checker does not validate authenticated endpoints or accept credentials. The CLI and GitHub Action can validate authenticated scenarios, but MCP Release does not audit the correctness of a server's authorization policy.
  • Runtime correctness of tool responses
  • Server authorization policies or access controls

PASS / WARNING / FAIL

PASS
All checks completed without a blocking or incomplete condition. A PASS does not guarantee universal security or correctness. It means the checks MCP Release ran all passed.
WARNING
One or more checks could not be completed or found a non-blocking issue. The overall result is the worst severity across all findings.
FAIL
One or more checks found a blocking condition. The server should be reviewed before production.

Authentication behavior

The web checker at mcprelease.dev does not accept credentials. When a server returns 401, MCP Release records AUTH_REQUIRED (WARNING) and stops. This is expected: it means the server is protected, not broken.

The CLI and GitHub Action can send credentials to the endpoint. Credential responses are classified as follows:

CodeSeverityWhen
AUTH_REQUIREDWARNING401 returned, no credentials were provided
AUTH_INVALIDFAIL401 with credentials (including RFC 6750 error="invalid_token", which covers expired, revoked, and malformed tokens)
AUTH_EXPIREDFAIL401 with credentials + an unambiguous expiry code: error="expired" or error="token_expired"
AUTH_FORBIDDENFAIL403 Forbidden (credentials lack required permissions)

Limitation: AUTH_EXPIRED is only produced when the server returns an unambiguous, expiry-specific error code in the WWW-Authenticate header. The RFC 6750 standard code error="invalid_token" produces AUTH_INVALID because it covers expired, revoked, and malformed tokens and is too broad for an expiry-specific classification. Most OAuth 2.0 servers use invalid_token and will produce AUTH_INVALID. Response bodies and error_description fields are never read or included in reports.

Rate limiting and resilience (CLI, v0.3.0)

429 and Retry-After

When a server returns 429 Too Many Requests, MCP Release records RATE_LIMITED (FAIL). If a Retry-After header is present, it is parsed as either integer seconds (e.g. Retry-After: 30) or an HTTP date (e.g. Retry-After: Wed, 21 Oct 2026 07:28:00 GMT). Retry-After values exceeding 60 seconds are capped at 60 seconds. Unparseable values produce RETRY_AFTER_INVALID (WARNING).

Timeout types

  • CONNECT_TIMEOUT: TCP connection could not be established within the timeout.
  • RESPONSE_TIMEOUT: TCP connection was established but the server did not send a response within the configured response timeout.

Retry rules

Retries are off by default. Each failure category must be explicitly listed in retries.retryOn, and retries.maxAttempts must be greater than 1. Without both, no retries occur.

  • rate-limit: retry HTTP 429. Honours the server`s Retry-After header (integer seconds or HTTP date), capped at 60 seconds.
  • server-error: retry 5xx responses.
  • connection-failure: retry connect failures and connect timeouts.
  • response-timeout: retry when the server connected but did not respond in time.
  • 401, 403, 400, schema errors, malformed MCP responses, and scenario timeouts are never retried regardless of configuration.

Private and authenticated servers

The web checker at mcprelease.dev supports public HTTPS endpoints only. It does not accept credentials of any kind, and it cannot reach private networks or localhost. If your server returns AUTH_REQUIRED, the web checker cannot validate it further.

For private, staging, localhost, or authenticated MCP endpoints, use the CLI or GitHub Action. Both run in your own environment so credentials never leave your machine or CI secrets store.

CLI

# Bearer token from an environment variable (recommended for secrets)
MCP_TOKEN=your-token mcp-release check https://staging.example.com/mcp \
  --bearer-token-env MCP_TOKEN

# Literal header (for non-secret values)
mcp-release check https://staging.example.com/mcp \
  --header "X-Tenant-Id: acme"

# Localhost or private network endpoint
mcp-release check http://localhost:4000/mcp --allow-http

GitHub Action

- name: Validate MCP server
  uses: daranium2020/mcp-release@v0.3.0
  with:
    endpoint: https://staging.example.com/mcp
    bearer-token-env: MCP_TOKEN
  env:
    MCP_TOKEN: ${{ secrets.MCP_TOKEN }}
  • Credentials stay in your local environment or CI secrets. They are not sent to the web app or stored anywhere.
  • MCP Release still discovers tools but never executes them. No tool arguments are constructed or sent.
  • A PASS result does not guarantee security or correctness. It reflects the checks MCP Release ran.

Configuration file (CLI, v0.3.0)

The CLI and GitHub Action support a YAML configuration file for running multiple named scenarios (authenticated, unauthenticated, and expected-failure) in a single command.

# mcp-release.config.yml
version: "1"
endpoint: "https://api.example.com/mcp"
headers:
  Authorization: "Bearer ${MCP_TOKEN}"   # resolved from environment at runtime

timeouts:
  connectMs: 5000
  responseMs: 10000

retries:
  maxAttempts: 3
  backoffMs: 1000
  retryOn:           # retries disabled by default
    - rate-limit     # retry HTTP 429
    - server-error   # retry 5xx

scenarios:
  - name: authenticated-pass
    expect:
      result: pass

  - name: missing-auth
    headers: {}               # override: send no credentials
    removeHeaders:
      - Authorization
    expect:
      httpStatus: 401         # expect 401 AUTH_REQUIRED (a negative test)

  - name: read-only-resource
    headers:
      Authorization: "Bearer ${READ_ONLY_TOKEN}"
    expect:
      result: pass

Environment variable substitution

Header values containing ${VAR_NAME} are resolved from the process environment at runtime, never at parse time. If a variable is unset, the literal placeholder is sent and the server will likely respond with AUTH_INVALID.

Scenario expectations

Each scenario has an expect block. Both fields are optional and independent:

  • result: pass: the overall check must produce PASS; any FAIL finding causes a mismatch.
  • result: fail: the check must produce at least one FAIL finding (useful for negative tests).
  • httpStatus: 401: the inferred HTTP status must match (see Auth finding codes above).

When the actual outcome does not match, MCP Release adds SCENARIO_MISMATCH (FAIL) to the findings and the overall config run status is FAIL.

Running config scenarios

# Terminal report (default)
mcp-release check --config mcp-release.config.yml

# JSON report
mcp-release check --config mcp-release.config.yml --json

# Markdown report
mcp-release check --config mcp-release.config.yml --markdown

GitHub Actions integration

- name: Validate MCP server scenarios
  uses: daranium2020/mcp-release@v0.3.0
  with:
    config: mcp-release.config.yml
  env:
    MCP_TOKEN: ${{ secrets.MCP_TOKEN }}
    READ_ONLY_TOKEN: ${{ secrets.READ_ONLY_TOKEN }}

Security model

Web checker (mcprelease.dev)

  • Only public HTTPS endpoints are accepted. HTTP is rejected before any connection.
  • Private, loopback, link-local (169.254.0.0/16), and cloud-metadata (169.254.169.254) destinations are blocked at the DNS level.
  • DNS pinning closes the TOCTOU gap. The resolved IP is pinned at connection time.
  • Redirects are re-validated at each hop. HTTPS applies across all redirects.
  • The web checker does not accept, forward, or store endpoint credentials.
  • Remote response bodies are never included in findings.
  • TLS verification is enforced (rejectUnauthorized: true).
  • Error messages are redacted. Token patterns, URL-embedded credentials, and WWW-Authenticate header values are stripped before being returned.
  • Tools are discovered via tools/list but never invoked. No arguments are constructed or sent.

CLI and GitHub Action

  • Credentials are sent only to the configured MCP endpoint. They are never sent to or stored by MCP Release.
  • Scenario execution and report generation run locally in the CLI or GitHub Actions runner.
  • Environment variable values used in header substitution are never logged or included in reports.
  • SSRF protections apply to all endpoints checked via the web app. The CLI and GitHub Action connect to arbitrary configured endpoints; apply allow-listing and network controls in your own environment as appropriate.

Report exports

Reports can be exported in three formats. All formats carry the same finding codes, severities, and scenario data.

FormatFlagUse
Terminal(default)Human-readable colored output
JSON--jsonMachine-readable; CI pipelines and automated tooling
Markdown--markdownGitHub PR summaries and release notes

The JSON schema version is "1". Single-check and config-run reports use a consistent schema; config runs add a scenarios array with per-scenario results. All fields are documented in the repository.

Known limitations

  • Web checker: public HTTPS endpoints only. HTTP and private network endpoints are rejected. Use the CLI or GitHub Action for localhost, staging, and private endpoints.
  • Web checker: no credential input. Authenticated checks are not performed. Use the CLI with --header or a config file for authenticated scenarios.
  • AUTH_EXPIRED requires an unambiguous expiry code. The RFC 6750 standard code error="invalid_token" produces AUTH_INVALID because it covers expired, revoked, and malformed tokens. Only error="expired" and error="token_expired" produce AUTH_EXPIRED. Most OAuth 2.0 servers will produce AUTH_INVALID. Response bodies are never inspected.
  • Tools are not invoked. Runtime correctness of tool responses is not validated.
  • A PASS is not a security guarantee. It reflects the checks MCP Release ran. Runtime behavior may differ in other environments.
  • In-memory rate limiting (web checker). Per-process only; not shared across scaled instances.
  • No persistent storage. Web checker reports are not saved server-side. Export before closing the tab.

Privacy and data handling

MCP Release does not store endpoint URLs, request bodies, reports, or validation results. Findings are returned to the browser in the HTTP response and are not retained server-side. Export or save them before closing the tab.

The web application does not request, accept, forward, or store credentials and connects as an unauthenticated client. CLI and GitHub Action credentials remain in the user's local environment or CI secrets and are sent only to the configured MCP endpoint. Discovered tools are listed but never executed; no tool arguments are constructed or sent.

JSON and Markdown exports are generated from the report data returned in the response. They are not transmitted to any external service.

Transport diagnostic errors are logged server-side at the time of the request. As with all hosted applications, the hosting provider may retain operational logs including request metadata and error diagnostics. The repository does not configure a specific retention period for those logs.

Questions or concerns: feedback@mcprelease.dev

Demo endpoint

A public fixture server is available for testing MCP Release itself:

https://mcp-release-fixture.vercel.app/mcp

This server is deterministic, unauthenticated, and has no external dependencies. Checking it should return PASS with two tools: echo and ping.

Local development

# Install dependencies
pnpm install

# Start the web app
pnpm --filter @mcp-release/web dev

# Run all tests
pnpm test

# Type-check all packages
pnpm typecheck

# Lint
pnpm lint

The development server starts on http://localhost:3000. Dev-only fixture buttons (PASS / FAIL / WARNING) are visible in development and removed in production builds.

GitHub Action

A GitHub Action is available at daranium2020/mcp-release to run MCP Release checks in CI:

# HTTP/SSE endpoint
- uses: daranium2020/mcp-release@v0.3.0
  with:
    endpoint: https://your-mcp-server.example.com/mcp
    fail-on: fail        # optional: fail (default) | warning
    timeout-ms: 10000    # optional: 1000-30000

# Local stdio server (spawned process)
- uses: daranium2020/mcp-release@v0.3.0
  with:
    transport: stdio
    command: npx -y my-mcp-server
    fail-on: fail

The action annotates the job with findings and writes a summary to the GitHub Actions job summary. Exit code is 0 when the result is below the threshold and 1 when the threshold is met or exceeded.

See the repository for the full action manifest and inputs.