Skip to main content

MCP server service discovery - Read-only Python library for querying MCP server capabilities

Project description

chora-manifest

MCP Server Service Discovery and Capability Catalog

Python Version License Code Style


Overview

chora-manifest is a Python library for querying MCP (Model Context Protocol) server capabilities from a centralized registry. It provides a simple, async API for service discovery, enabling applications to find and inspect available MCP servers programmatically.

Version 0.1.0 is a focused, read-only implementation perfect for:

  • Discovering available MCP servers in your ecosystem
  • Querying server capabilities and tools
  • Filtering servers by category (integration, database, productivity, infrastructure)
  • Building service catalogs and dashboards

Features

v0.1.0 Capabilities

  • Load Registry: Read MCP server definitions from YAML
  • List Capabilities: Get all registered MCP servers
  • Get by ID: Retrieve specific server by identifier
  • Filter by Category: Find servers by type (integration, database, etc.)
  • Search: Query by name or description (case-insensitive)
  • Count Operations: Get totals overall or by category
  • Type Safety: Full Pydantic validation throughout
  • Async API: Built with async/await for modern Python
  • 100% Test Coverage: Comprehensive test suite (51 tests)

What's NOT in v0.1.0

The following features are planned for future versions:

  • ❌ Write operations (add/update/remove servers)
  • ❌ CLI interface
  • ❌ REST API
  • ❌ MCP server interface
  • ❌ GitHub discovery/import
  • ❌ Health monitoring
  • ❌ Maturity levels

Installation

From PyPI

pip install chora-manifest

From Source

git clone https://github.com/yourusername/chora-manifest.git
cd chora-manifest
pip install -e .

Quick Start

Basic Usage

import asyncio
from manifest import create_manifest_service

async def main():
    # Create service (uses ecosystem-manifest/registry.yaml by default)
    service = create_manifest_service()

    # Initialize (loads registry)
    await service.initialize()

    # List all capabilities
    capabilities = await service.list_capabilities()
    print(f"Found {len(capabilities)} MCP servers")

    # Get specific server
    github = await service.get_capability("github")
    if github:
        print(f"GitHub server: {github.description}")
        print(f"Tools: {len(github.tools)}")

    # Filter by category
    databases = await service.filter_by_category("database")
    print(f"Database servers: {len(databases)}")

    # Search
    results = await service.search("integration")
    print(f"Integration-related servers: {len(results)}")

asyncio.run(main())

Custom Registry Path

from pathlib import Path
from manifest import create_manifest_service

service = create_manifest_service(
    registry_path=Path("my-registry.yaml")
)
await service.initialize()

API Reference

Factory Function

create_manifest_service(registry_path=None)

Create a ManifestService instance.

Parameters:

  • registry_path (Path | str | None): Path to registry YAML. Defaults to ecosystem-manifest/registry.yaml

Returns: ManifestService (not yet initialized)

Example:

service = create_manifest_service()
await service.initialize()

ManifestService

Async service for querying MCP server capabilities.

async initialize()

Load registry from file. Must be called before using query methods.

Raises:

  • FileNotFoundError: If registry file doesn't exist
  • yaml.YAMLError: If YAML is malformed

async list_capabilities() -> list[Capability]

List all registered MCP server capabilities.

Returns: List of all Capability objects

Example:

capabilities = await service.list_capabilities()
for cap in capabilities:
    print(f"{cap.name}: {cap.description}")

async get_capability(id: str) -> Capability | None

Get capability by ID.

Parameters:

  • id (str): Capability identifier (e.g., "github", "slack")

Returns: Capability if found, None otherwise

Example:

github = await service.get_capability("github")
if github:
    print(f"Version: {github.version}")

async filter_by_category(category: Category) -> list[Capability]

Filter capabilities by category.

Parameters:

  • category (Category): Category enum value

Returns: List of capabilities in the category

Example:

from manifest import Category

databases = await service.filter_by_category(Category.DATABASE)

async search(query: str) -> list[Capability]

Search capabilities by name or description (case-insensitive).

Parameters:

  • query (str): Search string

Returns: List of matching capabilities

Example:

results = await service.search("database")

async count_total() -> int

Count total registered capabilities.

Returns: Total count


async count_by_category(category: Category) -> int

Count capabilities in a specific category.

Parameters:

  • category (Category): Category enum value

Returns: Count in category


Data Models

Category (Enum)

MCP server categories:

  • Category.INTEGRATION - Integration servers (GitHub, Slack, etc.)
  • Category.DATABASE - Database servers (PostgreSQL, MySQL, etc.)
  • Category.PRODUCTIVITY - Productivity tools (Jira, Notion, etc.)
  • Category.INFRASTRUCTURE - Infrastructure tools (Docker, Kubernetes, etc.)

Capability (Pydantic Model)

Represents an MCP server capability.

Fields:

  • id (str): Unique identifier
  • name (str): Human-readable name
  • description (str): Brief description
  • version (str): Semantic version (e.g., "1.0.0")
  • docker_image (str): Docker image reference
  • tools (list[dict]): List of tool definitions
  • category (Category): Server category
  • tags (list[str]): Searchable tags (default: [])
  • repository (str | None): Source repository URL (optional)

Example:

from manifest import Capability, Category

capability = Capability(
    id="my-server",
    name="My Server",
    description="Custom MCP server",
    version="1.0.0",
    docker_image="my-server:1.0.0",
    tools=[{"name": "my_tool", "description": "Does something"}],
    category=Category.INTEGRATION,
    tags=["custom"],
    repository="https://github.com/example/my-server"
)

Registry Format

The registry is a YAML file with the following structure:

version: "1.0.0"
updated: "2025-11-15T00:00:00Z"
servers:
  - server:
      name: github
      description: GitHub integration MCP server
      version: 1.0.0
      repository: https://github.com/modelcontextprotocol/servers/tree/main/src/github
      transport: stdio
    tools:
      - name: create_or_update_file
        description: Create or update a file in a repository
        input_schema:
          type: object
          properties:
            path:
              type: string
            content:
              type: string
    metadata:
      tags:
        - integration
        - version-control
        - git

The service automatically converts the servers format to Capability objects.


Examples

Example 1: List All Servers

import asyncio
from manifest import create_manifest_service

async def main():
    service = create_manifest_service()
    await service.initialize()

    capabilities = await service.list_capabilities()

    print(f"MCP Server Catalog ({len(capabilities)} servers)\n")
    print("-" * 60)

    for cap in capabilities:
        print(f"\n{cap.name} (v{cap.version})")
        print(f"  {cap.description}")
        print(f"  Category: {cap.category}")
        print(f"  Tools: {len(cap.tools)}")
        if cap.repository:
            print(f"  Repo: {cap.repository}")

asyncio.run(main())

Example 2: Filter by Category

from manifest import create_manifest_service, Category

async def show_category_stats():
    service = create_manifest_service()
    await service.initialize()

    categories = [
        Category.INTEGRATION,
        Category.DATABASE,
        Category.PRODUCTIVITY,
        Category.INFRASTRUCTURE
    ]

    print("Server Distribution by Category\n")

    for category in categories:
        count = await service.count_by_category(category)
        servers = await service.filter_by_category(category)
        names = [s.name for s in servers]

        print(f"{category.value.upper()}: {count}")
        if names:
            print(f"  → {', '.join(names)}")

    total = await service.count_total()
    print(f"\nTotal: {total} servers")

Example 3: Search and Inspect

from manifest import create_manifest_service

async def search_and_inspect(query: str):
    service = create_manifest_service()
    await service.initialize()

    results = await service.search(query)

    print(f"Search results for '{query}': {len(results)} found\n")

    for cap in results:
        print(f"{cap.name}")
        print(f"  Description: {cap.description}")
        print(f"  Category: {cap.category}")
        print(f"  Available tools:")
        for tool in cap.tools:
            print(f"    - {tool['name']}: {tool['description']}")
        print()

Development

Setup

# Clone repository
git clone https://github.com/yourusername/chora-manifest.git
cd chora-manifest

# Create virtual environment
python -m venv venv
source venv/bin/activate  # On Windows: venv\Scripts\activate

# Install development dependencies
pip install -e ".[dev]"

Running Tests

# Run all tests
pytest

# Run with coverage
pytest --cov=manifest --cov-report=html

# Run specific test file
pytest tests/test_service.py

# Run with verbose output
pytest -v

Code Quality

# Lint with ruff
ruff check .

# Format with ruff
ruff format .

# Type check with mypy
mypy manifest

Testing

chora-manifest v0.1.0 has comprehensive test coverage:

  • 51 tests across 4 test modules
  • 100% coverage on core implementation files
  • TDD methodology: All tests written before implementation
  • Fixtures: Reusable test data in conftest.py

Test modules:

  • test_models.py: Pydantic model validation (9 tests)
  • test_loader.py: YAML loading and parsing (10 tests)
  • test_service.py: Service query methods (21 tests)
  • test_factory.py: Factory function and API (11 tests)

Roadmap

v0.2.0 (Planned)

  • Write operations (add/update/remove servers)
  • GitHub discovery and import
  • Health monitoring and status checks

v0.3.0 (Planned)

  • CLI interface
  • REST API
  • Maturity level support

v1.0.0 (Future)

  • MCP server interface
  • Advanced querying and filtering
  • Performance optimizations

Contributing

Contributions welcome! Please:

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for new functionality
  4. Ensure all tests pass (pytest)
  5. Run code quality checks (ruff check, mypy manifest)
  6. Submit a pull request

License

This project is licensed under the MIT License - see LICENSE file for details.


Acknowledgments

  • Built with Pydantic for data validation
  • Powered by PyYAML for registry parsing
  • Part of the chora ecosystem for MCP server management

Links


Version: 0.1.0 Status: Stable Python: 3.11+ License: MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

chora_manifest-0.1.1.tar.gz (45.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

chora_manifest-0.1.1-py3-none-any.whl (41.9 kB view details)

Uploaded Python 3

File details

Details for the file chora_manifest-0.1.1.tar.gz.

File metadata

  • Download URL: chora_manifest-0.1.1.tar.gz
  • Upload date:
  • Size: 45.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.0

File hashes

Hashes for chora_manifest-0.1.1.tar.gz
Algorithm Hash digest
SHA256 84ac592d5bec61962042c4685c0159e0adea379a32b81a240839b0bb57bcd0d1
MD5 b735b6d21a120bfba0dbbf51ad7b2203
BLAKE2b-256 e2bb2b1686e88ff1448e84bac902bb875004a7f618b39a53f799580f1873ced5

See more details on using hashes here.

File details

Details for the file chora_manifest-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: chora_manifest-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 41.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.0

File hashes

Hashes for chora_manifest-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 94118be2c9a1ff9f11f11810f37a90813d08f6c0999978533d1339801bc2c51e
MD5 5df7d093685f9287eefbbcb8281133c0
BLAKE2b-256 53154ea33773f1bea0ce788a730995f55e0f96e40758a16ff173f8fb38f37b79

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page