MCP Commons

API

A small HTTP API for listing tools, searching them and publishing your own releases. Built so you can wire publishing into CI and stop doing it by hand.

Authentication

Every request needs an API key, the same kind you mint in your dashboard. Send it as a bearer token. Keys are stored hash-only and shown once, so keep yours in a secret manager.

Authorization: Bearer <your-api-key>

The API is CORS-open because a key is never attached automatically by a browser. Never ship a key in client-side code: anyone who reads it can publish as you.

What it can do

Deliberately small. It lists tools, searches tools, creates a listing and publishes a version of a listing you own. It cannot approve a review, publish without review, delist anything, read another account's data or touch billing. Those stay in the dashboard.

EndpointDoes
GET /api/v1/toolsList and search published tools
POST /api/v1/toolsCreate a listing (as a draft)
POST /api/v1/tools/{slug}/versionsPublish a version for review

List and search

One endpoint covers both. Combine q with any filter to narrow the results.

curl "https://mcpcommons.com/api/v1/tools?q=npm&open_source=true" \
  -H "Authorization: Bearer $MCPC_API_KEY"
ParameterNotes
qMatches name, summary, slug and keywords
categoryA category slug, e.g. developer-tools
transportstdio or http
open_sourcetrue for recognized OSI licenses only
no_setuptrue for tools needing no keys or services
limit, offset1 to 100, default 25

Create a listing

Creates a draft. It stays private until a version passes review, so this publishes nothing on its own. Requires slug and name, plus link_out_url for link-out listings.

curl -X POST "https://mcpcommons.com/api/v1/tools" \
  -H "Authorization: Bearer $MCPC_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "slug": "package-inspector",
    "name": "Package Inspector",
    "summary": "Deep insight into npm packages",
    "listing_class": "third_party_free",
    "link_out_url": "https://github.com/you/package-inspector",
    "category": "developer-tools",
    "tags": ["npm", "security"]
  }'

Publish a version

Queues the same AI security review the dashboard uses and returns 202. A pass publishes automatically; a flag or fail holds the listing for a human. Versions are immutable, so reusing one returns 409.

This mirrors the creator form field for field. version, repo and tools are required; every other field is optional and, when present, updates the listing the same way the form does. Always send tools, the declared tool surface the review checks your code against. Omitting it is rejected rather than silently publishing an empty surface.

curl -X POST "https://mcpcommons.com/api/v1/tools/package-inspector/versions" \
  -H "Authorization: Bearer $MCPC_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "version": "1.2.0",
    "repo": "you/package-inspector",
    "ref": "v1.2.0",
    "language": "typescript",
    "transport": "stdio",
    "egress_allowlist": ["registry.npmjs.org", "api.osv.dev"],
    "tools": [
      { "name": "inspect_package", "description": "Full npm metadata" }
    ]
  }'

The API applies exactly the same requirements as the creator form:

Publishing from GitHub Actions

This is what the API was built for. Publish to MCP Commons every time you cut a release. Add your key as the MCPC_API_KEY repository secret, set SLUG to your listing's slug, then commit this as .github/workflows/mcp-commons.yml.

name: Publish to MCP Commons

on:
  release:
    types: [published]

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - name: Submit the release for review
        env:
          MCPC_API_KEY: ${{ secrets.MCPC_API_KEY }}
          SLUG: your-tool-slug
        run: |
          # Strip a leading "v" so v1.2.0 becomes 1.2.0
          VERSION="${GITHUB_REF_NAME#v}"

          RESPONSE=$(mktemp)
          STATUS=$(curl -sS -o "$RESPONSE" -w '%{http_code}' \
            -X POST "https://mcpcommons.com/api/v1/tools/$SLUG/versions" \
            -H "Authorization: Bearer $MCPC_API_KEY" \
            -H "Content-Type: application/json" \
            -d @- <<JSON
          {
            "version": "$VERSION",
            "repo": "$GITHUB_REPOSITORY",
            "ref": "$GITHUB_REF_NAME",
            "language": "typescript",
            "transport": "stdio",
            "egress_allowlist": ["api.example.com"],
            "tools": [
              { "name": "your_tool", "description": "What it does" }
            ]
          }
          JSON
          )

          cat "$RESPONSE"
          if [ "$STATUS" != "202" ]; then
            echo "::error::MCP Commons rejected the release (HTTP $STATUS)"
            exit 1
          fi
          echo "::notice::Queued for security review"

A 202 means the version is queued. Watch the listing page for the verdict, or wait for the email we send when the review finishes. The step fails the build on anything else, so a rejected release shows up red in CI instead of passing silently.

Rate limits

Limits are per account, not per key, so extra keys do not raise your allowance. Reads are generous. Writes are tight, because each one queues a review, fetches a repo and can change what the public sees.

EndpointLimit
GET /api/v1/tools120 per minute
POST /api/v1/tools/{slug}/versions10 per hour
POST /api/v1/tools5 per hour

Each bucket is independent, so exhausting your publish budget never blocks reads. Every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset. Going over returns 429 with Retry-After in seconds.

Errors

Errors are JSON with a stable error code. Validation errors also name the offending field. Unknown body fields are rejected rather than ignored, so a typo in a CI config fails loudly instead of publishing something you did not intend.

{ "error": "invalid_body", "message": "repo is not valid (owner/repo).", "field": "repo" }
StatusCodeMeaning
400invalid_queryBad query parameter
401unauthorizedMissing key
401invalid_keyUnknown or revoked key
402payouts_requiredPaid tool without connected payouts
402payment_method_requiredHosted tool without a card on file
403github_not_connectedConnect GitHub first
404no_such_listingNo listing with that slug on your account
409slug_takenThat slug is in use
409version_existsVersions are immutable
422invalid_bodyBody failed validation
429rate_limitedOver budget, see Retry-After

Questions

Open a thread from Support. You will need to be signed in.