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
- Open mcprelease.dev in a browser.
- Enter a public HTTPS MCP endpoint URL and click Run Release Check.
- 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/mcpRun without installing
npx -y @mcp-release/cli check https://your-mcp-server.example.com/mcpLocal 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-serverFor 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/mcpis 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 AjvoutputSchema(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:
| Code | Severity | When |
|---|---|---|
AUTH_REQUIRED | WARNING | 401 returned, no credentials were provided |
AUTH_INVALID | FAIL | 401 with credentials (including RFC 6750 error="invalid_token", which covers expired, revoked, and malformed tokens) |
AUTH_EXPIRED | FAIL | 401 with credentials + an unambiguous expiry code: error="expired" or error="token_expired" |
AUTH_FORBIDDEN | FAIL | 403 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`sRetry-Afterheader (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-httpGitHub 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: passEnvironment 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 --markdownGitHub 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-Authenticateheader values are stripped before being returned. - Tools are discovered via
tools/listbut 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.
| Format | Flag | Use |
|---|---|---|
| Terminal | (default) | Human-readable colored output |
| JSON | --json | Machine-readable; CI pipelines and automated tooling |
| Markdown | --markdown | GitHub 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
--headeror a config file for authenticated scenarios. - AUTH_EXPIRED requires an unambiguous expiry code. The RFC 6750 standard code
error="invalid_token"producesAUTH_INVALIDbecause it covers expired, revoked, and malformed tokens. Onlyerror="expired"anderror="token_expired"produceAUTH_EXPIRED. Most OAuth 2.0 servers will produceAUTH_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/mcpThis 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 lintThe 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: failThe 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.
