Skip to content

Latest commit

 

History

History
119 lines (85 loc) · 5.75 KB

File metadata and controls

119 lines (85 loc) · 5.75 KB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Overview

elasticsearch-mcp is a Python MCP (Model Context Protocol) server that enables AI assistants to interact with Elasticsearch clusters. It uses the official elasticsearch Python client and provides tools for searching, aggregations, index management, and cluster operations.

Development Commands

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

pytest                                      # All tests
pytest tests/test_config.py                 # Single file
pytest --cov=elasticsearch_mcp --cov-report=html    # With coverage

ruff check .                                # Lint
ruff format .                               # Format
mypy src/                                   # Type check

Architecture

Transport Modes

The server (server.py:main) supports three transport modes via CLI args:

  • stdio (default): elasticsearch-mcp — for Claude Desktop local connections
  • SSE (legacy): elasticsearch-mcp --http — HTTP/SSE for centralized deployment
  • Streamable HTTP: elasticsearch-mcp --streamable-http — for Claude.ai Integrations with OAuth

Tool Registration Pattern

Tools are registered via @mcp.tool() decorators on the module-level mcp FastMCP instance in server.py. Connection tools (connect, disconnect, cluster_health, etc.) are defined directly in server.py. Domain tools are implemented in tools/ modules and called from server.py tool wrappers.

Module Layout

src/elasticsearch_mcp/
├── server.py           # FastMCP instance, connection/cluster tools, CLI entry, transport modes
├── connection.py       # ConnectionManager: async connect/disconnect, health check
├── config.py           # Settings (pydantic-settings): all env vars including auth, watchdog, audit
├── auth/               # OAuth for Claude.ai Integrations (Streamable HTTP mode only)
│   ├── provider.py     # ESOAuthProvider: bridges Claude.ai DCR to external IdPs
│   ├── callback.py     # /oauth/callback route handler
│   ├── storage.py      # In-memory auth state (clients, codes, tokens, pending auths)
│   └── idp/            # Identity provider adapters
│       ├── base.py     # BaseIdPAdapter ABC (get_authorization_url, exchange_code, validate_token)
│       ├── duo.py      # Cisco Duo adapter
│       ├── auth0.py    # Auth0 adapter
│       └── oidc.py     # Generic OIDC adapter
├── tools/              # MCP tool implementations
│   ├── cluster.py      # cluster_health, cluster_info, list_nodes, cluster_stats
│   ├── indices.py      # list_indices, describe_index, get_index_stats, get_mappings, get_aliases
│   └── search.py       # search, search_simple, count, get_document, aggregate, terms_aggregation, date_histogram
├── resources/          # MCP resources (placeholder)
└── utils/              # Utility modules (placeholder)

Streamable HTTP + OAuth Flow

When --streamable-http with auth enabled:

  1. Claude.ai registers via DCR → ESOAuthProvider.register_client()
  2. /authorize → redirects to external IdP (Duo/Auth0/OIDC)
  3. User authenticates → IdP callbacks to /oauth/callback
  4. Callback generates auth code → redirects to Claude's callback
  5. Claude exchanges code at /token → returns access/refresh tokens

A second FastMCP instance is created for Streamable HTTP mode, and tools/resources are copied from the module-level mcp instance.

Global Singletons

  • server.py: mcp — module-level FastMCP instance
  • config.py: settings — module-level Settings instance
  • connection.py: connection_manager — module-level ConnectionManager instance

Environment Variables

Required: ES_HOST

Key optional groups:

  • Auth: ES_API_KEY or ES_USERNAME/ES_PASSWORD or ES_CLOUD_ID
  • Safety: ES_READ_ONLY, ES_MAX_RESULTS, ES_BLOCKED_INDICES, ES_TIMEOUT
  • HTTP: ES_HTTP_HOST, ES_HTTP_PORT, ES_HTTP_CORS_ORIGINS
  • OAuth: ES_AUTH_ENABLED, ES_AUTH_ISSUER_URL, ES_IDP_PROVIDER, ES_IDP_DISCOVERY_URL, ES_IDP_CLIENT_ID, ES_IDP_CLIENT_SECRET, ES_DUO_API_HOST
  • Watchdog: ES_WATCHDOG_ENABLED, ES_WATCHDOG_INTERVAL, ES_WATCHDOG_TIMEOUT
  • Audit: ES_AUDIT_ENABLED, ES_AUDIT_PATH

All config is in config.py (Settings pydantic-settings class) with env_prefix="ES_".

Testing

Tests do not require a live Elasticsearch cluster. The tests/ directory contains mock-based unit tests. Key pattern:

  • Config tests validate environment variable binding and defaults
  • Tool tests mock the Elasticsearch client responses

Coding Standards

  • Follow PEP 8; use ruff for linting/formatting
  • Maximum line length: 100 characters
  • Type hints required for all function signatures
  • Google-style docstrings for public APIs
  • Ruff rules: select = ["E", "F", "I", "N", "W", "UP"]
  • mypy: disallow_untyped_defs = true

Key Patterns

Connection Pattern: Use elasticsearch.AsyncElasticsearch for async operations. Single persistent connection via ConnectionManager with auto-reconnect.

Safety Controls: Index pattern validation via blocked_indices list (default: .security*,.kibana*,.apm*,.monitoring*), read-only mode, max results limit.

Schema Discovery: Use _cat/indices, _mapping, and _settings APIs for metadata.

Auth Layer: The OAuth module is database-agnostic — identical pattern across u2-mcp, pymssql-mcp, and elasticsearch-mcp (only class names and config types differ).

Reference Implementations