Skip to main content
Version: 0.8

MCP Server

Guide for contributing tools or other resources to the Couchbase MCP Server.

If you want to contribute to the documentation site, see Contributing to the Documentation.

Development Setup​

Prerequisites​

  • Python 3.10+
  • uv - Fast Python package installer and dependency manager
  • Git
  • VS Code (recommended) - With the Python extension for the best development experience

Clone and Setup​

# Clone the repository
git clone https://github.com/couchbase/mcp-server-couchbase.git
cd mcp-server-couchbase

# Install dependencies (including development tools)
uv sync --extra dev
External contributors

If you don't have commit access, fork the repo to your own GitHub account and clone your fork instead.

Install Pre-Commit Hooks​

# Install pre-commit hooks (runs linting on every commit)
uv run pre-commit install

# Verify installation
uv run pre-commit run --all-files

Project Structure​

mcp-server-couchbase/
├── src/
│ ├── mcp_server.py # Standalone MCP server entry point (uv run src/mcp_server.py)
│ ├── providers/ # Standalone-host ClusterProvider implementations
│ │ ├── __init__.py
│ │ └── static.py # StaticClusterProvider used by mcp_server.py
│ └── cb_mcp/ # Reusable Python package shared with other host implementations
│ ├── __init__.py # Package marker
│ ├── tool_registration.py # Shared tool prep: parse, filter, wrap with confirmation
│ ├── core/ # Host-agnostic contracts
│ │ ├── __init__.py
│ │ └── contracts.py # ClusterProvider protocol and related contracts
│ ├── certs/ # SSL/TLS certificates
│ │ ├── __init__.py
│ │ └── capella_root_ca.pem # Capella root CA certificate
│ ├── tools/ # MCP tool implementations
│ │ ├── __init__.py # Tool exports, READ_ONLY_TOOLS / KV_WRITE_TOOLS, TOOL_ANNOTATIONS
│ │ ├── server.py # Server status and connection tools
│ │ ├── kv.py # Key-value operations (CRUD)
│ │ ├── query.py # SQL++ query and performance tools
│ │ └── index.py # Index operations and recommendations
│ └── utils/ # Utility modules
│ ├── __init__.py
│ ├── constants.py # Project constants
│ ├── config.py # Configuration management
│ ├── connection.py # Couchbase connection handling
│ ├── context.py # Application context (AppContext, cluster provider accessors)
│ ├── elicitation.py # Confirmation/elicitation support
│ ├── index_utils.py # Index-related helper functions
│ └── query_utils.py # Query-related helper functions
├── tests/ # Test suite
├── website/ # Documentation website
├── scripts/ # Development scripts
│ ├── lint.sh # Manual linting script
│ ├── lint_fix.sh # Auto-fix linting issues
│ ├── setup_test_data.py # Setup script for integration tests
│ └── update_version.sh # Script to bump package version
├── pyproject.toml # Project config and dependencies
└── .pre-commit-config.yaml # Pre-commit hook configuration
Package layout

Tool, utility, and certificate modules live under the reusable src/cb_mcp/ package so other hosts (e.g. embedded servers, alternate transports) can import them. The standalone src/mcp_server.py entry point wires cb_mcp together with a ClusterProvider from src/providers/.

Development Workflow​

  1. Create a branch for your feature or fix:

    git checkout -b feature/your-feature-name
  2. Make your changes, following existing patterns in the codebase.

  3. Commit your changes:

    git add .
    git commit -m "feat: add your feature description"

    The pre-commit hooks will automatically run and fix any formatting issues.

  4. Push and open a pull request. If working from a fork, follow the GitHub fork PR guide.

Common Development Tasks​

# Install new dependencies
uv add package-name

# Install new dev dependencies
uv add --dev package-name

# Update all dependencies
uv sync --upgrade

# Update a specific package
uv sync --upgrade-package package-name

# Run the server for testing
uv run src/mcp_server.py \
--connection-string "couchbase://localhost" \
--username "Administrator" \
--password "password"

Debugging​

  • Use logging: The project uses hierarchical logging with the pattern

    logger = logging.getLogger(f"{MCP_SERVER_NAME}.module.name")
  • Check connection: Ensure your Couchbase cluster is accessible.

  • Validate configuration: Make sure all required environment variables are set.

Adding New Tools​

Before You Start​

  • Check existing issues to see if someone is already working on it

  • Open an issue to discuss larger changes

  • Review the codebase to understand existing patterns

Implementation Guidelines​

  • Follow existing patterns: Look at similar tools for guidance

  • Use the utility modules: Leverage existing connection and context management

  • Add proper logging: Use the hierarchical logging system

  • Handle errors gracefully: Provide helpful error messages

  • Consider read-only mode: If your tool modifies data, respect read_only_mode settings

  • Update documentation: Update README.md and DOCKER.md and the documentation website if adding user-facing features

Implementation Steps​

  1. Create the tool function in the appropriate module under src/cb_mcp/tools/:

    • server.py — Server and cluster management tools

    • kv.py — Key-value document operations

    • query.py — SQL++ query and performance analysis tools

    • index.py — Index management tools

  2. Export the tool in src/cb_mcp/tools/__init__.py:

    • Import the function

    • Add it to READ_ONLY_TOOLS (if it only reads data) or KV_WRITE_TOOLS (if it modifies data)

    • Add the annotation for the tool in TOOL_ANNOTATIONS

    • Add it to __all__

  3. Test the tool with an MCP client.

Example​

Here's the pattern used by existing tools (note the relative imports from within the cb_mcp package):

import logging
from typing import Any

from fastmcp import Context

from ..utils.constants import MCP_SERVER_NAME
from ..utils.context import get_cluster_connection

logger = logging.getLogger(f"{MCP_SERVER_NAME}.tools.your_module")


def your_new_tool(
ctx: Context, bucket_name: str, some_param: str
) -> dict[str, Any]:
"""Description of what this tool does.
This docstring is exposed to the LLM as the tool description.
"""
cluster = get_cluster_connection(ctx)
# ... your implementation
return {"result": "data"}

Key Patterns​

  • Context: Always accept ctx: Context (from fastmcp) as the first parameter. Use get_cluster_connection(ctx) from cb_mcp.utils.context to get the Couchbase Cluster object. If you need the full provider (e.g. to read host configuration), use get_cluster_provider(ctx) instead.

  • Provider-backed connection: Cluster access is mediated by a ClusterProvider (see src/cb_mcp/core/contracts.py). The standalone server uses StaticClusterProvider from src/providers/static.py; other hosts may inject their own. Tools should not assume how the connection is established.

  • Relative imports: Tool modules live inside the cb_mcp package, so use relative imports (from ..utils.constants import ...) rather than absolute utils.* imports.

  • Logging: Use the hierarchical logger pattern: logging.getLogger(f"{MCP_SERVER_NAME}.tools.module_name")

  • Error handling: Either raise exceptions (they are returned to the LLM) or return error dictionaries — follow the pattern of similar tools.

  • Read-only awareness: If your tool modifies data, add it to KV_WRITE_TOOLS so it's excluded in read-only mode.

Checklist​

  • Function created in appropriate src/cb_mcp/tools/*.py module
  • Exported in src/cb_mcp/tools/__init__.py
  • Added to the correct tool list (READ_ONLY_TOOLS or KV_WRITE_TOOLS)
  • Annotation added to TOOL_ANNOTATIONS
  • Added to __all__
  • Descriptive docstring (this is what the LLM sees)
  • Tested with an MCP client

Testing​

The project uses pytest with pytest-asyncio for testing.

Setting Up Test Data​

For integration tests that need a running Couchbase cluster:

Set your Couchbase cluster credentials:

# Set required environment variables
export CB_CONNECTION_STRING="couchbase://localhost"
export CB_USERNAME="Administrator"
export CB_PASSWORD="password"
export CB_MCP_TEST_BUCKET="travel-sample"

# Populate test data and create indexes
uv run scripts/setup_test_data.py

Run Tests​

# Run all tests
uv run pytest tests/ -v

# Run a specific test file
uv run pytest tests/test_query_tools.py -v

Test Structure​

Test FileDescription
test_mcp_integration.pyMCP Server integration tests
test_server_tools.pyServer status and connection tools
test_server_configuration_status_tool.pyServer configuration status tool
test_kv_tools.pyKV CRUD operations
test_query_tools.pySQL++ query execution
test_index_tools.pyIndex listing and advisor
test_performance_tools.pyQuery performance analysis
test_query_plan_evaluation.pyQuery plan evaluation
test_is_explain_statement.pyEXPLAIN statement detection
test_read_only_mode.pyRead-only mode enforcement
test_confirmation_tools.pyConfirmation/elicitation for tools
test_disabled_tools.pyTool disabling mechanism
test_tool_registration.pyTool registration
test_parse_tool_names.pyTool name parsing
test_utils.pyUtility function tests

Test Configuration​

  • conftest.py provides shared fixtures including create_mcp_session() for full MCP client-server communication over stdio.

  • Tests that require CB_MCP_TEST_BUCKET are skipped if the variable is not set.

  • Default test timeout is 120 seconds (CB_MCP_TEST_TIMEOUT).

Writing Tests​

When adding new features or tools, add corresponding tests:

  1. Create a test file in tests/ following the test_*.py naming convention.

  2. Use shared fixtures from conftest.py.

  3. Test both success and error paths.

  4. Test read-only mode interactions if your tool performs write operations.

CI/CD Testing​

The GitHub Actions workflow (test.yml) runs integration tests across all three transport modes (stdio, HTTP, SSE) against a Couchbase Enterprise 8.0.0 container with the travel-sample dataset.


Submitting Changes​

Before submitting a pull request (PR):

# Ensure all linting passes
./scripts/lint.sh

# Run pre-commit checks
uv run pre-commit run --all-files

# Run tests (if you have a Couchbase cluster available)
uv run pytest tests/ -v

Describe your changes in the PR description:

  • What does this change do?

  • Why is this change needed?

  • How have you tested it?


Code Quality​

The project uses Ruff for fast linting and code formatting.

Manual Linting​

# Check code quality (no changes made)
./scripts/lint.sh
# Runs: ruff check src/ --diff && ruff format src/ --diff --check

# Auto-fix issues
./scripts/lint_fix.sh
# Runs: ruff check src/ --fix && ruff format src/

Automatic Linting​

  • Pre-commit hooks: Ruff runs automatically on every git commit.

  • VS Code: Auto-format on save using the Ruff extension.

Code Style Guidelines​

  • Line length: 88 characters (enforced by Ruff)

  • Import organization: Use isort-style grouping (standard library, third-party, local)

  • Type hints: Use modern Python type hints where helpful

  • Docstrings: Add docstrings for public functions and classes

  • Error handling: Include appropriate exception handling with logging

Additional Resources​