npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@vrajved/mcpkit

v0.6.0

Published

Find the problems that break an MCP server in specific MCP clients (Claude, ChatGPT, Cursor, VS Code, Codex and more), with sourced client facts, SDK upgrade help and CI output.

Readme

mcpkit

Find the problems that break an MCP server in specific MCP clients, before your users do.

MCP is one protocol, but every client reads it differently: Claude Desktop, Claude Code, ChatGPT, Cursor, VS Code, OpenCode, Codex, Gemini CLI, Cline, Goose, Continue and Windsurf each rename, truncate, validate, cache and render things their own way. mcpkit connects to your server, records what it actually exposes, and checks it against sourced facts about each client. Every client-specific result links to where the fact comes from (docs, pinned client source, or maintainer statements), so a report tells you what breaks, where, and how we know.

npx @vrajved/mcpkit -- node dist/server.js

Installation

Requires Node.js 20 or newer. Your server can be written in any language.

npx @vrajved/mcpkit --help              # run without installing
npm install -g @vrajved/mcpkit          # install the CLI globally
npm install --save-dev @vrajved/mcpkit  # add it to a project, e.g. for CI

After installing, the command is mcpkit. To try the latest code from GitHub: npm install -g github:VrajVed/mcpkit.

Quick start

# A local server: put its start command after --
mcpkit -- node dist/server.js
mcpkit -- uv run server.py

# A remote server
mcpkit --url https://example.com/mcp

# Is its MCP SDK up to date?
mcpkit upgrade -- node dist/server.js

You get a report per client, with a fix for every problem, and an exit code for CI. New here? Start with the getting started guide.

What it finds

Real examples:

  • description: null on a tool. Absent is allowed, null is not: the TypeScript SDK rejects the whole tools/list response, so SDK-based clients see no tools at all.
  • files.read and files_read in one server. Cursor, VS Code and OpenCode replace . with _, so the two collide and one tool disappears.
  • Long tool names. Cursor, VS Code and Gemini CLI count their server prefix and then truncate, so the model sees a mangled name.
  • A large input schema. Codex compacts schemas over 5,000 characters and strips your descriptions first.
  • A root-level anyOf without "type": "object". Cline fails to register it and drops every tool from the server.
  • Structured results without a text copy. Gemini CLI, Goose and Continue only pass text to the model, so it gets an empty result.
  • Logs on stdout. SDK clients drop the lines, and a log write without a newline corrupts the next protocol message.
  • OAuth metadata without PKCE S256. Spec-following clients must refuse to connect, and ChatGPT does.
  • An outdated SDK. mcp 1.27.2 when 2.2.0 is out: you get the exact upgrade command for your package manager, and upgrade --apply can run it and re-check the server.

Commands

| Command | Short | What it does | |---|---|---| | check (default) | | Connect to a server and report issues per client | | upgrade | up | Find the server's MCP SDK, compare with the latest release, print or apply the upgrade | | diff <before> <after> | | Compare two snapshots: breaking changes and new compatibility failures | | call <tool> | | Call one tool and check its result | | oauth login \| status \| logout | | Log in to an OAuth-protected server and reuse the token | | fix <snapshot> | | Apply safe mechanical fixes to tool definitions | | explain <CHECK_ID> | ex | Why a check exists, its sources, and the client facts it uses | | list-checks, list-clients | checks, clients | Print all checks, or the client profiles and how fresh their facts are |

Every option also has a short form (for example -u for --url, -m for --version-matrix); see the tables below or mcpkit <command> --help.

Guides

Checking a server

# stdio: pass the full command after --
mcpkit -- node dist/server.js
mcpkit -- uv run server.py
mcpkit --env API_KEY=test -- npx -y @acme/mcp-server

# Streamable HTTP
mcpkit --url https://example.com/mcp
mcpkit --url https://example.com/mcp --header "Authorization: Bearer $TOKEN"

# Only some clients (ids or aliases such as claude, vscode, gemini)
mcpkit --clients cursor,vscode,codex -- node dist/server.js

# Also try every published protocol version, and call read-only tools
mcpkit --version-matrix --probe-calls -- node dist/server.js

# Save what the server exposed and re-check it later without running it
mcpkit --save-snapshot snap.json -- node dist/server.js
mcpkit --from-snapshot snap.json

| Option | Short | Default | | |---|---|---|---| | --url <url> | -u | | Streamable HTTP endpoint instead of a stdio command | | --clients <list> | -c | all | Comma-separated client ids or aliases | | --format <pretty\|md\|json\|github> | -f | pretty in a terminal, md otherwise | pretty is the coloured terminal view; json follows schema/report.schema.json; github prints workflow annotations and a job summary | | --verbose | -v | | Also show informational notes in the terminal view | | --out <file> | -o | stdout | Write the report to a file | | --fail-on <spec> | -F | error | error, warn, none, or check ids, globs and areas (TOOL_*,auth) | | --timeout <ms> | -t | 10000 | Per request; startup gets twice this | | --env KEY=VAL | -e | | Environment for the stdio server (repeatable) | | --header "Name: Value" | -H | | HTTP header for --url (repeatable) | | --cwd <dir> | -C | . | Working directory for the stdio server | | --oauth | -A | | Use credentials from oauth login (with --url) | | --probe-calls | -p | | Call tools that declare readOnlyHint: true and check their results | | --version-matrix | -m | | Also connect with each handshake version (2024-11-05 to 2025-11-25) | | --save-snapshot <file> | -s | | Save what the server exposed | | --from-snapshot <file> | -r | | Re-check a saved snapshot instead of connecting | | --no-auth-probe | -N | | Skip the unauthenticated OAuth discovery requests | | --offline | -O | | Don't look up the latest SDK versions | | --no-color | | | Plain output (also respects NO_COLOR; FORCE_COLOR forces colour) |

Short flags combine like any CLI, for example mcpkit -mp -c cursor,vscode -- node dist/server.js.

In a terminal you get a coloured report with one line per client, grouped findings (the same problem across several clients shows once), clickable sources and a PASS or FAIL line at the end; informational notes are folded away unless you add -v. When the output is piped or written with -o, it's Markdown instead, so CI logs and files stay clean.

Exit codes: 0 nothing matched --fail-on, 1 something did, 2 usage error, 3 the server could not be started or reached.

What it sends to your server: initialize, the tools, resources, resource templates and prompts list methods, resources/read for UI resources that tools link to, and a separate 2026-07-28 session (server/discover and the same list methods). Over HTTP it also makes one unauthenticated initialize POST and GETs the OAuth well-known URLs. For stdio servers it reads the project's manifest and lockfiles to find the MCP SDK, and looks up the latest SDK release on the package registry (skip with --offline).

Tool calls happen only when you ask. --probe-calls calls tools that explicitly declare readOnlyHint: true, never ones that also claim to be destructive or are named like writes (for example place_order), with the minimal arguments their schema requires. call <tool> calls exactly the tool you name:

mcpkit call get_quote --args '{"symbol":"INFY"}' -- node dist/server.js

Keeping the SDK current

mcpkit upgrade -- node dist/server.js            # what to upgrade, and the command
mcpkit upgrade --apply -- node dist/server.js    # run it, then re-check the server
mcpkit upgrade --apply --major -- node dist/server.js

Options: -a, --apply, -M, --major, -d, --dir <path>, -C, --cwd <dir>, -e, --env KEY=VAL, -O, --offline, -t, --timeout <ms>. up is short for upgrade.

upgrade finds the server's project from its command and working directory and detects the MCP SDK it uses: @modelcontextprotocol/sdk, @modelcontextprotocol/server, mcp-use, fastmcp and others on npm, mcp and fastmcp on PyPI, the Go SDKs and rmcp. It reads the installed version (node_modules, the virtualenv, lockfiles), looks up the latest release, and prints the command for the package manager the project uses (npm, pnpm, yarn, bun, uv, poetry, pipenv, pip, go or cargo).

--apply runs minor upgrades, and major ones only with --major. It updates == pins in requirements files, and when you pass the server command it checks the server before and after, then reports the protocol change and any breaking changes to its tools. Moving from @modelcontextprotocol/sdk v1 to the v2 packages is a migration, so it is explained but never applied automatically.

check uses the same detection: when a server negotiates an old protocol version, the finding includes the exact upgrade command.

Comparing versions

mcpkit check --save-snapshot before.json -- node old/server.js
mcpkit check --save-snapshot after.json -- node dist/server.js
mcpkit diff before.json after.json    # exits 1 on breaking changes

Options: -F, --fail-on <breaking|any|none>, -f, --format <md|json|github>, -o, --out <file>.

Breaking: removed tools, resources, templates, prompts or capabilities; new required arguments; arguments that become required or change type; removed enum values; removed or no-longer-guaranteed output fields; and compatibility errors that are new in the second snapshot. Description and annotation changes are reported as notable.

OAuth-protected servers

mcpkit oauth login --url https://example.com/mcp
mcpkit check --oauth --url https://example.com/mcp
mcpkit oauth status
mcpkit oauth logout --url https://example.com/mcp

Options for oauth login: -u, --url, -i, --client-id, -k, --client-secret, -P, --callback-port, -m, --client-metadata-url, -s, --scope, -n, --no-browser, -t, --timeout.

oauth login runs the flow MCP clients use and reports each step: discovery, client registration (Client ID Metadata Documents, Dynamic Client Registration, or --client-id for a pre-registered client), the authorization request (PKCE S256 and resource), the callback (state and iss), the token exchange, and an authenticated tools/list. Credentials are stored in ~/.config/mcpkit/oauth.json, readable only by you. Runs with --oauth never register a client or open a browser; they refresh stored tokens or tell you to log in again.

Fixing tool definitions

mcpkit fix snap.json --out fixed-tools.json
mcpkit fix snap.json --rename    # also rename tools that clients would rewrite

Options: -o, --out <file>, -j, --json, -r, --rename.

fix never invents content. It removes description: null, adds a missing inputSchema, sets an object-shaped root to "type": "object", drops required entries that aren't properties, and removes invalid required values and empty enums. Anything that needs judgement, such as a non-object root schema or a missing description, is listed as a TODO. Tool definitions live in your code, so apply the listed changes there.

In CI

Copy examples/github-workflow.yml. With --format github, findings show up as annotations on the pull request, and diff can gate releases on breaking changes.

How results are decided

  • FAIL and WARN only for what the server was seen doing: a null description, a colliding name, a missing OAuth field. A client limitation your server doesn't trigger is never reported as a failure.
  • Client-specific results cite a source. If a client's behaviour is unknown, the check doesn't guess; it passes.
  • INFO covers things worth knowing that break nothing today, such as no server/discover yet.
  • Rules borrowed from other linters were checked against client source before being adopted. Most were wrong or unsourced and were left out.

Example rows from a report:

| Status | Check | Subject | Message |
|---|---|---|---|
| ❌ FAIL | `TOOL_NAME_CLIENT_COLLISION` | files.read, files_read | "files.read" and "files_read" both become "files_read" in Cursor, so only one of them is usable. |
| ⚠️ WARN | `SCHEMA_TOO_LARGE` | create_update_strategy | inputSchema is about 12540 characters; OpenAI Codex CLI compacts schemas over 5000. |
| ⚠️ WARN | `SDK_OUTDATED` | mcp | mcp 1.27.2 → 2.2.0 (major version: check the changelog for breaking changes); adds protocol 2026-07-28. |

Clients

| Client | stdio | HTTP | Tool list refresh | Resources | Prompts | OAuth | CIMD | MCP Apps UI | Limits | |---|---|---|---|---|---|---|---|---|---| | Claude Desktop / claude.ai | ✅ | ✅ | ? | ✅ | ✅ | ✅ | ✅ | ✅ | chars [A-Za-z0-9_-] (unknown) | | Claude Code | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | prefix mcp__{server}__; name ≤ 128 (unknown); chars [A-Za-z0-9_-] (unknown); descriptions ≤ 2048 chars | | ChatGPT (Apps SDK / connectors) | ⚠️ | ✅ | ❌ | ? | ? | ✅ | ✅ | ✅ | none | | Cursor | ✅ | ✅ | ⚠️ | ✅ | ✅ | ✅ | ? | ✅ | prefix {server}; name ≤ 60 (truncateWithHash); chars [A-Za-z0-9_-] (replace) | | VS Code (GitHub Copilot) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | prefix mcp_{server}_; name ≤ 64 (truncate); chars [A-Za-z0-9_-] (replace); ≤ 128 tools | | OpenCode | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ? | ❌ | prefix {server}_; chars [A-Za-z0-9_-] (replace) | | OpenAI Codex CLI | ✅ | ✅ | ❌ | ✅ | ? | ✅ | ✅ | ? | prefix mcp__{server}__; name ≤ 128 (truncateWithHash); chars [A-Za-z0-9_] (replaceUnique) | | Gemini CLI | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ? | ❌ | prefix mcp_{server}_; name ≤ 63 (truncateMiddle); chars [A-Za-z0-9_.:-] (replace) | | Cline | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ? | ? | prefix {server}__; name ≤ 64 (truncateWithHash); chars [A-Za-z0-9_-] (truncateWithHash) | | Goose | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | prefix {server}__ | | Continue | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ? | ⚠️ | prefix {server}_ | | Windsurf / Devin Desktop | ✅ | ✅ | ? | ✅ | ✅ | ✅ | ? | ? | ≤ 100 tools |

✅ supported · ❌ not supported · ⚠️ partial or unreliable · ? unknown. Every mark links to its source. Facts verified 2026-09-26 to 2026-09-27; run mcpkit list-clients for details.

Client behaviour changes quickly. If a fact is wrong or stale, please open an issue or PR against src/profiles/ with a source.

Checks

transport

| Check | What it verifies | |---|---| | TRANSPORT_CONNECT_FAILED | The server starts and answers initialize | | TRANSPORT_AUTH_REQUIRED | Notes when an HTTP server needs credentials before initialize | | TRANSPORT_STDOUT_POLLUTION | stdout carries only JSON-RPC messages | | TRANSPORT_SLOW_STARTUP | initialize answers within 5s of launch | | TRANSPORT_SLOW_LIST | list requests answer within 3s | | TRANSPORT_STDERR_ERRORS | stderr shows no errors during the session |

protocol

| Check | What it verifies | |---|---| | PROTOCOL_MODERN_ONLY | Server still accepts the initialize handshake most clients use | | PROTOCOL_DISCOVER_MISSING | Server implements server/discover (required from protocol 2026-07-28) | | PROTOCOL_MODERN_RESULT_TYPE | 2026-07-28 results carry a valid resultType | | PROTOCOL_MODERN_CACHE_FIELDS | 2026-07-28 list and discover results carry ttlMs and cacheScope | | PROTOCOL_MODERN_SERVERINFO | 2026-07-28 results identify the server in _meta | | PROTOCOL_MODERN_VERSION_ERROR | 2026-07-28 servers reject unsupported versions with UnsupportedProtocolVersion (-32022) | | PROTOCOL_MODERN_SURFACE_DIFFERS | Tools are the same over the initialize handshake and 2026-07-28 | | PROTOCOL_VERSION_UNSUPPORTED | Negotiated protocol version is one the SDK supports | | PROTOCOL_VERSION_OLD | Server speaks the latest protocol version | | PROTOCOL_VERSIONS_REJECTED | Server handles every published protocol version clients may request (--version-matrix) | | PROTOCOL_VERSION_SURFACE_DIFFERS | The tool list is the same whichever protocol version a client negotiates | | PROTOCOL_SERVERINFO_MISSING | initialize returns serverInfo with name and version | | PROTOCOL_CAPABILITY_UNDECLARED | Every feature the server serves is declared in capabilities | | PROTOCOL_CAPABILITY_BROKEN | Every declared capability's list method works | | PROTOCOL_PAGINATION_BROKEN | List pagination terminates |

tools

| Check | What it verifies | |---|---| | TOOL_NONE | A server declaring tools exposes at least one | | TOOL_NAME_INVALID | Tool names match the MCP spec format (A-Z a-z 0-9 _ - ., 1-128 chars) | | TOOL_NAME_DUPLICATE | Tool names are unique | | TOOL_NAME_CLIENT_CHARS | Tool names use only characters each client accepts as-is | | TOOL_NAME_TOO_LONG | Prefixed tool names fit each client's length limit | | TOOL_NAME_CLIENT_COLLISION | Tool names stay unique after a client renames or truncates them | | TOOL_COUNT_OVER_LIMIT | Tool count fits each client's limit | | TOOL_DESCRIPTION_MISSING | Every tool has a non-empty description | | TOOL_DESCRIPTION_SHORT | Tool descriptions are at least 20 characters | | TOOL_DESCRIPTION_TRUNCATED | Tool descriptions and server instructions fit each client's length limit | | TOOL_STRUCTURED_OUTPUT_HANDLING | Notes how each client passes structured tool output to the model | | TOOL_ANNOTATIONS_CONFLICT | Tool annotations are consistent with each other and with the tool's name | | TOOL_TITLE_MISSING | Tools have a human-readable title | | TOOL_ANNOTATIONS_MISSING | Read-only tools declare readOnlyHint |

schema

| Check | What it verifies | |---|---| | SCHEMA_MISSING | Every tool has an inputSchema object | | SCHEMA_NOT_OBJECT | inputSchema and outputSchema have type "object" at the root | | SCHEMA_INVALID | Schemas are valid JSON Schema | | SCHEMA_REQUIRED_UNKNOWN | required only lists declared properties | | SCHEMA_TOP_LEVEL_COMBINATOR | inputSchema has no root-level oneOf/anyOf/allOf/not | | SCHEMA_UNSUPPORTED_KEYWORD | Schemas avoid keywords a client rejects or drops | | SCHEMA_PROPERTY_NAME_REJECTED | Top-level input property names match each client's rules | | SCHEMA_EMPTY_ENUM | enum lists have at least one value | | SCHEMA_ROOT_COMBINATOR_CLIENT | Root-level oneOf/anyOf/allOf is shaped the way each client can handle | | SCHEMA_TYPE_ARRAY_CLIENT | Type arrays (e.g. ["string","null"]) avoided for clients that fail on them | | SCHEMA_KEYWORDS_DROPPED | Notes schema constraints a client removes before the model sees them | | SCHEMA_TOO_LARGE | inputSchema fits each client's size limit | | SCHEMA_PROPERTY_NO_TYPE | Every input property declares a type | | SCHEMA_TOO_DEEP | inputSchema nests at most 5 levels |

discovery

| Check | What it verifies | |---|---| | DISCOVERY_TOOLS_LIST_CHANGED | Notes clients that ignore tools/list_changed when the server declares it | | DISCOVERY_RESOURCES_LIST_CHANGED | Notes clients that ignore resources/list_changed when the server declares it | | DISCOVERY_PROMPTS_LIST_CHANGED | Notes clients that ignore prompts/list_changed when the server declares it | | DISCOVERY_RESOURCES_UNSUPPORTED | Clients without resources support are flagged when the server exposes resources | | DISCOVERY_PROMPTS_UNSUPPORTED | Clients without prompts support are flagged when the server exposes prompts |

resources

| Check | What it verifies | |---|---| | RESOURCE_URI_INVALID | Resource URIs are absolute and parse | | RESOURCE_URI_DUPLICATE | Resource URIs are unique | | RESOURCE_NAME_MISSING | Resources and templates have a name (required by the spec) | | RESOURCE_MIME_MISSING | Resources declare a mimeType | | RESOURCE_TEMPLATE_INVALID | Resource templates are valid RFC 6570 URI templates |

ui

| Check | What it verifies | |---|---| | UI_RESOURCE_MIME | UI resources use text/html;profile=mcp-app | | UI_RESOURCE_SCHEME | UI resources use the ui:// scheme | | UI_TOOL_LINK_BROKEN | UI resources that tools link to can be read | | UI_TOOL_LINK_KEY | Tools link UI with _meta.ui.resourceUri | | UI_CSP_LOCAL_ORIGIN | A remote server's UI resources don't point their CSP at localhost | | UI_CSP_INSECURE | UI resource CSP origins use HTTPS | | UI_VISIBILITY_INVALID | Tool _meta.ui.visibility only uses "model" and "app" | | UI_UNSUPPORTED | Clients that don't render MCP Apps are flagged when tools rely on UI |

auth

| Check | What it verifies | |---|---| | AUTH_CHALLENGE_MISSING | 401 responses carry a WWW-Authenticate challenge | | AUTH_PRM_MISSING | Protected resource metadata (RFC 9728) is discoverable | | AUTH_PRM_INVALID | Protected resource metadata names the resource and its authorization servers | | AUTH_ASM_MISSING | Authorization server metadata (RFC 8414 / OIDC) is discoverable | | AUTH_ISSUER_MISMATCH | Authorization server metadata issuer matches the advertised issuer | | AUTH_PKCE_S256_MISSING | Authorization server advertises PKCE S256 | | AUTH_CLIENT_REGISTRATION | Clients can register themselves (CIMD or DCR) | | AUTH_INSECURE_URL | Remote servers and OAuth endpoints use HTTPS |

calls

| Check | What it verifies | |---|---| | CALL_RESULT_INVALID | tools/call results match the MCP result schema | | CALL_OUTPUT_SCHEMA_MISMATCH | Tools with an outputSchema return matching structuredContent | | CALL_STRUCTURED_WITHOUT_TEXT | Structured results also carry a text copy | | CALL_RESULT_TOO_LARGE | Tool results fit each client's result size limit | | CALL_SLOW | Tool calls finish well within each client's timeout | | CALL_FAILED | Reports tool calls that errored |

sdk

| Check | What it verifies | |---|---| | SDK_OUTDATED | The server's MCP SDK is the latest release |

Protocol versions

Every published revision is covered:

  • 2024-11-05 to 2025-11-25 (the initialize handshake, which current clients use): the checks run over the latest version the server supports. --version-matrix also connects once per revision and reports versions the server rejects, answers incorrectly, or serves different tools on.
  • 2026-07-28 (stateless): always probed in a separate session with the _meta envelope and the required HTTP headers. For servers that speak it, the tool checks resultType, ttlMs and cacheScope, serverInfo in _meta, the -32022 unsupported-version error, and whether the tools match the handshake's. Servers that only speak 2026-07-28 get every check run over the new protocol.

With --version-matrix the report shows a line like Protocol versions: 2024-11-05 ✅ · 2025-03-26 ✅ · 2025-06-18 ✅ · 2025-11-25 ✅ · 2026-07-28 ❌.

Contributing

Corrections to client facts are the most useful contribution: client behaviour changes often. See CONTRIBUTING.md for setup, how facts must be sourced, and how to add a check.

npm install
npm test          # unit, fixture and end-to-end tests
npm run readme    # regenerate the Clients and Checks sections

Security issues: see SECURITY.md.

License

MIT