Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,25 @@ All notable changes to `codex-plugin-doctor` are documented here.

This changelog groups the shipped work into product-level release blocks instead of repeating every low-level git diff in isolation.

## [1.54.0] - 2026-07-28

### Added

- added offline MCP Registry readiness checks for metadata, ownership, package integrity, transport readiness, and client installability
- added opt-in exact-name lookup against the official MCP Registry with lifecycle validation and a non-executing Codex configuration preview
- added optional GitHub Action Registry readiness gating and the `mcp-registry-readiness.json` artifact

### Changed

- extended the public output contract with the additive `doctor.registry.readiness.json` schema surface
- accept historical official MCP Registry schema URLs with a compatibility warning while retaining strict metadata validation

### Security

- keep local Registry checks offline and require explicit `--allow-network` consent for live inspection
- constrain live requests to the fixed official Registry endpoint through the existing bounded HTTP client without following metadata URLs
- reject literal embedded secrets and unsafe package or transport declarations while preserving supported variable templates

## [1.53.0] - 2026-07-26

### Added
Expand Down
16 changes: 14 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,18 @@ Remote transport reliability adds one bounded SSE GET after initialization. HTTP

`--allow-session-lifecycle` is disabled by default and is state-changing: only after a valid `MCP-Session-Id` it permits one bounded session `DELETE`. `--require-remote-reliability` is a strict result gate: it fails unless every attempted remote reliability scorecard passes. Local-only runs are unaffected. It grants no network consent, so `--runtime --allow-network` (and loopback consent when applicable) remain required. See [Remote MCP Readiness](./docs/architecture/remote-mcp-readiness.md) and [Remote MCP Transport Reliability](./docs/architecture/remote-mcp-transport-reliability.md).

### MCP Registry Readiness

Validate official MCP Registry publication metadata without publishing or installing anything:

```bash
codex-plugin-doctor registry check path/to/server.json
codex-plugin-doctor registry check path/to/server.json --json --require-registry-readiness
codex-plugin-doctor registry inspect io.github.example/weather --allow-network
```

Local checks validate metadata, ownership consistency, package integrity, transports, and Codex installability. Exact Registry lookup is read-only, requires explicit network consent, and contacts only the fixed official Registry endpoint. It never follows advertised package, icon, repository, or remote MCP URLs. See [MCP Registry Readiness](./docs/architecture/mcp-registry-readiness.md).

Output formats:

- human text output
Expand Down Expand Up @@ -442,9 +454,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: Esquetta/CodexPluginDoctor@v1.53.0
- uses: Esquetta/CodexPluginDoctor@v1.54.0
with:
version: "1.53.0"
version: "1.54.0"
path: .
runtime: "true"
policy: codex-publish
Expand Down
31 changes: 31 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ inputs:
description: Fail the validation result unless every attempted remote MCP reliability scorecard passes; this does not grant network access.
required: false
default: "false"
registry-metadata:
description: Optional local server.json file or containing directory to validate for MCP Registry readiness.
required: false
default: ""
require-registry-readiness:
description: Fail unless the configured local Registry metadata receives a pass result.
required: false
default: "false"
installed:
description: Validate plugins from the local Codex plugin cache.
required: false
Expand Down Expand Up @@ -158,6 +166,9 @@ outputs:
action-manifest-path:
description: Path to the generated GitHub Action artifact manifest.
value: ${{ steps.run-doctor.outputs.action-manifest-path }}
registry-report-path:
description: Path to the MCP Registry readiness JSON report when registry-metadata is configured.
value: ${{ steps.run-doctor.outputs.registry-report-path }}
review-bundle-path:
description: Path to the generated review bundle directory when review-bundle is enabled.
value: ${{ steps.run-doctor.outputs.review-bundle-path }}
Expand All @@ -180,6 +191,8 @@ runs:
ALLOW_LOCAL_NETWORK_INPUT: ${{ inputs['allow-local-network'] }}
ALLOW_SESSION_LIFECYCLE_INPUT: ${{ inputs['allow-session-lifecycle'] }}
REQUIRE_REMOTE_RELIABILITY_INPUT: ${{ inputs['require-remote-reliability'] }}
REGISTRY_METADATA_INPUT: ${{ inputs['registry-metadata'] }}
REQUIRE_REGISTRY_READINESS_INPUT: ${{ inputs['require-registry-readiness'] }}
CORPUS_METRICS_MANIFEST_INPUT: ${{ inputs['corpus-metrics-manifest'] }}
CORPUS_METRICS_BASELINE_INPUT: ${{ inputs['corpus-metrics-baseline'] }}
CORPUS_METRICS_FAIL_ON_REGRESSION_INPUT: ${{ inputs['corpus-metrics-fail-on-regression'] }}
Expand All @@ -195,6 +208,7 @@ runs:
corpus_metrics_diff_path="$report_dir/corpus-metrics-diff.json"
output_contract_path="$report_dir/output-contract.json"
action_manifest_path="$report_dir/codex-plugin-doctor-action-manifest.json"
registry_report_path="$report_dir/mcp-registry-readiness.json"
review_bundle_path="$report_dir/${{ inputs['review-bundle-dir'] }}"
review_bundle_verification_path="$report_dir/review-bundle-verification.json"
status_file="${RUNNER_TEMP:-.}/codex-plugin-doctor-status"
Expand Down Expand Up @@ -321,6 +335,19 @@ runs:
run_doctor "output contract" doctor contract --json --output "$output_contract_path"
fi

if [[ -z "$REGISTRY_METADATA_INPUT" ]]; then
if [[ "$REQUIRE_REGISTRY_READINESS_INPUT" == "true" ]]; then
echo "require-registry-readiness requires registry-metadata."
record_status 2
fi
else
registry_args=(registry check "$REGISTRY_METADATA_INPUT" --json --output "$registry_report_path")
if [[ "$REQUIRE_REGISTRY_READINESS_INPUT" == "true" ]]; then
registry_args+=(--require-registry-readiness)
fi
run_doctor "MCP Registry readiness" "${registry_args[@]}"
fi

if [[ "${{ inputs['review-bundle'] }}" == "true" ]]; then
signing_key_env="${{ inputs['signing-key-env'] }}"

Expand Down Expand Up @@ -367,6 +394,7 @@ runs:
export CODEX_PLUGIN_DOCTOR_ACTION_CORPUS_METRICS="$([[ -n "$CORPUS_METRICS_MANIFEST_INPUT" ]] && echo true || echo false)"
export CODEX_PLUGIN_DOCTOR_ACTION_CORPUS_METRICS_DIFF="$([[ -n "$CORPUS_METRICS_BASELINE_INPUT" ]] && echo true || echo false)"
export CODEX_PLUGIN_DOCTOR_ACTION_CONTRACT="${{ inputs.contract }}"
export CODEX_PLUGIN_DOCTOR_ACTION_REGISTRY="$([[ -n "$REGISTRY_METADATA_INPUT" ]] && echo true || echo false)"
export CODEX_PLUGIN_DOCTOR_ACTION_REVIEW_BUNDLE="${{ inputs['review-bundle'] }}"
export CODEX_PLUGIN_DOCTOR_ACTION_REVIEW_BUNDLE_VERIFY="${{ inputs['review-bundle-verify'] }}"
export CODEX_PLUGIN_DOCTOR_ACTION_SUMMARY_PATH="$summary_path"
Expand All @@ -376,6 +404,7 @@ runs:
export CODEX_PLUGIN_DOCTOR_ACTION_CORPUS_METRICS_PATH="$corpus_metrics_path"
export CODEX_PLUGIN_DOCTOR_ACTION_CORPUS_METRICS_DIFF_PATH="$corpus_metrics_diff_path"
export CODEX_PLUGIN_DOCTOR_ACTION_CONTRACT_PATH="$output_contract_path"
export CODEX_PLUGIN_DOCTOR_ACTION_REGISTRY_PATH="$registry_report_path"
export CODEX_PLUGIN_DOCTOR_ACTION_REVIEW_BUNDLE_PATH="$review_bundle_path"
export CODEX_PLUGIN_DOCTOR_ACTION_REVIEW_BUNDLE_VERIFICATION_PATH="$review_bundle_verification_path"
node <<'NODE'
Expand Down Expand Up @@ -407,6 +436,7 @@ runs:
corpusMetrics: report("corpusMetrics", "CODEX_PLUGIN_DOCTOR_ACTION_CORPUS_METRICS", "CODEX_PLUGIN_DOCTOR_ACTION_CORPUS_METRICS_PATH"),
corpusMetricsDiff: report("corpusMetricsDiff", "CODEX_PLUGIN_DOCTOR_ACTION_CORPUS_METRICS_DIFF", "CODEX_PLUGIN_DOCTOR_ACTION_CORPUS_METRICS_DIFF_PATH"),
contract: report("contract", "CODEX_PLUGIN_DOCTOR_ACTION_CONTRACT", "CODEX_PLUGIN_DOCTOR_ACTION_CONTRACT_PATH"),
registryReport: report("registryReport", "CODEX_PLUGIN_DOCTOR_ACTION_REGISTRY", "CODEX_PLUGIN_DOCTOR_ACTION_REGISTRY_PATH"),
reviewBundle: report("reviewBundle", "CODEX_PLUGIN_DOCTOR_ACTION_REVIEW_BUNDLE", "CODEX_PLUGIN_DOCTOR_ACTION_REVIEW_BUNDLE_PATH"),
reviewBundleVerification: report("reviewBundleVerification", "CODEX_PLUGIN_DOCTOR_ACTION_REVIEW_BUNDLE_VERIFY", "CODEX_PLUGIN_DOCTOR_ACTION_REVIEW_BUNDLE_VERIFICATION_PATH")
}
Expand All @@ -432,6 +462,7 @@ runs:
echo "corpus-metrics-diff-path=$corpus_metrics_diff_path"
echo "output-contract-path=$output_contract_path"
echo "action-manifest-path=$action_manifest_path"
echo "registry-report-path=$registry_report_path"
echo "review-bundle-path=$review_bundle_path"
echo "review-bundle-verification-path=$review_bundle_verification_path"
} >> "$GITHUB_OUTPUT"
Expand Down
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ This directory contains public documentation for users, contributors, and securi
- [MCP 2025-11 Conformance](architecture/mcp-2025-11-conformance.md)
- [Remote MCP Readiness](architecture/remote-mcp-readiness.md)
- [Remote MCP Transport Reliability](architecture/remote-mcp-transport-reliability.md)
- [MCP Registry Readiness](architecture/mcp-registry-readiness.md)
- [Real-World Corpus Quality Metrics](architecture/real-world-corpus-quality-metrics.md)
- [Corpus Metrics Regression Diff](architecture/corpus-metrics-regression-diff.md)

Expand Down
85 changes: 85 additions & 0 deletions docs/architecture/mcp-registry-readiness.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# MCP Registry Readiness

## Purpose

The Registry Doctor validates whether MCP `server.json` metadata is structurally consistent, safe to consume, and useful for installation before publication. It also supports a bounded read-only lookup of an exact server name in the official MCP Registry.

This is a metadata readiness check. A passing Registry record does not prove that the referenced code or remote server is trustworthy, available, or safe to execute.

## Commands

Validate a local file or a directory containing `server.json`:

```bash
codex-plugin-doctor registry check ./server.json
codex-plugin-doctor registry check . --json
codex-plugin-doctor registry check . --require-registry-readiness
```

Inspect the latest published record for an exact server name:

```bash
codex-plugin-doctor registry inspect io.github.example/weather --allow-network
```

`registry check` never uses the network. `registry inspect` fails before making a request unless `--allow-network` is explicit.

## Scorecard

The report keeps these dimensions separate:

- metadata: required schema, name, description, and exact version shape
- ownership: local npm `mcpName` and GitHub namespace/repository consistency
- package integrity: exact versions, MCPB SHA-256, transport shape, and embedded secret checks
- transport readiness: declared package and remote transport validity
- client installability: whether a safe Codex configuration preview can be derived

The official Registry permits metadata-only records without `packages` or `remotes`. Those records remain valid but receive a warning because no installation channel can be derived. The default command exits successfully for warning-only reports; `--require-registry-readiness` turns any non-pass result into a blocking exit.

## Installation Preview

The JSON report may contain a `codexPreview` for:

- an exact-version npm package using `stdio`
- a fixed HTTPS remote URL without template variables

The preview is informational. The command never edits Codex configuration, downloads packages, starts a process, or contacts an advertised remote endpoint.

## Network Boundary

Registry inspection:

- sends one unauthenticated `GET` to the fixed official Registry host
- percent-encodes the exact server name
- uses the versioned `/v0.1` latest-version endpoint
- applies the shared timeout, response-size, DNS, peer, redirect, and SSRF controls
- does not follow package, icon, repository, website, or remote MCP URLs
- does not authenticate, publish, update, deprecate, or delete Registry data

The Registry is currently a preview service. Historical active records may reference an older official schema; they are reported as warnings rather than treated as malformed. New local publication metadata should use the current official schema.

## Security Findings

The readiness report fails on:

- URL-embedded credentials
- literal secret values in secret-like inputs
- mismatched `io.github` namespace and GitHub repository owner
- mismatched local npm `mcpName` or package version
- range or `latest` package versions
- MCPB packages without a valid lowercase SHA-256 digest
- invalid package or remote transport declarations

Variable templates such as `Bearer {api_key}` are not treated as literal embedded secrets.

## Non-Goals

Registry Doctor does not:

- prove namespace ownership independently of Registry publication
- download or inspect package artifacts
- validate an MCPB file against its declared hash
- execute generated installation commands
- probe advertised MCP endpoints
- claim that a listed server is secure or endorsed
- publish metadata to the Registry
Loading