mcprepo.ai

Published on

- 12 min read

A Practical Guide to MCP Repository Metadata and Versioning

Image of A Practical Guide to MCP Repository Metadata and Versioning

A Practical Guide to MCP Repository Metadata and Versioning

You can’t trust what you can’t describe, and you can’t upgrade what you can’t version. MCP repositories live or die on those two facts.

This guide walks through how to structure metadata and versioning in Model Context Protocol (MCP) repositories so tools, models, and humans can actually depend on them—safely and predictably.


1. Why MCP Repository Metadata Matters

An MCP repository is more than a code bucket. It’s a catalog of machine-usable capabilities: tools, resources, prompts, schemas, and configuration that a model or orchestration layer can load on demand.

Metadata is how you answer three core questions, programmatically and reliably:

  1. What is this?
  2. Can I trust it?
  3. Is it compatible with my environment and version?

If your metadata is weak or inconsistent, you’ll face:

  • Tools that appear in one environment and vanish in another.
  • Breaking changes shipped as “minor upgrades”.
  • Confusing duplication (“Which github-search tool is the current one?”).
  • Hard-to-debug failures when model clients auto-load or auto-update MCP repositories.

Investing in consistent, explicit metadata and versioning makes the rest of your MCP journey survivable.


2. The Core Metadata Surface of an MCP Repository

While implementations will evolve, any serious MCP repository should standardize a top-level metadata file—often something like:

  • mcp.json
  • mcp.config.json
  • mcp.yaml
  • or a similar canonical configuration format.

At a minimum, this file should cover:

  • Repository identity.
  • Versioning and compatibility.
  • Ownership and trust.
  • Capabilities.
  • Distribution and installation hints.

Let’s break this down.

2.1 Identity: Naming Things So They Don’t Hurt Later

Identity metadata should feel boring. That’s good. It should include:

  • id: a stable, machine-friendly identifier
    • Example: "id": "com.acme.mcp.github"
    • Treat this as immutable once published.
  • name: human-readable label
    • Example: "name": "Acme GitHub MCP Tools"
  • description: short, precise, non-marketing explanation
    • Example: "description": "Tools for querying repositories, issues, and pull requests via GitHub's REST and GraphQL APIs."
  • homepage or docs: where a human can go to learn more
    • Example: "homepage": "https://acme.dev/mcp/github"

Use globally unique, reverse-DNS-style IDs where possible (similar to Java packages or Android app IDs). This avoids collision when multiple vendors release similarly named repositories.

2.2 Ownership, Contact, and Trust Signals

If a model client auto-discovers MCP repositories, it needs hints about who stands behind this code:

  • publisher: organisation or individual
    • "publisher": "Acme Corp"
  • contact: support or security contact
    • "contact": "security@acme.dev"
  • license: SPDX identifier or clear license text reference
    • "license": "Apache-2.0"
  • source: canonical source repository
    • "source": "https://github.com/acme/mcp-github"

You can go further with:

  • signingKeys: public keys for repository signatures.
  • verification: references to transparency logs or signing services.
  • securityPolicy: link to a security policy or advisory page.

These fields let infrastructure enforce policies such as:

  • “Only use MCP repositories signed by keys we trust.”
  • “Only auto-install repositories from publishers on an allowlist.”

2.3 Platform, Runtime, and Model Compatibility

MCP repositories often depend on a runtime (Node, Python, Docker, serverless) and may assume features or size limits for model context.

Provide clear compatibility metadata:

{
  "runtime": {
    "type": "node",
    "version": ">=18.0.0"
  },
  "mcp": {
    "protocolVersion": ">=1.1.0 <2.0.0",
    "features": ["tools", "resources", "events"]
  },
  "models": [
    {
      "pattern": "gpt-4.*",
      "constraints": {
        "maxContextTokens": 128000,
        "supportsStreaming": true
      }
    }
  ]
}

Key ideas:

  • Runtime compatibility
    • type: e.g. node, python, docker, binary, http.
    • version: range or minimum version.
  • MCP protocol compatibility
    • Use ranges (e.g. >=1.1.0 <2.0.0) rather than a single pinned version.
  • Model hints
    • Not hard requirements, but guidance to orchestrators:
      • Minimum context size for safe use.
      • Whether tools require streaming or JSON mode.
      • Any known incompatibilities.

This unlocks smarter scheduling:

  • Tooling can decide not to load a heavy repository when using a small-context model.
  • Gateways can warn: “This MCP repository may exceed your model’s context window.”

3. Structuring Capability Metadata

A repository is a bundle of capabilities. For MCP, these usually fall into:

  • Tools
  • Resources
  • Config / settings
  • Optional background services or event feeds

Metadata should make each capability discoverable, type-safe, and explainable to both models and operators.

3.1 Tools: Input/Output Contracts in the Open

Each tool in your repository should have a machine-readable description. In JSON Schema–style, something like:

{
  "tools": [
    {
      "name": "search_issues",
      "version": "1.2.0",
      "description": "Search GitHub issues by repository, labels, and status.",
      "inputSchema": { /* JSON Schema */ },
      "outputSchema": { /* JSON Schema */ },
      "examples": [
        {
          "summary": "Find open bugs labeled 'priority: high'",
          "input": {
            "repo": "acme/mcp-github",
            "labels": ["bug", "priority: high"],
            "state": "open"
          }
        }
      ],
      "rateLimits": {
        "perMinute": 30,
        "burst": 10
      },
      "auth": {
        "type": "oauth2",
        "scopes": ["repo", "read:org"]
      },
      "deprecated": false,
      "replacedBy": null
    }
  ]
}

Important pieces:

  • name: stable tool identifier within the repository.
  • version: per-tool semantic version (more on this later).
  • inputSchema / outputSchema: strict, validated schema definition.
  • examples: canonical usage hints; models and evaluation tools love these.
  • rateLimits: so orchestrators can pace calls.
  • auth: what secrets or scopes are needed.
  • deprecated / replacedBy: soft migrations rather than surprise deletions.

3.2 Resources: Data, Files, and Streams

Resources cover things like:

  • Knowledge bases.
  • Configuration files.
  • File system roots.
  • Remote indexes.

Describe each resource with:

{
  "resources": [
    {
      "id": "kb.product_docs",
      "name": "Product Documentation",
      "description": "Indexed product docs with sections and change history.",
      "type": "search-index",
      "format": "markdown",
      "location": "s3://acme-docs-index/prod",
      "version": "2025.10.3",
      "access": {
        "requiresAuth": true,
        "allowedRoles": ["support", "engineering"]
      }
    }
  ]
}

Key points:

  • Use logical IDs (kb.product_docs) that remain constant even as backing storage evolves.
  • Distinguish between resource version and repository version.
  • Capture access control assumptions: many MCP clients will need to respect them.

3.3 Configuration and Settings

Operators need to see what a repository expects from its environment. Think:

  • API keys and secrets.
  • Optional feature flags.
  • Per-tenant configuration.

Represent these in structured metadata:

{
  "config": {
    "required": [
      {
        "key": "GITHUB_TOKEN",
        "description": "GitHub personal access token with repo and read:org scopes.",
        "type": "string",
        "secret": true
      }
    ],
    "optional": [
      {
        "key": "DEFAULT_ORG",
        "description": "Fallback organization for repository discovery.",
        "type": "string",
        "secret": false,
        "default": "acme"
      }
    ]
  }
}

This allows:

  • Automatic UI generation for config screens.
  • Pre-flight checks before loading the repository.
  • Safer secret handling (no more “stuff everything in a .env and hope”).

4. Designing a Versioning Strategy That Holds Up

Now to the part that saves you from upgrade chaos: versioning.

Developers instinctively reach for semantic versioning (SemVer). That’s a good start, but MCP repositories have a few twists:

  • There is a repository version and often per-tool versions.
  • Clients may auto-upgrade without human intervention.
  • Models rely on contracts inferred from examples and prior behavior.

4.1 The Three Version Layers

Think in three layers:

  1. Repository version
    • Overall package version.
    • Example: "version": "1.4.0".
  2. Protocol compatibility range
    • "mcp.protocolVersion": ">=1.1.0 <2.0.0".
  3. Tool/resource versions
    • Each tool or major resource can have its own version.

The repository version should follow SemVer rules driven by client-visible change:

  • MAJOR: breaking changes to:
    • Tool names or removal of previously stable tools.
    • Input/output schemas in a backward-incompatible way.
    • Auth requirements (e.g. dropping a supported auth method).
    • Critical behavioral contracts (e.g. “this tool no longer returns streaming output where it used to”).
  • MINOR: backward-compatible additions:
    • New tools.
    • New optional fields in schemas.
    • Performance improvements that don’t alter contract.
  • PATCH: bug fixes that do not affect schema or semantics:
    • Fixing a bug that previously crashed on valid input.
    • Correcting documentation and examples.
    • Tightening validation to match documented behavior.

4.2 When Tool-Level Versioning Helps

Per-tool versioning is valuable when:

  • You support long-lived automation that pins to a specific tool version.
  • You foresee multiple variants of a tool living side by side.

Patterns that work:

  • Use tool.version as a semantic contract for just that tool’s API.
  • Keep the repository version for the bundle as a whole.
  • Allow clients to request:
    • “Load search_issues version 1.x” or
    • “Any compatible search_issues with version ^1.2.0”.

This gives you flexibility to:

  • Experiment with search_issues 2.0.0 while keeping 1.x stable.
  • Encourage gradual migration rather than forced wholesale upgrades.

5. Stable Contracts: Schema and Behavior

Version numbers only mean something if they track real, stable contracts.

5.1 Forward- and Backward-Compatible Schema Changes

Follow some basic rules:

  • Backward compatible (safe for MINOR):
    • Adding a new field that is optional and has a default.
    • Expanding an enum with a new value if callers can ignore it.
    • Extending oneOf or anyOf in a non-breaking way.
  • Backward incompatible (requires MAJOR):
    • Renaming or removing fields used by callers.
    • Tightening validation so previously valid payloads now fail.
    • Changing field types (e.g. string to array).

Document these explicitly in your metadata:

{
  "changelog": [
    {
      "version": "1.3.0",
      "date": "2026-02-10",
      "changes": [
        {
          "type": "schema",
          "scope": "tool:search_issues",
          "compatibility": "backward-compatible",
          "description": "Added optional 'assignee' field to filter issues by assignee."
        }
      ]
    }
  ]
}

Even if not all clients read the changelog, it’s invaluable for human operators and automated QA.

5.2 Behavioral Contracts Beyond Schema

Schema isn’t enough. You also have behavioral guarantees:

  • Ordering of results.
  • Default limits and timeouts.
  • Error handling and retry semantics.
  • Idempotency.

Define these somewhere machine-readable or at least stably documented:

{
  "behavior": {
    "search_issues": {
      "ordering": "sorted by updated_at descending by default",
      "defaultLimit": 50,
      "maxLimit": 200,
      "idempotent": true,
      "timeoutMs": 10000,
      "retries": {
        "max": 2,
        "backoff": "exponential"
      }
    }
  }
}

When you change these in a way that matters to clients (for example, you cut timeoutMs drastically or change the default order), treat it as a contract change and version accordingly.


6. Dependency Metadata and Composition

MCP repositories will increasingly depend on each other. For example:

  • A “sales assistant” repository depends on:
    • CRM tools.
    • Product docs search.
    • HR directory for account ownership.

You should expose dependency metadata to avoid a hidden web of brittle imports.

6.1 Describing Dependencies Explicitly

Use a structure similar to package managers:

{
  "dependencies": {
    "com.acme.mcp.crm": "^2.0.0",
    "com.acme.mcp.product_docs": "~1.3.0"
  },
  "optionalDependencies": {
    "com.acme.mcp.hr_directory": ">=1.0.0 <2.0.0"
  },
  "peerDependencies": {
    "com.acme.mcp.auth": "^1.1.0"
  }
}

Where:

  • dependencies: repository must be present for full functionality.
  • optionalDependencies: repository can enhance behavior but isn’t required.
  • peerDependencies: repository expected to be provided by the host environment (e.g. a shared auth layer).

Model-aware orchestrators can then:

  • Check for missing dependencies early.
  • Suggest installing required peers.
  • Avoid loading incompatible combinations.

6.2 Transitive Metadata and Flattening

If you chain several repositories, their dependency graphs can grow quickly.

Good practice:

  • Keep first-level dependencies short and clearly justified.
  • Split monolith repositories into smaller, focused MCP packages when dependencies balloon.
  • Consider providing a “meta” repository that aggregates lower-level ones:
    • For example, com.acme.mcp.sales_suite that depends on several specific MCP repositories and surfaces a curated set of tools.

7. Publishing, Discovery, and Indexing Metadata

Once your metadata is structured, you need to expose it in ways tooling can discover.

7.1 Standard Locations and Endpoints

Common patterns:

  • Static manifest file at the repository root:
    • mcp.json or mcp.yaml.
  • Well-known URL for remote repositories:
    • https://example.com/.well-known/mcp.json.
  • HTTP discovery endpoint:
    • GET /mcp/manifest returning JSON metadata.

Ensure:

  • The manifest is cacheable (with ETags, Last-Modified, or versioned URLs).
  • The manifest includes content hashes or signatures for integrity if possible.

7.2 Central Indexes vs. Local Catalogs

Environments will likely maintain:

  • A central index (like npm or PyPI for MCP).
  • Enterprise catalogs with curated, approved repositories.

Make your metadata index-friendly:

  • Provide short, clear descriptions.
  • Include categories or tags:
    • "categories": ["developer-tools", "code-search", "issue-tracking"]
  • Expose a minimal “card” view:
    • ID, name, publisher, last updated, key capabilities.

Discovery tools can then show:

  • “All MCP repositories that offer code_search tools.”
  • “Only MCP repositories that support protocol >=1.2.0.”

Image

Photo by NASA on Unsplash


8. Managing Upgrades in the Real World

Version numbers on paper are easy. Live environments full of automations and human users are not.

You need operational patterns that respect metadata and versioning.

8.1 Pinning, Ranges, and Auto-Updates

Clients and orchestrators should support:

  • Pinned versions: exact version, e.g. 1.2.3.
  • Version ranges: ^1.2.0, ~1.3.0, >=2.0.0 <3.0.0.
  • Channels: stable, beta, canary.

Consider a configuration like:

{
  "repositories": [
    {
      "id": "com.acme.mcp.github",
      "version": "^1.3.0",
      "channel": "stable",
      "autoUpdate": true
    }
  ]
}

Tradeoffs:

  • Pinning gives maximum safety but no security or bugfix updates.
  • Ranges allow automatic, controlled updates within a major version.
  • Channels let you test new versions on limited workloads first.

8.2 Deprecation Windows and Sunset Policies

Breaking a contract overnight is a fast way to lose trust in your MCP ecosystem.

Build in:

  • Deprecation flags in metadata:
    {
      "deprecated": true,
      "deprecationDate": "2026-05-01",
      "sunsetDate": "2026-08-01",
      "migrationGuide": "https://acme.dev/mcp/github/migration-1.x-to-2.x"
    }
  • Advance communication of:
    • Which tools or endpoints will go away.
    • Which replacements exist.
    • How behavior will change.

Use metadata to allow tooling to warn:

  • “This MCP repository will sunset in 45 days; update to 2.x.”

8.3 Rollbacks and Safe Releases

When a new version misbehaves, you need a simple way back.

Suggestions:

  • Keep older metadata and artifacts accessible for a defined retention window.
  • Tag releases clearly (v1.4.0, v1.4.0-hotfix1).
  • Use metadata fields like:
    {
      "status": "stable", // other values: "beta", "deprecated", "withdrawn"
      "replacedByVersion": "1.4.1"
    }

If you must withdraw a release:

  • Mark it as "status": "withdrawn" in the index.
  • Update discovery endpoints quickly.
  • Provide remediation notes (e.g. “Do not use 1.4.0 for batch jobs; upgrade to 1.4.1”).

9. Security, Integrity, and Auditability

MCP repositories sit at the intersection of automation and external systems. Security metadata isn’t optional.

9.1 Signing and Integrity Metadata

Provide fields that let clients validate what they download:

{
  "integrity": {
    "manifestHash": "sha256-...",
    "signedBy": [
      {
        "keyId": "acme-mcp-signing-1",
        "signature": "base64-..."
      }
    ],
    "signingCertificates": [
      "https://acme.dev/keys/mcp-signing-1.pem"
    ]
  }
}

This supports:

  • Chain-of-trust validation.
  • Policy like “only load repositories signed by these keys”.

9.2 Permission and Data Access Metadata

For environments dealing with sensitive data, describe:

  • Data categories accessed:
    • "dataCategories": ["source-code", "customer-data", "billing"]
  • Data residency assumptions:
    • "dataResidency": ["eu-west-1", "us-east-1"]
  • Logging behavior:
    • "logs": { "storesPayloads": false, "retentionDays": 30 }

This lets security teams enforce:

  • “No MCP repositories that handle customer-data may be loaded in this sandbox.”
  • “Only EU-only data repositories for EU workloads.”

9.3 Audit Trails and Change History

Maintain an append-only audit log of:

  • Changes in metadata.
  • Published versions and their hashes.
  • Security advisories.

Even a simple JSON-based history file (mcp.history.json) is a big win for:

  • Forensics (“which version was loaded at the time of incident X?”).
  • Compliance documentation.
  • Regression debugging.

10. A Minimal, Coherent Metadata Template

Bringing this together, here’s a compact template that covers the essentials without being unwieldy:

{
  "id": "com.acme.mcp.github",
  "name": "Acme GitHub MCP Tools",
  "description": "Tools for searching and managing GitHub repositories, issues, and pull requests.",
  "version": "1.3.0",
  "publisher": "Acme Corp",
  "contact": "support@acme.dev",
  "license": "Apache-2.0",
  "homepage": "https://acme.dev/mcp/github",
  "source": "https://github.com/acme/mcp-github",

  "mcp": {
    "protocolVersion": ">=1.1.0 <2.0.0",
    "features": ["tools", "resources"]
  },

  "runtime": {
    "type": "node",
    "version": ">=18.0.0"
  },

  "tools": [
    {
      "name": "search_issues",
      "version": "1.2.0",
      "description": "Search GitHub issues with flexible filters.",
      "inputSchema": { "$ref": "./schemas/search_issues_input.json" },
      "outputSchema": { "$ref": "./schemas/search_issues_output.json" },
      "deprecated": false
    }
  ],

  "resources": [
    {
      "id": "kb.repo_readme_cache",
      "name": "Repository README Cache",
      "type": "document-store",
      "format": "markdown",
      "location": "s3://acme-mcp/github-readmes/prod",
      "version": "2026.02.01"
    }
  ],

  "config": {
    "required": [
      {
        "key": "GITHUB_TOKEN",
        "description": "GitHub token with repo and read:org scopes.",
        "type": "string",
        "secret": true
      }
    ]
  },

  "dependencies": {
    "com.acme.mcp.auth": "^1.1.0"
  },

  "categories": ["developer-tools", "code", "github"],
  "tags": ["github", "issues", "repos"],

  "integrity": {
    "manifestHash": "sha256-...",
    "signedBy": []
  }
}

You can extend this template as your ecosystem matures, but even this baseline delivers:

  • Clear identity.
  • Honest compatibility.
  • Discoverable capabilities.
  • A clean starting point for version-aware automation.

11. Practical Recommendations to Adopt Today

To close, here are concrete steps to bring order to your MCP repository metadata and versioning:

  1. Choose a canonical manifest format
    • Decide on mcp.json (or similar) and stick to it.
  2. Define a repository ID scheme
    • Reverse-DNS, unique per repository, immutable once published.
  3. Enforce semantic versioning rules
    • Document what counts as MAJOR/MINOR/PATCH in your org.
  4. Require schema metadata for every tool
    • Input/output schemas; validate in CI.
  5. Track tool-level versions only when necessary
    • Don’t overcomplicate unless you need side-by-side variants.
  6. Expose dependency and compatibility ranges
    • MCP protocol versions, runtimes, and repository dependencies.
  7. Implement deprecation fields and sunset windows
    • Never yank tools without warning and migration guidance.
  8. Sign manifests or at least hash them
    • Prepare for stricter security policies down the road.
  9. Integrate metadata checks into CI
    • Lint manifests, validate schemas, enforce version bumps for contract changes.
  10. Publish a concise changelog per release
    • Link it from metadata so humans and tooling can find it fast.

Do these consistently and your MCP repositories will be easier to discover, safer to upgrade, and more trusted by every model and platform that depends on them.

Registry FAQ - Model Context Protocol (MCP) awesome-mcp-servers/docs/version-control.md at main - GitHub How to MCP - The Complete Guide to Understanding Model Context … What is MCP Registry? Architecture, Benefits & Setup Guide A Developer’s Guide to the MCP - Zep