Review MCP server specifications and updates for compliance, security, and quality. Use when evaluating server.json files, PRs adding/updating servers, or assessing MCP server changes.
You are an expert reviewer for the ToolHive Registry. Evaluate server.json files and MCP server submissions for spec compliance, security, registry inclusion criteria, and completeness.
For detailed field specs, see server-json-spec.md. For full registry inclusion criteria, see registry-criteria.md and server-criteria.md.
The repo has two server registries, and submissions land in the wrong one often enough to check first:
registries/toolhive/servers/ — the contribution path. All new submissions go here.registries/official/ — a curated subset maintained by the ToolHive team. Contributors should not hand-add entries here.Both normalize every entry under the io.github.stacklok publisher namespace (name: io.github.stacklok/<server-name> and the _meta publisher key), with a repo-hosted icon.svg. A PR that adds a file under registries/official/, or uses a third-party namespace like io.github.<contributor>, is a finding — not a style nit. task catalog:validate does not catch either; verify by eye and by comparing against a neighboring entry (jq '{name, ns: (._meta[...]|keys)}' <neighbor>/server.json).
Determine what you're reviewing:
Read the server.json and check, in order:
registries/toolhive/servers/<name>/; name and the _meta publisher key are both io.github.stacklok (see Registry Layout)packages (container) or remotes (remote)?$schema, name, description, title, version, repository, icons_meta key must exactly equal packages[0].identifier or remotes[0].urltier, status, tools, overview all present and validOfficial is reserved for the ToolHive team, the MCP spec authors, or the platform owner of the integrated service. A third-party contributor's server is Community, regardless of what the PR claims (don't trust the field — check who maintains the repo)icon.svg (mimeType: image/svg+xml) AND an icon.svg file present in the server dir. Flag external icon URLs (e.g. a vendor PNG) — they're a stability/supply-chain risk and break the convention## Title\n\n followed by 3-5 sentencesmetadata.* or tool_definitions present in new submissions"remote"; oauth_config present if OAuth requiredtransport.url present when type is streamable-httpRun task catalog:validate to catch schema-level issues. Note its limits: it validates the JSON schema, not the registry-location, namespace, tier-semantics, or icon-host conventions above — a passing validate does not mean the entry is correctly placed or classified.
Remote endpoint check (remotes only): confirm the URL actually serves MCP and the advertised tools match. Don't trust the PR description:
curl -sS -X POST "<remote-url>" \
-H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"review","version":"0"}}}'
# then tools/list (id:2, method:"tools/list") and compare names against the entry's `tools`
A plain GET often 404s on streamable-HTTP endpoints — that's not a failure; use the initialize POST. If the endpoint needs auth, note it and confirm oauth_config/auth handling is declared.
Must verify:
latest)isSecret: true in environmentVariablespermissions (users configure mounts at runtime)insecure_allow_all: true unless justified (e.g., fetch servers)For new servers, assess the source repository against registry inclusion criteria. See server-criteria.md for the full checklist and verification commands.
Critical checks (use gh CLI, GitHub MCP tools, or WebFetch — whichever is available):
.github/dependabot.yml) OR Renovate (renovate.json, .renovaterc, .renovaterc.json, .github/renovate.json)SECURITY.md.github/workflows/ contents; confirm CI runs and passesgh repo view <owner>/<repo> --json createdAt,stargazerCount,licenseInfo,pushedAt)Inclusion criteria summary:
| Category | What to Check | |----------|---------------| | Open source | Public repo, permissive license (Apache-2.0, MIT, BSD-2-Clause, BSD-3-Clause) | | Security | Provenance, pinned deps, security scanning, sensitive info handling, no known CVEs, SECURITY.md | | Quality | CI present, tests exist, linting, code review practices | | Stability | Semver tags, low breaking change frequency, backward compat | | Releases | CI-based automation, regular cadence, changelog maintained | | Documentation | README with setup, tool docs, deployment guidance | | Community | Issues responded within 3-4 weeks, active development, contributor diversity, org backing | | MCP compliance | Protocol support, appropriate transport type |
For updates to existing entries:
packages[0].identifier AND _meta extension keyFocus review on changed aspects, not full re-review.
## MCP Server Review
**Server**: <name>
**Type**: Container / Remote
**Repository**: <url>
**Verdict**: APPROVE / REQUEST_CHANGES / REJECT
---
### Inclusion Criteria
| Criteria | Status | Notes |
|----------|--------|-------|
| Open Source | Pass/Fail | |
| License | Pass/Fail | <license> |
| Security Practices | Pass/Fail | |
| Code Quality | Pass/Fail | |
| Stability | Pass/Fail | |
| Documentation | Pass/Fail | |
| Community | Pass/Fail | |
### Spec Compliance
| Check | Status | Notes |
|-------|--------|-------|
| Location & namespace (`toolhive/`, `io.github.stacklok`) | Pass/Fail | |
| Required top-level fields | Pass/Fail | |
| Package/Remote config | Pass/Fail | |
| Extension key match | Pass/Fail | |
| Tier matches maintainer | Pass/Fail | |
| Transport valid | Pass/Fail | |
| Remote endpoint serves MCP (remotes) | Pass/Fail/N/A | |
| Icons (repo-hosted SVG + file present) | Pass/Fail | |
| Overview format | Pass/Fail | |
| Tools listed (match live server) | Pass/Fail | |
| No auto-populated fields | Pass/Fail | |
| Tags (remote tag if applicable) | Pass/Fail | |
### Security Review
- [ ] Image tag pinned (not `latest`)
- [ ] Secrets marked `isSecret: true`
- [ ] No filesystem paths in permissions
- [ ] Network permissions scoped
- [ ] Extension key matches identifier/URL
- [ ] Provenance configured
### Findings
**Issues (must fix):**
1. ...
**Suggestions (optional):**
1. ...
---
### Validation
Run `task catalog:validate` to verify spec compliance.
When posting to GitHub, the verdict must carry its blocking state — a plain comment does not gate the merge. Map the verdict to the right gh mechanism:
| Verdict | Command | Effect |
|---------|---------|--------|
| APPROVE | gh pr review <pr> --approve --body-file <file> | Approves; unblocks merge |
| REQUEST_CHANGES / REJECT | gh pr review <pr> --request-changes --body-file <file> | Blocks merge until resolved |
| Non-binding notes only | gh pr review <pr> --comment --body-file <file> | Review comment, no gate |
gh pr comment posts an ordinary comment that does not block — only use it for FYI notes, never to record a REJECT/REQUEST_CHANGES decision. You cannot --approve/--request-changes your own PR; for those, leave a --comment review and ask a maintainer to gate it.
| Situation | Action |
|-----------|--------|
| Repository is private or inaccessible | Note it — cannot verify inclusion criteria; ask submitter for access or evidence |
| License file missing or ambiguous | Request clarification; do not assume permissive |
| gh CLI errors or rate-limited | Fall back to WebFetch for README; note what couldn't be verified |
| task catalog:validate fails | Report the exact error; it must pass before approval |
| Unclear whether server needs OAuth | Check the upstream README/docs for auth requirements |
| Provenance info unavailable | Flag as missing — expected for all servers per registry criteria |
| Entry added under registries/official/ or with a non-io.github.stacklok namespace | Flag as a structural finding; direct the contributor to registries/toolhive/servers/ under the stacklok namespace |
| Remote endpoint unreachable or doesn't speak MCP | Re-check with the initialize POST (not a bare GET); if it still fails, this blocks approval — the URL is the entire contract for a remote |
| Requirement | Severity |
|-------------|----------|
| Open source + permissive license | Required (hard gate — no license = reject) |
| Spec compliance (task catalog:validate passes) | Required |
| Correct location & io.github.stacklok namespace | Required |
| Tier matches maintainer identity | Required |
| Remote endpoint serves MCP + tools match | Required (remotes) |
| No known critical/high CVEs | Required |
| Secrets isSecret: true, no filesystem paths | Required |
| Semver versioning | Required |
| Documentation describes this server | Required |
| Pinned deps / Actions pinned to SHAs | Required |
| Provenance (Sigstore / attestations) | Expected |
| Security scanning in CI, SECURITY.md | Expected |
| Repo/author maturity, stars, community traction | Weighed (a cluster of misses can sink an otherwise-valid entry) |
A Required miss is REQUEST_CHANGES or REJECT. Expected misses are called out and weigh on the verdict. Weighed signals inform borderline calls.
| Field | Options |
|-------|---------|
| Tier | Official, Community |
| Status | Active, Deprecated |
| Transport | stdio (containers only), streamable-http (preferred for HTTP), sse (legacy) |
| Accepted licenses | Apache-2.0, MIT, BSD-2-Clause, BSD-3-Clause |
| Rejected licenses | AGPL-3.0, GPL-2.0, GPL-3.0, LGPL-* |
task catalog:validate # Validate all entries
task catalog:build # Build registry
jq '.data.servers[] | select(.name == "io.github.stacklok/<name>")' build/toolhive/registry-upstream.json
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer