Help us improve
Share bugs, ideas, or general feedback.
From pubmed-mcp-server
Finalizes documentation and project metadata for MCP servers after implementation is complete. Audits surface area, updates README and agent protocol files.
npx claudepluginhub cyanheads/cyanheads --plugin pubmed-mcp-serverHow this skill is triggered — by the user, by Claude, or both
Slash command
/pubmed-mcp-server:polish-docs-metaThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
- Server implementation is functionally complete (tools, resources, prompts, services all working)
Finalizes documentation and project metadata for MCP servers after implementation is complete. Audits surface area, updates README and agent protocol files.
Finalizes documentation and project metadata for an MCP server after implementation is complete. Audits surface area, updates README and agent protocol files.
Orients developers after scaffolding an MCP server with @cyanheads/mcp-ts-core init. Explains agent protocol files (CLAUDE.md/AGENTS.md), project structure, conventions, and the skill sync model.
Share bugs, ideas, or general feedback.
bun run devcheck passes, tests passPrefer running after implementation is complete, but safe to re-run at any point — steps are idempotent.
Companion: pair with security-pass for a full pre-ship review — this skill polishes docs and metadata; security-pass audits handlers for MCP-specific security gaps.
bun run devcheck passesbun run test)If these aren't met, address them first.
Read all tool, resource, and prompt definitions. Build a mental model of what the server actually does — names, descriptions, input/output shapes, auth scopes. This inventory drives every document below.
Read:
src/index.ts (what's registered in createApp())src/mcp-server/tools/definitions/src/mcp-server/resources/definitions/src/mcp-server/prompts/definitions/src/services/ (if any)src/config/server-config.ts (if any)Capture: tool count, resource count, prompt count, service count, required env vars.
Read references/readme.md for structure and conventions. If README.md doesn't exist, create it from scratch. If it exists, diff the current content against the audit — update tool/resource/prompt tables, env var lists, and descriptions to match the actual surface area. Don't rewrite sections that are already accurate.
The bold header tagline (the <b> text inside the first <p>) must match the package.json description. The surface count is a nested <div> inside the same <p>, separated by •.
Update the project's agent protocol file to reflect the actual server. Scope is the project-root CLAUDE.md / AGENTS.md only — do not edit skills/*/SKILL.md or their references/ files. Those are external skill files synced from @cyanheads/mcp-ts-core and get overwritten on the next maintenance refresh.
Read references/agent-protocol.md for the full update checklist, then review the current file and address what's stale or missing:
searchItems, itemData), replace with real definitions from this serverpackage.json, update the commands table.env.exampleCompare .env.example against the server config Zod schema. Add any missing server-specific vars with a comment and default (if any). Remove vars for features that no longer exist. Group by category. Preserve existing framework vars that are still relevant.
package.json MetadataCheck for empty or placeholder metadata fields. Read references/package-meta.md for which fields matter and why. Fill in anything still missing — skip fields that are already correct.
Key fields: name, description, repository, author, homepage, bugs, keywords.
name must communicate the server's domain at a glance. See references/package-meta.md for the naming convention — ambiguous abbreviations and acronym-only names fail the scannability test for humans and agents alike.
description is the canonical source. Every other surface (README header, server.json, Dockerfile OCI label, GitHub repo description) derives from it. Write it here first, then propagate.
server.jsonRead references/server-json.md for the official MCP server manifest schema. If server.json doesn't exist, create it from the surface area audit. If it exists, diff against current state and update stale fields.
Key sync points:
$schema set to https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.jsonname matches mcpName from package.json (reverse-domain: io.github.{owner}/{repo})version matches package.json version (in all three places: top-level + each package entry)description matches package.json descriptionenvironmentVariables reflect the server config Zod schema — server-specific required vars in both entries, transport vars only in HTTP entrySync the GitHub repo with package.json using the gh CLI. Skip if the repo isn't hosted on GitHub or gh isn't available.
Description:
gh repo edit <owner>/<repo> --description "<package.json description>"
Topics ↔ Keywords:
Compare GitHub topics (gh repo view --json repositoryTopics) against package.json keywords. They should be the union — add any that exist in one but not the other:
gh repo edit --add-topic <topic>package.json → add to keywords arrayCommon keywords shared across MCP servers (e.g., mcp, mcp-server, model-context-protocol, typescript) should appear in both. Domain-specific keywords should also be present in both.
bunfig.tomlVerify a bunfig.toml exists at the project root. If not, create one:
[install]
auto = "fallback"
frozenLockfile = false
[run]
bun = true
Two patterns are supported — pick one and stay consistent.
| Pattern | Best for |
|---|---|
| Directory-based (template default) | Published libraries, or servers whose consumers run the maintenance skill against them — per-version files ship inside node_modules/<pkg>/changelog/<minor>.x/<version>.md for direct agent inspection. |
Monolithic CHANGELOG.md | Runtime-only consumer servers where nobody imports types and nobody runs maintenance against the package — skips the build step and devcheck drift gate. |
Both are acceptable. The template scaffolds the directory-based structure by default; collapse to monolithic only if the rollup tooling is pure ceremony for this project.
Directory-based — per-version files live at changelog/<major.minor>.x/<version>.md (e.g. changelog/0.1.x/0.1.0.md), and CHANGELOG.md is a rollup regenerated by bun run changelog:build. Devcheck's Changelog Sync step enforces drift protection. changelog/template.md is a pristine format reference — never edited, never moved, never renamed. Read it to remember the frontmatter + section layout when scaffolding a new per-version file.
If the structure doesn't exist yet:
changelog/ directorychangelog/template.md once from the template (frontmatter stub + H1 # <version> — YYYY-MM-DD placeholder + empty Added/Changed/Fixed sections) — this file is a format reference only and stays as-is after creationchangelog/0.1.x/0.1.0.md with H1 # 0.1.0 — YYYY-MM-DD, concrete version and date — do not rename or move template.md to create the version file; author the per-version file directlybun run changelog:build to generate CHANGELOG.mdPer-version file format:
---
summary: One-line headline for the rollup index — ≤350 chars, no markdown
breaking: false
---
# 0.1.0 — YYYY-MM-DD
Optional narrative intro (1-3 sentences).
## Added
- [list tools, resources, prompts, key capabilities]
Frontmatter: summary is required (powers the CHANGELOG.md index), breaking is optional and defaults to false (set true for releases requiring consumer code changes).
Never hand-edit CHANGELOG.md when using this pattern — it's a build artifact. Never edit changelog/template.md — it's the format reference. Never use [Unreleased] as a version header in a released file.
Monolithic — maintain CHANGELOG.md directly in Keep a Changelog format. To collapse from the template default: delete the changelog/ directory, remove changelog:build and changelog:check from package.json scripts (and from devcheck.config.json if referenced), and drop "changelog/" from the files array. The release skill's directory-specific steps then don't apply — just edit CHANGELOG.md and bump version at release time.
If .codex-plugin/plugin.json exists, verify it's populated and in sync with package.json and server.json:
name matches package.json nameversion matches package.json versiondescription matches package.json descriptionrepository matches package.json repository.urllicense matches package.json licenseinterface.displayName = package.json nameinterface.shortDescription matches package.json descriptioninterface.category is set to a meaningful categoryIf .codex-plugin/mcp.json exists, verify the server name key matches package.json name and env vars include any required API keys from the server config schema.
If .claude-plugin/plugin.json exists, apply the same checks: name, version, description, repository, license from package.json. Verify the inline mcpServers entry key matches package.json name and env vars include any required API keys.
If the project ships as an .mcpb bundle for Claude Desktop (check for manifest.json at the project root), verify the full artifact set is present and consistent. If the project doesn't ship .mcpb bundles, skip this step.
Files that must exist:
manifest.json — MCPB manifest with mcp_config.env, user_config, and metadata.mcpbignore — controls what's excluded from the bundlepackage.json scripts:
bundle — builds the .mcpb (e.g., mcpb pack --output dist/)lint:packaging — validates manifest.json ↔ server.json env var consistency (run by devcheck)Cross-file consistency:
manifest.json version matches package.json versionmanifest.json (mcp_config.env + user_config) match server.json environmentVariables — lint:packaging enforces this, but verify the set is completemanifest.json name matches package.json name without the npm scope prefix (e.g. bls-mcp-server, not @cyanheads/bls-mcp-server); description matches package.jsonmanifest.json user_config entries must include title and type fields — mcpb pack validates theseuser_config entry referenced as ${user_config.X} in mcp_config.env: if it's not required: true, set "default": "". MCPB hosts (Claude Desktop included) pass the literal placeholder string through to the process when an optional field is left blank without a default — strict consumer validators (z.email(), z.url(), .regex()) then crash at lazy config load, exiting silently after initialize. Server-side: pair every optional env-backed strict-validator field with a z.preprocess that strips ${...} placeholders to undefined.server.json env var isRequired must match the upstream API's actual requirement — if the API works without the value (rate-limited, DEMO_KEY fallback, polite pool), mark isRequired: false and describe the tradeoff in the descriptionpackage.json, manifest.json, server.json (condensed, hard 100-char limit), README header <p><b>, and GitHub repo description (gh repo edit --description)package.json keywords include baseline terms: mcp, mcp-server, model-context-protocol, typescript, bun, stdio, streamable-http, plus data-domain terms. GitHub repo topics (gh repo edit --add-topic) should match.README install badges:
manifest.json exists, the README should include the Claude Desktop install badge linking to releases/latest/download/<name>.mcpbreferences/readme.md for badge format and config generation commandsLICENSEConfirm a license file exists. If not, ask the user which license to use (default: Apache-2.0, matching the scaffolded package.json). Create the file.
DockerfileIf a Dockerfile exists, verify the OCI labels and runtime config match the actual server:
org.opencontainers.image.title matches the package nameorg.opencontainers.image.description matches package.json descriptionorg.opencontainers.image.source points to the real repository URL (add if missing)mkdir and LOGS_DIR uses the correct server nameIf no Dockerfile exists and the server is deployed via HTTP transport, consider scaffolding one — the template is available via npx @cyanheads/mcp-ts-core init.
docs/tree.mdRegenerate the directory structure:
bun run tree
Review the output for anything unexpected (leftover files, missing directories).
Run the full check suite one last time:
bun run devcheck
bun run test
Both must pass clean.
README.md accurate — tool/resource tables, config, descriptions match actual code.env.example in sync with server config schemapackage.json metadata complete (description, mcpName, repository, author, keywords, engines, packageManager)server.json matches official MCP schema, versions synced, env vars currentpackage.json description; topics ↔ keywords in syncbunfig.toml presentCHANGELOG.md (hand-edited, Keep a Changelog) or directory-based (changelog/<minor>.x/<version>.md + rollup regenerated and in sync).codex-plugin/plugin.json populated and in sync with package.json (if present).codex-plugin/mcp.json server name and env vars current (if present).claude-plugin/plugin.json populated and in sync with package.json (if present)manifest.json present) — version synced, env vars match server.json, bundle + lint:packaging scripts exist, README install badges presentLICENSE file presentDockerfile OCI labels and runtime config accurate (if present)docs/tree.md regeneratedbun run devcheck passesbun run test passes