test: enforce domain coverage floors (#152)
Doris MCP (Model Context Protocol) Server is a backend service built with Python and FastAPI. It implements the MCP, allowing clients to interact with it through defined “Tools”. It's primarily designed to connect to Apache Doris databases, potentially leveraging Large Language Models (LLMs) for tasks like converting natural language queries to SQL (NL2SQL), executing queries, and performing metadata management and analysis.
The current package metadata and latest Git tag are 0.6.1. The master branch also contains changes made after that tag; those changes are recorded under Unreleased until the next version is selected and published.
MCP 2026-07-28 protocol compatibility on master is Generally Available (GA) for Streamable HTTP and stdio. The supported protocol contract has passed the full test suite with warnings treated as errors, the official stateless conformance suite, clean wheel installation, and real Apache Doris tests over both transports.
This GA statement is scoped to protocol compatibility on the supported transports. The project package metadata remains Beta, and it does not expand the supported deployment shapes or remove the documented operational constraints. Review the changelog, the protocol support matrix, and the deployment constraints before deploying it outside a controlled environment.
Release note: v0.6.0 introduced the authentication, token management, connection, and multi-worker features summarized above. These features do not remove the Beta status or the deployment limits documented for the current codebase.
doris_mcp_server/tools/tools_manager.py)doris_mcp_server/tools/resources_manager.py)doris_mcp_server/tools/prompts_manager.py)doris_mcp_server/utils/query_executor.py)doris_mcp_server/utils/security.py)doris_mcp_server/utils/schema_extractor.py)doris_mcp_server/utils/analysis_tools.py)doris_mcp_server/auth/token_handlers.py)config.py with comprehensive validation, standardized parameter naming, and smart default database handling with automatic fallback to information_schema# Install the latest version pip install doris-mcp-server # Install specific version pip install doris-mcp-server==0.6.1
💡 Packaged Commands:
doris-mcp-serverstarts the MCP server.doris-mcp-clientis a separate client for connecting to a server over Streamable HTTP or stdio; the two commands are not interchangeable.
The primary communication mode offering optimal performance and reliability:
# Full configuration with database connection doris-mcp-server \ --transport http \ --host 127.0.0.1 \ --port 3000 \ --db-host 127.0.0.1 \ --db-port 9030 \ --db-user root \ --db-password your_password
Standard input/output mode for direct integration with MCP clients:
# For direct integration with MCP clients like Cursor doris-mcp-server --transport stdio
Access the Web-Based Token Management Dashboard for enterprise-grade token administration:
127.0.0.1 and ::1 for maximum securityTOKEN_MANAGEMENT_ADMIN_TOKEN for access# Generate separate high-entropy credentials; do not commit them. export TOKEN_ADMIN="$(python -c 'import secrets; print(secrets.token_urlsafe(32))')" export TOKEN_MANAGEMENT_ADMIN_TOKEN="$(python -c 'import secrets; print(secrets.token_urlsafe(32))')" export ENABLE_HTTP_TOKEN_MANAGEMENT=true export ENABLE_TOKEN_AUTH=true export TOKEN_MANAGEMENT_ALLOWED_IPS=127.0.0.1,::1
Management requests accept the admin token only in an HTTP header. Query-string tokens are rejected and must not be placed in URLs, browser history, or access logs.
curl -H "Authorization: Bearer $TOKEN_MANAGEMENT_ADMIN_TOKEN" \ http://127.0.0.1:3000/token/stats
The optional /token/management page must be opened through a client or local proxy that supplies the same header. Its API requests use an in-page password field and do not persist or propagate the token in URLs.
tokens.json; plaintext is returned once when a token is created🔐 Security Note: The interface is designed for localhost administration only. It cannot be accessed remotely, ensuring maximum security for token management operations.
# Check installation doris-mcp-server --version doris-mcp-client --version doris-mcp-server --help doris-mcp-client --help # Test HTTP mode (in another terminal) curl --fail http://localhost:3000/live curl --fail http://localhost:3000/ready
Instead of command-line arguments, you can use environment variables:
# Basic Database Configuration export DORIS_HOST="127.0.0.1" export DORIS_PORT="9030" export DORIS_USER="root" export DORIS_PASSWORD="your_password" # Keep the isolated legacy HTTP migration adapter disabled unless an # identified 2025-11-25 client still needs /mcp/legacy. export ENABLE_LEGACY_HTTP_ADAPTER=false # Bound each resources/list, tools/list, and prompts/list response. export MCP_LIST_PAGE_SIZE=100 # A launch-local key is generated automatically. Configure one shared # high-entropy value when independently launched replicas share traffic. export MCP_STATE_HANDLE_SECRET="$(python -c 'import secrets; print(secrets.token_urlsafe(32))')" export MCP_STATE_HANDLE_TTL_SECONDS=300 # Token Management Interface (Security-Critical) export TOKEN_ADMIN="$(python -c 'import secrets; print(secrets.token_urlsafe(32))')" export TOKEN_MANAGEMENT_ADMIN_TOKEN="$(python -c 'import secrets; print(secrets.token_urlsafe(32))')" export ENABLE_HTTP_TOKEN_MANAGEMENT=true export ENABLE_TOKEN_AUTH=true export TOKEN_MANAGEMENT_ALLOWED_IPS="127.0.0.1,::1" # Then start with simplified command doris-mcp-server --transport http --host 127.0.0.1 --port 3000
The doris-mcp-server command supports the following arguments:
| Argument | Description | Default | Required |
|---|---|---|---|
--transport | Transport mode: http or stdio | http | No |
--host | HTTP server host (HTTP mode only) | localhost | No |
--port | HTTP server port (HTTP mode only) | 3000 | No |
--db-host | Doris database host | localhost | No |
--db-port | Doris database port | 9030 | No |
--db-user | Doris database username | root | No |
--db-password | Doris database password | - | Yes (unless in env) |
For developers who want to build from source:
# Replace with the actual repository URL if different git clone https://github.com/apache/doris-mcp-server.git cd doris-mcp-server
pip install -r requirements.txt
Copy the .env.example file to .env and modify the settings according to your environment:
cp .env.example .env
Key Environment Variables:
DORIS_HOST: Database hostname (default: localhost)DORIS_PORT: Database port (default: 9030)DORIS_USER: Database username (default: root)DORIS_PASSWORD: Database passwordDORIS_DATABASE: Default database name (default: information_schema)DORIS_MIN_CONNECTIONS: Minimum connection pool size (default: 5)DORIS_MAX_CONNECTIONS: Maximum connection pool size (default: 20)DORIS_FE_HTTP_HOST: Independent FE HTTP host for profile, table-size, and monitoring tools (default: empty, falling back to DORIS_HOST)DORIS_FE_HTTP_PORT: Independent FE HTTP API port (default: 8030)DORIS_BE_HOSTS: Explicit BE HTTP allowlist for monitoring (comma-separated; BE HTTP metrics are disabled when empty)DORIS_BE_WEBSERVER_PORT: BE webserver port for monitoring tools (default: 8040)DORIS_HTTP_CONNECT_TIMEOUT_SECONDS: FE/BE HTTP connection timeout (default: 3)DORIS_HTTP_READ_TIMEOUT_SECONDS: FE/BE HTTP socket read timeout (default: 15)DORIS_HTTP_TOTAL_TIMEOUT_SECONDS: FE/BE HTTP total timeout (default: 30; hard maximum: 60)DORIS_HTTP_MAX_RESPONSE_BYTES: FE/BE HTTP response limit (default: 4 MiB; hard maximum: 16 MiB)FE_ARROW_FLIGHT_SQL_PORT: Frontend Arrow Flight SQL port for ADBC (New in v0.5.0)BE_ARROW_FLIGHT_SQL_PORT: Backend Arrow Flight SQL port for ADBC (New in v0.5.0)ENABLE_LEGACY_HTTP_ADAPTER: Expose the isolated 2025-11-25 migration adapter at /mcp/legacy (default: false); modern traffic always uses POST /mcpMCP_LIST_PAGE_SIZE: Maximum resources, tools, or prompts returned per protocol page (default: 100; range: 1-1000)MCP_STATE_HANDLE_SECRET: Optional shared high-entropy key (at least 32 bytes) used to authenticate explicit cross-call state handlesMCP_STATE_HANDLE_TTL_SECONDS: Lifetime of an explicit state handle (default: 300 seconds; range: 1-3600)ENABLE_TOKEN_AUTH: Enable token-based authentication (default: false)ENABLE_JWT_AUTH: Enable JWT authentication (default: false)ENABLE_OAUTH_AUTH: Enable OAuth authentication (default: false)OAUTH_ISSUER: Exact external authorization-server issuerOAUTH_RESOURCE: Canonical MCP protected-resource URIOAUTH_AUDIENCE: Expected access-token audience (defaults to OAUTH_RESOURCE)OAUTH_INTROSPECTION_URL: Trusted RFC 7662 token-introspection endpointOAUTH_SCOPE / OAUTH_REQUIRED_SCOPE: Allowed and mandatory external OAuth scopesENABLE_DORIS_OAUTH_AUTH: Enable Doris-backed OAuth authentication (default: false)DORIS_OAUTH_BASE_URL: Public base URL used by Doris-backed OAuth discovery and token endpointsDORIS_OAUTH_CIMD_FETCH_TIMEOUT_SECONDS: Client ID Metadata Document fetch timeout (default: 5)DORIS_OAUTH_CIMD_MAX_DOCUMENT_BYTES: Maximum Client ID Metadata Document size (default: 5120)DORIS_OAUTH_CIMD_DEFAULT_CACHE_SECONDS: Cache lifetime when the document does not provide one (default: 300)DORIS_OAUTH_CIMD_MAX_CACHE_SECONDS: Maximum accepted document cache lifetime (default: 3600)DORIS_OAUTH_CIMD_MAX_CLIENTS: Maximum discovered Client ID Metadata clients held in memory (default: 1000)TOKEN_FILE_PATH: Path to tokens.json file for token management (default: tokens.json)TOKEN_HOT_RELOAD: Enable hot reloading of token configuration (default: true)TOKEN_HASH_ALGORITHM: Digest algorithm for newly created static tokens (sha256 or sha512; default: sha256)TOKEN_<ID>: Explicit static bearer token; each active token must be a securely generated value of at least 32 charactersAUTH_TYPE: Legacy authentication type (token/basic/oauth, deprecated - use individual switches)ENABLE_SECURITY_CHECK: Enable/disable SQL security validation (default: true)BLOCKED_KEYWORDS: Comma-separated list of blocked SQL keywordsENABLE_MASKING: Enable data masking (default: true)MAX_RESULT_ROWS: Deployment ceiling for returned query rows (default: 10000; absolute hard cap: 100000)ADBC_DEFAULT_MAX_ROWS: Default maximum rows for ADBC queries (default: 10000; cannot exceed MAX_RESULT_ROWS)ADBC_DEFAULT_TIMEOUT: Default ADBC query timeout in seconds (default: 60)ADBC_DEFAULT_RETURN_FORMAT: Default return format - arrow/pandas/dict (default: arrow)ADBC_CONNECTION_TIMEOUT: ADBC connection timeout in seconds (default: 30)ADBC_ENABLED: Enable/disable ADBC tools (default: true)ENABLE_QUERY_CACHE: Enable query caching (default: true)CACHE_TTL: Cache time-to-live in seconds (default: 300)MAX_CONCURRENT_QUERIES: Maximum concurrent queries (default: 50)QUERY_TIMEOUT: Deployment ceiling for query execution time (default and absolute hard cap: 300 seconds)MAX_RESULT_BYTES: Deployment ceiling for UTF-8 JSON row data (default: 1048576; allowed range: 256-16777216 bytes)MAX_RESPONSE_CONTENT_SIZE: Maximum response content size for LLM compatibility (default: 4096, New in v0.4.0)LOG_LEVEL: Log level (DEBUG/INFO/WARNING/ERROR, default: INFO)LOG_FILE_PATH: Log file path (automatically organized by level)ENABLE_AUDIT: Enable audit logging (default: true)ENABLE_LOG_CLEANUP: Enable automatic log cleanup (default: true, Enhanced in v0.5.0)LOG_MAX_AGE_DAYS: Maximum age of log files in days (default: 30, Enhanced in v0.5.0)LOG_CLEANUP_INTERVAL_HOURS: Log cleanup check interval in hours (default: 24, Enhanced in v0.5.0)debug.log, info.log, warning.log, error.log, critical.logaudit.log with separate retention managementThe canonical catalog is generated from the same ToolDefinitionRegistry used for MCP JSON Schemas, execution dispatch, authorization policy, and audit metadata. See docs/tool-registry.md. The checked-in catalog is verified against the runtime registry by the test suite.
Doris-backed OAuth note: The generated catalog describes global server capabilities. Doris-backed OAuth uses configuration gates for its operation surface. MCP resources are available with resource metadata caching disabled. Reviewed metadata tools are callable when DORIS_OAUTH_DB_TOOLS_ENABLED=true; exec_query and get_sql_explain are callable when their Doris OAuth query/explain gates are enabled. These MySQL-channel operations run through the logged-in Doris user pool, so Doris RBAC is the final data authorization backend. Prompts, ADBC, FE HTTP profile/monitoring, audit/governance, and performance analytics remain closed until they have per-user routing or an explicit service-account/admin design.
Execute the following command to start the server:
./start_server.sh
This command starts the FastAPI application with Streamable HTTP MCP service.
If you want to run only Doris MCP Server in docker:
cd doris-mcp-server docker build -t doris-mcp-server . docker run -d -p <host-port>:3000 -v /*your-host*/doris-mcp-server/.env:/app/.env --name <your-mcp-server-name> doris-mcp-server:latest
The container always listens on port 3000. The bundled Compose deployment publishes it on host port 3000 by default and publishes Grafana on host port 3003, so the two services do not contend for the same port. Override those defaults with MCP_HTTP_PORT and GRAFANA_HTTP_PORT:
MCP_HTTP_PORT=3100 GRAFANA_HTTP_PORT=3103 docker compose up -d
Before starting the bundled stack, create the five ignored secret files used by Compose. No database, MCP, Redis, or Grafana credential is stored in docker-compose.yml, .env.example, or the rendered service environment:
mkdir -p .secrets chmod 700 .secrets python -c 'import secrets; print(secrets.token_urlsafe(32))' > .secrets/doris_password python -c 'import secrets; print(secrets.token_urlsafe(32))' > .secrets/mcp_static_token python -c 'import secrets; print(secrets.token_urlsafe(32))' > .secrets/redis_password python -c 'import secrets; print(secrets.token_urlsafe(32))' > .secrets/grafana_admin_password python -c 'import hashlib, pathlib; p=pathlib.Path(".secrets/doris_password").read_text().rstrip("\n").encode(); print("initial_root_password = *" + hashlib.sha1(hashlib.sha1(p).digest()).hexdigest().upper())' > .secrets/doris_fe_custom.conf chmod 0444 .secrets/* docker compose up -d
doris_fe_custom.conf contains Doris's two-stage SHA-1 password verifier, not the plaintext password, and is mounted as the FE initial-root configuration. The matching plaintext file is mounted only into the services that must authenticate to Doris. Treat both files as credentials. The parent directory remains mode 0700, while the files are host-read-only and container-readable for the non-root MCP process. To keep secret files elsewhere, copy .env.example to .env and change only the COMPOSE_*_FILE host paths.
All third-party images in the Compose model use an explicit upstream version and an immutable OCI digest. Upgrade them deliberately by changing both the version tag and digest, then rerun the deployment contract and transport tests; do not replace them with latest or another floating tag.
The image-level Docker health check uses /live, so a temporary Doris outage does not cause the MCP process to be treated as dead. Compose overrides that probe with /ready and only marks the service ready after the bounded Doris probe succeeds. Both probes use the actual internal listener on port 3000.
Compose allows 127.0.0.1:* and localhost:* Host headers by default. A deployment reached through another hostname, IP address, or reverse proxy must set an explicit comma-separated allowlist; an allow-all * value is rejected:
MCP_ALLOWED_HOSTS='mcp.example.com,mcp.example.com:*' docker compose up -d
Service Endpoints:
http://<host>:<port>/mcp (MCP messages use POST; do not depend on GET or DELETE compatibility behavior)http://<host>:<port>/live — process and protocol service are running; does not depend on Dorishttp://<host>:<port>/ready — returns 200 only when a bounded Doris SELECT 1 probe succeeds; otherwise returns 503http://<host>:<port>/health — backward-compatible liveness alias; do not use it to decide whether to route database workThe readiness probe has a fixed short timeout and exposes only stable status fields, not connection errors or credentials. Stdio mode has no HTTP health surface; supervise the process and use the MCP initialization/discovery handshake for transport-level availability.
Note: The server uses Streamable HTTP for web-based communication, providing unified request/response and streaming capabilities.
Doris MCP Server uses the official Python SDK v2 protocol core for both Streamable HTTP and stdio. The MCP protocol revision used on the wire is independent of the Doris MCP Server package version and the Python SDK package version.
The authoritative protocol references are the MCP 2026-07-28 specification, 2026-07-28 key changes, and Streamable HTTP transport specification.
| Client protocol | Streamable HTTP | stdio | Connection behavior | Doris MCP Server status |
|---|---|---|---|---|
2026-07-28 | Supported and preferred | Supported and preferred | Stateless, self-contained requests; no initialization handshake or protocol session | Covered by modern HTTP and real-process stdio tests |
2025-11-25 | Opt-in at /mcp/legacy | Supported for migration | Legacy initialize is accepted, but the server remains stateless and does not issue Mcp-Session-Id | HTTP adapter is disabled by default; covered by legacy HTTP and stdio tests |
2025-06-18 and older | Not guaranteed | Not guaranteed | Older negotiation and transport behavior is outside the supported compatibility contract | Upgrade the client before connecting |
HTTP+SSE (2024-11-05) | Not supported | Not applicable | The retired separate SSE endpoint is not exposed | Migrate to Streamable HTTP at /mcp |
The HTTP compatibility path for 2025-11-25 is isolated from the modern endpoint and disabled by default. New integrations should target 2026-07-28 at POST /mcp.
Modern clients may call server/discover before any other method to inspect the supported protocol versions, capabilities, and server identity. Discovery is optional; every normal request is still self-contained.
Every modern request must carry these values in params._meta:
io.modelcontextprotocol/protocolVersion: 2026-07-28io.modelcontextprotocol/clientCapabilities: the capabilities available for that request, or an empty objectio.modelcontextprotocol/clientInfo: the client name and version; this is recommended by the specificationFor Streamable HTTP, send one JSON-RPC request per POST /mcp and include:
| Header | Required | Value |
|---|---|---|
Content-Type | Yes | application/json |
Accept | Yes | Both application/json and text/event-stream |
MCP-Protocol-Version | Yes | Must match the protocol version in _meta |
Mcp-Method | Yes | Must match the JSON-RPC method |
Mcp-Name | For tools/call, resources/read, and prompts/get | Must match params.name or params.uri |
Header names are case-insensitive, but method and name values are not. A required header that is missing or disagrees with the request body is rejected with HTTP 400 and the protocol HeaderMismatch error (-32020). Unsupported protocol versions are rejected with UnsupportedProtocolVersion (-32022). If a name or URI is not safe as a plain ASCII header value, encode its UTF-8 bytes as Base64 and send Mcp-Name: =?base64?{value}?= as defined by the transport specification.
Example discovery request:
curl --request POST http://127.0.0.1:3000/mcp \ --header 'Content-Type: application/json' \ --header 'Accept: application/json, text/event-stream' \ --header 'MCP-Protocol-Version: 2026-07-28' \ --header 'Mcp-Method: server/discover' \ --data '{ "jsonrpc": "2.0", "id": 1, "method": "server/discover", "params": { "_meta": { "io.modelcontextprotocol/protocolVersion": "2026-07-28", "io.modelcontextprotocol/clientCapabilities": {}, "io.modelcontextprotocol/clientInfo": { "name": "example-client", "version": "1.0.0" } } } }'
Stdio carries the same JSON-RPC request metadata in the message body, but it does not use HTTP headers. Do not write logs or other diagnostics to stdout in stdio mode; stdout is reserved for MCP protocol messages.
Clients may propagate the W3C traceparent, tracestate, and baggage carrier fields in params._meta, as defined by MCP SEP-414, W3C Trace Context, and W3C Baggage. The same message-level carrier works on Streamable HTTP and stdio; these values are not separate MCP HTTP headers.
When an OpenTelemetry provider and exporter are configured, each MCP operation span is parented to the valid incoming trace context. The active context is available to instrumented downstream work for the lifetime of that operation and is reset before the next request. Trace carrier fields are never passed to the Doris tool, resource, or prompt managers and are never copied into model content or structured results.
The server validates trace carrier values before the SDK propagator sees them. Malformed, oversized, duplicate, or orphaned fields are ignored independently, and warning logs identify only the field name—never its supplied value. Values under credential-like baggage keys such as token, secret, or authorization are replaced with [REDACTED] before propagation. baggage can still contain other deployment-sensitive correlation data, so clients should send only values approved by their telemetry data-handling policy.
resources/list, tools/list, and prompts/list return at most MCP_LIST_PAGE_SIZE entries per response on both Streamable HTTP and stdio. Results are ordered by their stable resource URI, tool name, or prompt name. When nextCursor is present, pass that opaque value as the next request's cursor; do not parse or construct cursors.
A cursor is an HMAC-authenticated explicit state handle bound to its list type, scope, resource, current visible-list snapshot, authorization context, and expiry. It does not depend on an MCP protocol session, transport connection, or worker-local memory. Reusing it for another list, after visibility changes, after expiry, after modification, or under another principal returns Invalid Params. Restart the listing without a cursor in that case. This prevents a multi-page traversal from silently duplicating, dropping, or crossing permission-scoped entries.
The server generates a launch-local handle secret by default and passes it to all workers created by the same CLI process. Set one shared MCP_STATE_HANDLE_SECRET on independently launched replicas when a load balancer can route consecutive pages to different instances. Handle payloads contain bounded continuation metadata and are signed rather than encrypted; credentials, SQL text, and query results must never be placed in them. See ADR 0002.
List failures are never represented as successful empty collections. A Doris metadata outage returns List backend unavailable; a Doris metadata permission failure returns List operation permission denied; and an unexpected tool/resource/prompt registry failure returns Internal server error. These responses use JSON-RPC code -32603 and include only the list operation and a bounded error category/code, never backend exception text. A successful response with an empty resources, tools, or prompts array therefore means that the caller's visible collection is genuinely empty. The same contract applies to Streamable HTTP and stdio, and a failed request does not prevent the next list request from succeeding.
The server does not currently advertise or serve subscriptions/listen. Tools and prompts have no runtime mutation channel, and Doris catalog metadata does not provide this process with a reliable cross-worker change-event source. Cache expiry and periodic catalog polling are not treated as change events.
Consequently, MCP 2026-07-28 discovery reports listChanged: false for tools, prompts, and resources and subscribe: false for resources. A subscriptions/listen request returns Method not found; clients should refresh lists explicitly. Streamable HTTP and true subprocess stdio tests enforce this boundary. See the subscription decision record for the conditions required before the capability can be enabled.
Tool inputSchema and outputSchema use JSON Schema 2020-12. The server validates every visible tool definition before advertising or executing it, then validates each call's arguments before invoking Doris. Invalid arguments return Invalid Params without echoing rejected values. Successful structured results are also checked whenever a tool declares outputSchema; a server-side schema mismatch is hidden behind Internal error.
Schemas are self-contained. Only same-document $ref and $dynamicRef fragments are accepted; the server never fetches a schema over HTTP, from a file URI, or from a relative URI. Recursive references are rejected by the bounded validation policy. The default hard limits per schema are 64 KiB, 2,048 nodes, depth 32, 64 composition branches, and 64 references. Each input or structured output is limited to 1 MiB, 10,000 nodes, depth 32, and 262,144 characters per string. At most 16 validation violations are reported, and reports contain only instance paths and failed keywords.
These checks run in the shared protocol handler, so Streamable HTTP, modern stdio, and legacy stdio use the same enforcement. MCP 2026-07-28 clients can also receive array, string, number, or boolean structuredContent when a matching outputSchema is declared.
2026-07-28-capable MCP SDK./mcp endpoint for Streamable HTTP, but send every JSON-RPC message as its own POST request.initialize, notifications/initialized, Mcp-Session-Id, and any sticky-session dependency._meta. Add client identity on every request where possible.MCP-Protocol-Version and Mcp-Method to every HTTP request, plus Mcp-Name for named tool, resource, and prompt requests.Last-Event-ID, and resumable SSE behavior. Reissue an interrupted request with a new JSON-RPC request ID.resultType field in modern results and handle protocol-defined errors such as -32020, -32021, and -32022.Clients that cannot migrate immediately may keep the 2025-11-25 initialize flow over stdio. For Streamable HTTP, an operator must explicitly set ENABLE_LEGACY_HTTP_ADAPTER=true and point that client to /mcp/legacy; /mcp never falls back to the legacy transport. The adapter remains stateless and does not create an HTTP protocol session.
127.0.0.1, localhost, or ::1. The transport validates both Host and Origin to protect against DNS rebinding.0.0.0.0 does not authorize arbitrary Host or Origin values.ALLOW_UNAUTHENTICATED_NON_LOOPBACK=true is an explicit dangerous override for isolated testing only; do not use it as a deployment shortcut.WORKERS=1.Interaction with the Doris MCP Server requires an MCP Client. The client connects to the server‘s Streamable HTTP endpoint and sends requests according to the MCP specification to invoke the server’s tools.
Main Interaction Flow:
server/discover to inspect supported protocol versions, capabilities, and identity.tools/list to get the supported tools, descriptions, and parameter schemas.tools/call request with its required _meta, name, and arguments.name: get_table_schemaarguments: Include table_name, db_name, catalog_name.content or isError.Legacy 2025-11-25 stdio clients initialize as before. HTTP clients require the explicitly enabled /mcp/legacy adapter and remain subject to the stateless compatibility limits described above.
The Doris MCP Server supports catalog federation, enabling interaction with multiple data catalogs (internal Doris tables and external data sources like Hive, MySQL, etc.) within a unified interface.
get_db_list, get_db_table_list, get_table_schema, etc.) support an optional catalog_name parameter to query specific catalogs.get_catalog_list to discover available catalogs and their types.All SQL queries MUST use three-part naming for table references:
internal.database_name.table_namecatalog_name.database_name.table_nameGet Available Catalogs:
{ "tool_name": "get_catalog_list", "arguments": {"random_string": "unique_id"} }
Get Databases in Specific Catalog:
{ "tool_name": "get_db_list", "arguments": {"random_string": "unique_id", "catalog_name": "mysql"} }
Query Internal Catalog:
{ "tool_name": "exec_query", "arguments": { "random_string": "unique_id", "sql": "SELECT COUNT(*) FROM internal.ssb.customer" } }
Query External Catalog:
{ "tool_name": "exec_query", "arguments": { "random_string": "unique_id", "sql": "SELECT COUNT(*) FROM mysql.ssb.customer" } }
Cross-Catalog Query:
{ "tool_name": "exec_query", "arguments": { "random_string": "unique_id", "sql": "SELECT i.c_name, m.external_data FROM internal.ssb.customer i JOIN mysql.test.user_info m ON i.c_custkey = m.customer_id" } }
The Doris MCP Server includes a comprehensive enterprise-grade security framework with advanced authentication, authorization, SQL security validation, and data masking capabilities enhanced in v0.6.0.
Configure the new authentication system with granular control:
# Generate a deployment-specific token before enabling static authentication. export TOKEN_ADMIN="$(python -c 'import secrets; print(secrets.token_urlsafe(32))')" # Individual Authentication Control (New in v0.6.0) ENABLE_TOKEN_AUTH=true # Enable token-based authentication ENABLE_JWT_AUTH=false # Enable JWT authentication ENABLE_OAUTH_AUTH=false # Enable OAuth authentication # Token Management (New in v0.6.0) TOKEN_FILE_PATH=tokens.json # Token configuration file TOKEN_HOT_RELOAD=true # Enable hot reloading # Legacy Configuration (Deprecated) # AUTH_TYPE=token # Use individual switches instead
The repository ships no usable static token or legacy secret. Startup fails when static token authentication is enabled without at least one active, high-entropy TOKEN_<ID> value or an equivalent entry in TOKEN_FILE_PATH.
External OAuth is fail-closed. Before the server requests user information, it uses a trusted RFC 7662 introspection endpoint and requires an active, unexpired token with the exact configured issuer, expected audience, MCP resource binding, required scopes, and a subject. A successful userinfo request is not accepted as proof that a token was issued for this MCP server. The userinfo subject must also match the introspected token subject.
OAUTH_RESOURCE is sent on authorization-code and refresh-token requests. OAUTH_AUDIENCE defaults to that resource URI. OAUTH_REQUIRED_SCOPE defaults to all scopes in OAUTH_SCOPE; scopes returned by the authorization server but not present in OAUTH_SCOPE are excluded from the authentication context.
ENABLE_OAUTH_AUTH=true OAUTH_CLIENT_ID=your_oauth_client_id OAUTH_CLIENT_SECRET=your_oauth_client_secret OAUTH_REDIRECT_URI=https://mcp.example.com/auth/callback OAUTH_ISSUER=https://issuer.example.com OAUTH_RESOURCE=https://mcp.example.com/mcp OAUTH_AUDIENCE=https://mcp.example.com/mcp OAUTH_INTROSPECTION_URL=https://issuer.example.com/introspect OAUTH_USERINFO_URL=https://issuer.example.com/userinfo OAUTH_SCOPE="tool:list tool:call:exec_query resource:list resource:read" OAUTH_REQUIRED_SCOPE="tool:list resource:read"
An authorization-server discovery document may provide the introspection and userinfo endpoints, but its issuer must exactly match OAUTH_ISSUER. Dedicated OAUTH_INTROSPECTION_CLIENT_ID and OAUTH_INTROSPECTION_CLIENT_SECRET values may be configured; otherwise the regular OAuth client credentials are used. Remote issuer, discovery, introspection, and userinfo URLs must use HTTPS.
For HTTP deployments, the server publishes RFC 9728 metadata at /.well-known/oauth-protected-resource. Missing or invalid credentials receive an HTTP 401 Bearer challenge containing resource_metadata and the minimum configured scopes. A valid token that lacks an operation scope receives HTTP 403 with error="insufficient_scope" and the exact scope required for step-up authorization. Access tokens and provider-internal error details are not copied into these responses. These HTTP OAuth challenges do not apply to stdio transport, where credentials are supplied through the local process environment.
External OAuth operation scopes are exact: listing tools requires tool:list, calling a tool requires tool:call:<tool-name>, listing and reading resources require resource:list and resource:read, and Prompt operations require prompt:list and prompt:get. Add every callable tool scope to OAUTH_SCOPE; * and unrelated scopes never satisfy an operation check. Static token, JWT, anonymous loopback, and local stdio authorization keep their existing non-OAuth permission behavior.
Doris-backed OAuth is a separate OAuth mode where Doris itself is the authorization backend. The MCP client discovers this server‘s OAuth metadata, the user signs in with a Doris username and password, the server validates those credentials by creating a per-user Doris connection pool, and issued doa_ access tokens route tool calls through that Doris user’s pool. MCP scopes control which MCP operations can be called; Doris RBAC controls which catalogs, databases, tables, and metadata the user can see.
This mode is not the same as external OAuth/OIDC. ENABLE_DORIS_OAUTH_AUTH=true conflicts with ENABLE_OAUTH_AUTH=true, OAUTH_ENABLED=true, and legacy AUTH_TYPE=oauth; startup fails fast if both modes are configured. A standard MCP agent enters one MCP URL and should discover exactly one OAuth behavior for that URL, so the existing /auth/* external OAuth login flow is not used in Doris-backed OAuth mode.
Each Doris OAuth token record is bound to the resource selected during authorization. The protected MCP endpoint accepts only the exact canonical resource ${DORIS_OAUTH_BASE_URL}/mcp; a token issued for the authorization server or any other resource is rejected with an invalid_token challenge before a per-user Doris pool is used. Clients must send that canonical resource in both the authorization request and authorization-code token request. The token endpoint returns RFC 8707 invalid_target if the value is missing or does not exactly match the resource bound to the authorization code.
The following example is for local development on a single worker:
TRANSPORT=http WORKERS=1 DORIS_HOST=localhost DORIS_PORT=9030 DORIS_USER=root DORIS_PASSWORD=<service-account-password> DORIS_DATABASE=information_schema ENABLE_DORIS_OAUTH_AUTH=true DORIS_OAUTH_BASE_URL=http://localhost:3000 ENABLE_OAUTH_AUTH=false DORIS_OAUTH_DB_TOOLS_ENABLED=true DORIS_OAUTH_DB_TOOL_ALLOWLIST=get_db_list,get_db_table_list,get_table_schema,get_table_comment,get_table_column_comments,get_table_indexes,get_catalog_list DORIS_OAUTH_QUERY_TOOLS_ENABLED=true DORIS_OAUTH_EXPLAIN_TOOLS_ENABLED=true # Optional: let Doris RBAC, not the legacy MCP SQL guard, decide DDL/DML. ENABLE_SECURITY_CHECK=false
The configured service Doris account is still required by startup validation and by non-Doris-OAuth compatibility paths. Doris-backed OAuth requests are fail-closed if the per-user pool is missing and must not fall back to the service/global account.
DORIS_OAUTH_DB_TOOLS_ENABLED=true opens the reviewed metadata bucket. The reviewed tools are:
get_db_listget_db_table_listget_table_schemaget_table_commentget_table_column_commentsget_table_indexesget_catalog_listFor normal MCP OAuth flows, clients do not need to pass a long --scopes list. If the OAuth request omits scope, the server grants the configured Doris OAuth capability envelope. For MySQL-channel operations, Doris RBAC decides whether the logged-in Doris user can actually read metadata, run SQL, or explain SQL.
DORIS_OAUTH_QUERY_TOOLS_ENABLED=true opens exec_query. DORIS_OAUTH_EXPLAIN_TOOLS_ENABLED=true opens get_sql_explain. If ENABLE_SECURITY_CHECK=true, the legacy MCP SQL security layer can still reject some SQL before Doris sees it. Set ENABLE_SECURITY_CHECK=false when the intended policy is to let Doris RBAC decide SQL/DDL/DML.
Doris-backed OAuth still does not open prompts, ADBC, FE HTTP profile/monitoring, audit/governance, or performance analytics in this phase unless those paths are separately routed through per-user credentials or given an explicit service-account/admin design.
The preferred client-registration order is:
client_id.Authorization-server metadata advertises client_id_metadata_document_supported=true. A CIMD must be a JSON object whose client_id exactly equals the requested URL and whose client_name and redirect_uris are valid. This server currently accepts public CIMD clients with token_endpoint_auth_method=none, authorization-code plus optional refresh-token grants, the code response type, and exact redirect URI matching. Native clients may use a reverse-domain custom scheme or loopback HTTP URI; web clients must use non-loopback HTTPS.
CIMD retrieval is fail-closed. The URL must use HTTPS, contain a path, and have no userinfo, fragment, backslash, or dot path segment. The resolver rejects special-use destination addresses, pins validated DNS results for the request, does not follow redirects, requires JSON, limits the response to 5 KiB by default, rejects embedded shared secrets or private key material, and caches only valid documents according to HTTP cache controls. Loopback metadata hosts are accepted only when the Doris OAuth issuer is itself a loopback development issuer. The login page displays the client and redirect hostnames and warns before a localhost redirect.
The CIMD controls can be adjusted with DORIS_OAUTH_CIMD_FETCH_TIMEOUT_SECONDS, DORIS_OAUTH_CIMD_MAX_DOCUMENT_BYTES, DORIS_OAUTH_CIMD_DEFAULT_CACHE_SECONDS, DORIS_OAUTH_CIMD_MAX_CACHE_SECONDS, and DORIS_OAUTH_CIMD_MAX_CLIENTS.
DCR remains available for older clients when DORIS_OAUTH_DYNAMIC_CLIENT_REGISTRATION_MODE permits it. DCR requests must include application_type as either native or web; the same type-specific and exact redirect URI rules apply. Production DCR still requires ENABLE_DORIS_OAUTH_PRODUCTION_DCR=true.
Authorization success and redirectable error responses include the exact authorization-server issuer in the RFC 9207 iss parameter. Discovery advertises authorization_response_iss_parameter_supported=true; clients must compare the returned value with the discovered issuer without URI normalization before accepting the response.
Doris-backed OAuth is currently single-process and single-worker:
WORKERS=1 is required. WORKERS=0 expands to CPU count and fails when Doris-backed OAuth is enabled.If an access token is otherwise valid but its Doris user pool is gone, the request fails with login required / DORIS_OAUTH_POOL_MISSING. The server does not store raw Doris passwords for automatic pool reconstruction.
For production deployments:
DORIS_OAUTH_BASE_URL for any non-loopback address.DORIS_OAUTH_ALLOW_INSECURE_HTTP=false; non-loopback http:// is rejected unless explicitly overridden for development.DORIS_OAUTH_TRUST_PROXY_HEADERS only behind a controlled reverse proxy and set DORIS_OAUTH_TRUSTED_PROXY_CIDRS.doa_ prefix as reserved for Doris-backed OAuth access tokens; static tokens and JWT bearer values must not use it.ENABLE_DORIS_OAUTH_PRODUCTION_DCR=true.Managed token creation returns the bearer value once and writes only its digest to tokens.json. For manual provisioning, generate the bearer value and digest outside the repository, store the bearer value in the client secret store, and place only the digest in the server file:
python - <<'PY' import hashlib import secrets token = secrets.token_urlsafe(32) print(f"Bearer token (store once): {token}") print(f"token_digest: sha256:{hashlib.sha256(token.encode()).hexdigest()}") PY
The v2 file format uses the generated digest:
{ "version": "2.0", "tokens": [ { "token_id": "customer-a-token", "token_digest": "sha256:0000000000000000000000000000000000000000000000000000000000000000", "created_at": "2026-07-29T00:00:00Z", "expires_at": null, "last_used": null, "description": "Customer A dedicated database access", "is_active": true, "database_config": { "host": "customer-a-db.example.com", "port": 9030, "user": "customer_a_user", "password": "secure_password", "database": "customer_a_data", "charset": "UTF8", "fe_http_port": 8030 } } ] }
Replace the all-zero example with the generated digest; it is intentionally not a usable credential. Managed writes are atomic and set the file mode to 0600. Version 1 files containing token plaintext are accepted once for migration, then immediately replaced with version 2 digest-only records. The server cannot recover or display the original bearer value from token_digest.
The system automatically detects and applies configuration changes:
# Client authentication with token auth_info = { "type": "token", "token": "your_jwt_token", "session_id": "unique_session_id" }
# Client authentication with username/password auth_info = { "type": "basic", "username": "analyst", "password": "secure_password", "session_id": "unique_session_id" }
The system supports four security levels with hierarchical access control:
| Security Level | Access Scope | Typical Use Cases |
|---|---|---|
| Public | Unrestricted access | Public reports, general statistics |
| Internal | Company employees | Internal dashboards, business metrics |
| Confidential | Authorized personnel | Customer data, financial reports |
| Secret | Senior management | Strategic data, sensitive analytics |
Configure user roles and permissions:
# Example role configuration role_permissions = { "data_analyst": { "security_level": "internal", "permissions": ["read_data", "execute_query"], "allowed_tables": ["sales", "products", "orders"] }, "data_admin": { "security_level": "confidential", "permissions": ["read_data", "execute_query", "admin"], "allowed_tables": ["*"] }, "executive": { "security_level": "secret", "permissions": ["read_data", "execute_query", "admin"], "allowed_tables": ["*"] } }
The system automatically validates SQL queries for security risks:
Configure blocked SQL operations using environment variables (New in v0.4.2):
# Enable/disable SQL security check (New in v0.4.2) ENABLE_SECURITY_CHECK=true # Customize blocked keywords via environment variable (New in v0.4.2) BLOCKED_KEYWORDS="DROP,DELETE,TRUNCATE,ALTER,CREATE,INSERT,UPDATE,GRANT,REVOKE,EXEC,EXECUTE,SHUTDOWN,KILL" # Maximum query complexity score MAX_QUERY_COMPLEXITY=100
Default Blocked Keywords (Unified in v0.4.2):
The system automatically detects and blocks:
UNION SELECT attacksOR 1=1 patternsSLEEP(), WAITFOR functions--, /**/ patterns;# This query would be blocked dangerous_sql = "SELECT * FROM users WHERE id = 1; DROP TABLE users;" # This query would be allowed safe_sql = "SELECT name, email FROM users WHERE department = 'sales'"
Configure automatic data masking for sensitive information:
# Default masking rules masking_rules = [ { "column_pattern": r".*phone.*|.*mobile.*", "algorithm": "phone_mask", "parameters": { "mask_char": "*", "keep_prefix": 3, "keep_suffix": 4 }, "security_level": "internal" }, { "column_pattern": r".*email.*", "algorithm": "email_mask", "parameters": {"mask_char": "*"}, "security_level": "internal" }, { "column_pattern": r".*id_card.*|.*identity.*", "algorithm": "id_mask", "parameters": { "mask_char": "*", "keep_prefix": 6, "keep_suffix": 4 }, "security_level": "confidential" } ]
| Algorithm | Description | Example |
|---|---|---|
phone_mask | Masks phone numbers | 138****5678 |
email_mask | Masks email addresses | j***n@example.com |
id_mask | Masks ID card numbers | 110101****1234 |
name_mask | Masks personal names | 张*明 |
partial_mask | Partial masking with ratio | abc***xyz |
Add custom masking rules in your configuration:
# Custom masking rule custom_rule = { "column_pattern": r".*salary.*|.*income.*", "algorithm": "partial_mask", "parameters": { "mask_char": "*", "mask_ratio": 0.6 }, "security_level": "confidential" }
# Generate this outside source control, then inject it into the process. export TOKEN_SECURITY_ADMIN="$(python -c 'import secrets; print(secrets.token_urlsafe(32))')" export ENABLE_TOKEN_AUTH=true ENABLE_MASKING=true MAX_RESULT_ROWS=10000 BLOCKED_SQL_OPERATIONS=DROP,DELETE,TRUNCATE,ALTER MAX_QUERY_COMPLEXITY=100 ENABLE_AUDIT=true
# Configure sensitive tables with security levels sensitive_tables = { "user_profiles": "confidential", "payment_records": "secret", "employee_salaries": "secret", "customer_data": "confidential", "public_reports": "public" }
WORKERS=1, rely on Doris RBAC for MySQL-channel data access, and expose only operations that are configured and verified to use the logged-in Doris user's credentials.The system provides comprehensive security monitoring:
# Security audit log example { "timestamp": "2024-01-15T10:30:00Z", "user_id": "analyst_user", "action": "query_execution", "resource": "customer_data", "result": "blocked", "reason": "insufficient_permissions", "risk_level": "medium" }
⚠️ Important: Always test security configurations in a development environment before deploying to production. Regularly review and update security policies based on your organization's requirements.
You can connect Cursor to this MCP server using Stdio mode (recommended) or Streamable HTTP mode.
Stdio mode allows Cursor to manage the server process directly. Configuration is done within Cursor's MCP Server settings file (typically ~/.cursor/mcp.json or similar).
Install the package from PyPI and configure Cursor to use it:
pip install doris-mcp-server
Configure Cursor: Add an entry like the following to your Cursor MCP configuration:
{ "mcpServers": { "doris-stdio": { "command": "doris-mcp-server", "args": ["--transport", "stdio"], "env": { "DORIS_HOST": "127.0.0.1", "DORIS_PORT": "9030", "DORIS_USER": "root", "DORIS_PASSWORD": "your_db_password" } } } }
If you have uv installed and want to run from source:
uv run --project /path/to/doris-mcp-server doris-mcp-server
Note: Replace /path/to/doris-mcp-server with the actual absolute path to your project directory.
Configure Cursor: Add an entry like the following to your Cursor MCP configuration:
{ "mcpServers": { "doris-stdio": { "command": "uv", "args": ["run", "--project", "/path/to/your/doris-mcp-server", "doris-mcp-server"], "env": { "DORIS_HOST": "127.0.0.1", "DORIS_PORT": "9030", "DORIS_USER": "root", "DORIS_PASSWORD": "your_db_password" } } } }
Streamable HTTP mode requires you to run the MCP server independently first, and then configure Cursor to connect to it.
Configure .env: Ensure your database credentials and any other necessary settings are correctly configured in the .env file within the project directory.
Start the Server: Run the server from your terminal in the project's root directory:
./start_server.sh
This script reads the .env file and starts the Streamable HTTP server on 127.0.0.1 by default. A non-loopback listener requires at least one authentication method.
Configure Cursor: Add an entry like the following to your Cursor MCP configuration, pointing to the running server's Streamable HTTP endpoint:
{ "mcpServers": { "doris-http": { "url": "http://127.0.0.1:3000/mcp" } } }
Note: Adjust the host/port if your server runs on a different address. The
/mcpendpoint is the unified Streamable HTTP interface.
After configuring either mode in Cursor, you should be able to select the server (e.g., doris-stdio or doris-http) and use its tools.
Or add the following to your Kiro MCP config file (~/.kiro/settings/mcp.json for global, or .kiro/settings/mcp.json for project-scoped). See the Kiro MCP documentation for more details.
{ "mcpServers": { "doris-stdio": { "command": "doris-mcp-server", "args": ["--transport", "stdio"], "env": { "DORIS_HOST": "127.0.0.1", "DORIS_PORT": "9030", "DORIS_USER": "root", "DORIS_PASSWORD": "your_db_password" } } } }
doris-mcp-server/ ├── doris_mcp_server/ # Main server package │ ├── main.py # Main entry point and FastAPI app │ ├── multiworker_app.py # Multi-worker application module (New in v0.6.0) │ ├── auth/ # Authentication modules (New in v0.6.0) │ │ ├── token_manager.py # Enterprise token management with hot reload │ │ ├── jwt_manager.py # JWT authentication provider │ │ ├── oauth_provider.py # OAuth authentication provider │ │ ├── oauth_handlers.py # OAuth HTTP endpoint handlers │ │ ├── token_handlers.py # Token management HTTP endpoints │ │ ├── auth_middleware.py # Authentication middleware │ │ └── __init__.py │ ├── tools/ # MCP tools implementation │ │ ├── tools_manager.py # Centralized tools management and registration │ │ ├── resources_manager.py # Resource management and metadata exposure │ │ ├── prompts_manager.py # Intelligent prompt templates for data analysis │ │ └── __init__.py │ ├── utils/ # Core utility modules │ │ ├── config.py # Configuration management with validation │ │ ├── db.py # Enhanced database connection management with token binding (Enhanced in v0.6.0) │ │ ├── query_executor.py # High-performance SQL execution with caching │ │ ├── security.py # Advanced security management and authentication (Enhanced in v0.6.0) │ │ ├── schema_extractor.py # Metadata extraction with catalog federation │ │ ├── analysis_tools.py # Data analysis and performance monitoring │ │ ├── data_governance_tools.py # Data lineage and freshness monitoring (v0.5.0) │ │ ├── data_quality_tools.py # Comprehensive data quality analysis (v0.5.0) │ │ ├── data_exploration_tools.py # Advanced statistical analysis (v0.5.0) │ │ ├── security_analytics_tools.py # Access pattern analysis (v0.5.0) │ │ ├── dependency_analysis_tools.py # Impact analysis and dependency mapping (v0.5.0) │ │ ├── performance_analytics_tools.py # Query optimization and capacity planning (v0.5.0) │ │ ├── adbc_query_tools.py # High-performance Arrow Flight SQL operations (v0.5.0) │ │ ├── logger.py # Logging configuration │ │ └── __init__.py │ └── __init__.py ├── doris_mcp_client/ # MCP client implementation │ ├── client.py # Unified MCP client for testing and integration │ ├── README.md # Client documentation │ └── __init__.py ├── logs/ # Log files directory ├── tokens.json # Token configuration file (New in v0.6.0) ├── README.md # This documentation ├── CHANGELOG.md # Tagged release history and unreleased changes ├── .env.example # Environment variables template ├── requirements.txt # Python dependencies ├── pyproject.toml # Project configuration and entry points ├── uv.lock # UV package manager lock file ├── generate_requirements.py # Requirements generation script ├── start_server.sh # Server startup script └── restart_server.sh # Server restart script
This section outlines the process for adding new MCP tools to the Doris MCP Server, based on the unified modular architecture with centralized tool management.
The server provides comprehensive utility modules for common database operations:
doris_mcp_server/utils/db.py: Database connection management with connection pooling and health monitoring.doris_mcp_server/utils/query_executor.py: High-performance SQL execution with advanced caching, optimization, and performance monitoring.doris_mcp_server/utils/schema_extractor.py: Metadata extraction with full catalog federation support.doris_mcp_server/utils/security.py: Comprehensive security management, SQL validation, and data masking.doris_mcp_server/utils/analysis_tools.py: Advanced data analysis and statistical tools.doris_mcp_server/utils/config.py: Configuration management with validation.doris_mcp_server/utils/data_governance_tools.py: Data lineage tracking and freshness monitoring (New in v0.5.0).doris_mcp_server/utils/data_quality_tools.py: Comprehensive data quality analysis framework (New in v0.5.0).doris_mcp_server/utils/adbc_query_tools.py: High-performance Arrow Flight SQL operations (New in v0.5.0).Add a private handler to DorisToolsManager in doris_mcp_server/tools/tools_manager.py. Handler names follow _<tool_name>_tool; the registry resolves this name and validates that the handler exists during manager construction.
Example: Adding a new analysis tool:
# In doris_mcp_server/tools/tools_manager.py async def _your_new_analysis_tool( self, arguments: dict[str, Any], ) -> dict[str, Any]: """ Your new analysis tool implementation Args: arguments: Tool arguments from MCP client Returns: JSON-serializable tool result """ try: # Use existing utilities result = await self.query_executor.execute_sql_for_mcp( sql="SELECT COUNT(*) FROM your_table", max_rows=arguments.get("max_rows", 100) ) return result except Exception as e: logger.error(f"Tool execution failed: {str(e)}", exc_info=True) return {"success": False, "error": "Analysis failed"}
Add one Tool schema to doris_mcp_server/tools/tool_catalog.py::build_tool_registry, then classify its policy once in doris_mcp_server/tools/tool_registry.py. Do not add a decorator wrapper or an if/elif dispatch branch:
# In doris_mcp_server/tools/tool_catalog.py Tool( name="your_new_analysis_tool", description="Description of your new analysis tool", input_schema={ "type": "object", "properties": { "parameter1": { "type": "string", "description": "Description of parameter1" }, "parameter2": { "type": "integer", "description": "Description of parameter2", "default": 100 } }, "required": ["parameter1"], }, )
The registry derives the execution handler, safe audit fields, and generated documentation from that definition. Add the name to exactly one policy class: metadata, query, explain, or restricted. Run the registry tests and refresh docs/tool-registry.md from ToolDefinitionRegistry.render_markdown(); the test suite rejects documentation drift.
For more complex tools, you can leverage the comprehensive framework:
Test your new tool using the included MCP client:
# Using doris_mcp_client/client.py from doris_mcp_client.client import DorisUnifiedMCPClient async def test_new_tool(): client = DorisUnifiedMCPClient() result = await client.call_tool("your_new_analysis_tool", { "parameter1": "test_value", "parameter2": 50 }) print(result)
Run the release test gate with:
uv run pytest -q -W error uv run coverage json -o coverage.json uv run python test/deployment/check_coverage_domains.py coverage.json
The test suite enforces at least 55% coverage across the repository. The generated coverage report is also checked at an 80% floor for the protocol, authentication, and core manager domains, so high-risk runtime code cannot be hidden by unrelated coverage.
The project includes a unified MCP client (doris_mcp_client/) for testing and integration purposes. The client supports multiple connection modes and provides a convenient interface for interacting with the MCP server.
For detailed client documentation, see doris_mcp_client/README.md.
Contributions are welcome via Issues or Pull Requests.
This project is licensed under the Apache 2.0 License. See the LICENSE file for details.
A: This is a common issue. The main reason is that these models need more explicit guidance to correctly use MCP tools. It's recommended to add the following instruction prompt for the model:
<instruction> 尽可能使用MCP工具完成任务,仔细阅读每个工具的注解、方法名、参数说明等内容。请按照以下步骤操作: 1. 仔细分析用户的问题,从已有的Tools列表中匹配最合适的工具。 2. 确保工具名称、方法名和参数完全按照工具注释中的定义使用,不要自行创造工具名称或参数。 3. 传入参数时,严格遵循工具注释中规定的参数格式和要求。 4. 调用工具时,根据需要直接调用工具,但参数请求参考以下请求格式:{"mcp_sse_call_tool": {"tool_name": "$tools_name", "arguments": "{}"}} 5. 输出结果时,不要包含任何XML标签,仅返回纯文本内容。 <input> 用户问题:user_query </input> <output> 返回工具调用结果或最终答案,以及对结果的分析。 </output> </instruction>
<instruction> Use MCP tools to complete tasks as much as possible. Carefully read the annotations, method names, and parameter descriptions of each tool. Please follow these steps: 1. Carefully analyze the user's question and match the most appropriate tool from the existing Tools list. 2. Ensure tool names, method names, and parameters are used exactly as defined in the tool annotations. Do not create tool names or parameters on your own. 3. When passing parameters, strictly follow the parameter format and requirements specified in the tool annotations. 4. When calling tools, call them directly as needed, but refer to the following request format for parameters: {"mcp_sse_call_tool": {"tool_name": "$tools_name", "arguments": "{}"}} 5. When outputting results, do not include any XML tags, return plain text content only. <input> User question: user_query </input> <output> Return tool call results or final answer, along with analysis of the results. </output> </instruction>
If you have further requirements for the returned results, you can describe the specific requirements in the <output> tag.
A: You can configure database connections in several ways:
Environment Variables (Recommended):
export DORIS_HOST="your_doris_host" export DORIS_PORT="9030" export DORIS_USER="root" export DORIS_PASSWORD="your_password"
Command Line Arguments:
doris-mcp-server --db-host your_host --db-port 9030 --db-user root --db-password your_password
Configuration File: Modify the corresponding configuration items in the .env file.
A: Configure SQL, FE HTTP, and BE HTTP endpoints independently when a proxy, tunnel, or split network exposes them at different addresses:
# SQL/MySQL protocol endpoint DORIS_HOST=sql-gateway.internal DORIS_PORT=9030 # FE HTTP endpoint; omit DORIS_FE_HTTP_HOST to reuse DORIS_HOST DORIS_FE_HTTP_HOST=fe-http-proxy.internal DORIS_FE_HTTP_PORT=8030 # Explicit BE HTTP allowlist DORIS_BE_HOSTS=10.1.1.100,10.1.1.101,10.1.1.102 DORIS_BE_WEBSERVER_PORT=8040
BE HTTP endpoints are never inferred from SHOW BACKENDS: SQL metadata is not an outbound HTTP allowlist. If DORIS_BE_HOSTS is empty, BE HTTP metrics are disabled. DORIS_FE_HTTP_HOST falls back to DORIS_HOST only for backward compatibility; an explicit value is used for every FE monitoring, profile, trace, and table-size HTTP request. FE and BE requests use only configured hosts and ports, pin validated DNS results for each request, reject metadata/link-local destinations, disable redirects, and enforce connection/read/total timeouts plus a response byte limit. Private and loopback addresses remain available for normal internal Doris deployments and SSH tunnels.
A: The tools provide both truncated content and complete files for LLM analysis:
Get Analysis Results:
{ "content": "Truncated plan for immediate review", "file_path": "/tmp/explain_12345.txt", "is_content_truncated": true }
LLM Analysis Workflow:
Configure Content Size:
MAX_RESPONSE_CONTENT_SIZE=4096 # Adjust as needed
A: Set the following configurations in your .env file:
# Enable data masking ENABLE_MASKING=true # Set maximum result rows MAX_RESULT_ROWS=10000
A:
Recommendations:
A: Try the following solutions:
Increase timeout settings:
# Set in .env file QUERY_TIMEOUT=60 CONNECTION_TIMEOUT=30
Check network connectivity:
# /live verifies the process; /ready also verifies Doris curl --fail http://localhost:3000/live curl --fail http://localhost:3000/ready
Optimize connection pool configuration:
DORIS_MAX_CONNECTIONS=20
at_eof connection errors? (Completely Fixed in v0.5.0)A: Version 0.5.0 has completely resolved the critical at_eof connection errors through comprehensive connection pool redesign:
at_eof errors occurred due to connection pool pre-creation and improper connection state managementConnection Pool Strategy Overhaul:
min_connections from default to 0 to prevent pre-creation issuesEnhanced Health Monitoring:
Intelligent Recovery System:
Performance Optimizations:
# Monitor connection pool health in real-time tail -f logs/doris_mcp_server_info.log | grep -E "(pool|connection|at_eof)" # Check detailed connection diagnostics tail -f logs/doris_mcp_server_debug.log | grep "connection health" # Check process liveness and Doris readiness curl --fail http://localhost:8000/live curl --fail http://localhost:8000/ready
# Recommended connection pool settings in .env DORIS_MAX_CONNECTIONS=20 # Adjust based on workload CONNECTION_TIMEOUT=30 # Connection establishment timeout QUERY_TIMEOUT=60 # Query execution timeout # Health monitoring settings HEALTH_CHECK_INTERVAL=60 # Pool health check frequency
Result: The redesigned lifecycle reduces stale-connection failures and improves recovery behavior. Validate the configured pool under the target workload before production use.
A: New integrations should use MCP 2026-07-28. Doris MCP Server accepts the 2025-11-25 initialization flow over stdio and, when ENABLE_LEGACY_HTTP_ADAPTER=true, at the isolated /mcp/legacy HTTP endpoint. The modern /mcp endpoint is POST-only and never falls back to the legacy transport. Older revisions and the retired HTTP+SSE transport are not part of the supported compatibility contract.
Do not infer wire-protocol support from the Doris MCP Server package version or the Python mcp dependency version. See MCP Protocol Support and Migration for the request metadata, HTTP headers, migration steps, and deployment limits.
A: ADBC (Arrow Flight SQL) provides 3-10x performance improvements for large datasets:
ADBC Dependencies (automatically included in v0.5.0+):
# ADBC dependencies are now included by default in doris-mcp-server>=0.5.0 # No separate installation required
Configure Arrow Flight SQL Ports:
# Add to your .env file FE_ARROW_FLIGHT_SQL_PORT=8096 BE_ARROW_FLIGHT_SQL_PORT=8097
Optional ADBC Customization:
# Customize ADBC behavior (optional) ADBC_DEFAULT_MAX_ROWS=10000 ADBC_DEFAULT_TIMEOUT=120 ADBC_DEFAULT_RETURN_FORMAT=pandas # arrow/pandas/dict
Test ADBC Connection:
# Use get_adbc_connection_info tool to verify setup # Should show "status": "ready" and port connectivity
A: The 7 new analytics tools provide comprehensive data governance capabilities:
Data Quality Analysis:
{ "tool_name": "analyze_data_quality", "arguments": { "table_name": "customer_data", "analysis_scope": "comprehensive", "sample_size": 100000 } }
Column Lineage Tracking:
{ "tool_name": "trace_column_lineage", "arguments": { "target_columns": ["users.email", "orders.customer_id"], "analysis_depth": 3 } }
Data Freshness Monitoring:
{ "tool_name": "monitor_data_freshness", "arguments": { "freshness_threshold_hours": 24, "include_update_patterns": true } }
Performance Analytics:
{ "tool_name": "analyze_slow_queries_topn", "arguments": { "days": 7, "top_n": 20, "include_patterns": true } }
A: Version 0.5.0 introduces a comprehensive logging system with automatic management and level-based organization:
logs/ ├── doris_mcp_server_debug.log # DEBUG level messages ├── doris_mcp_server_info.log # INFO level messages ├── doris_mcp_server_warning.log # WARNING level messages ├── doris_mcp_server_error.log # ERROR level messages ├── doris_mcp_server_critical.log # CRITICAL level messages ├── doris_mcp_server_all.log # Combined log (all levels) └── doris_mcp_server_audit.log # Audit trail (separate)
# View real-time logs by level tail -f logs/doris_mcp_server_info.log # General operational info tail -f logs/doris_mcp_server_error.log # Error tracking tail -f logs/doris_mcp_server_debug.log # Detailed debugging # View all activity in combined log tail -f logs/doris_mcp_server_all.log # Monitor specific operations tail -f logs/doris_mcp_server_info.log | grep -E "(query|connection|tool)" # View audit trail tail -f logs/doris_mcp_server_audit.log
# Enhanced logging configuration in .env LOG_LEVEL=INFO # Base log level ENABLE_AUDIT=true # Enable audit logging ENABLE_LOG_CLEANUP=true # Enable automatic cleanup LOG_MAX_AGE_DAYS=30 # Keep logs for 30 days LOG_CLEANUP_INTERVAL_HOURS=24 # Check for cleanup daily # Advanced settings LOG_FILE_PATH=logs # Log directory (auto-organized)
# Debug connection issues grep -E "(connection|pool|at_eof)" logs/doris_mcp_server_error.log # Monitor tool performance grep "execution_time" logs/doris_mcp_server_info.log # Check system health tail -20 logs/doris_mcp_server_warning.log # View recent critical issues cat logs/doris_mcp_server_critical.log
LOG_MAX_AGE_DAYSA: The revolutionary token-bound database configuration allows each token to carry its own database connection parameters for secure multi-tenant access:
Enable Token Authentication:
# In your .env file ENABLE_TOKEN_AUTH=true TOKEN_HOT_RELOAD=true TOKEN_FILE_PATH=tokens.json
Create the bearer token once and store only its digest in tokens.json:
{ "version": "2.0", "tokens": [ { "token_id": "tenant-alpha", "token_digest": "sha256:0000000000000000000000000000000000000000000000000000000000000000", "created_at": "2026-07-29T00:00:00Z", "expires_at": null, "last_used": null, "description": "Tenant Alpha database access", "is_active": true, "database_config": { "host": "tenant-alpha-db.company.com", "port": 9030, "user": "alpha_user", "password": "secure_password", "database": "alpha_analytics", "charset": "UTF8" } } ] }
Generate the real bearer token and digest with the command in Token-Bound Database Configuration. The all-zero value above is deliberately unusable. Give the bearer value to the client once; do not place it in the file.
Configuration Priority (New in v0.6.0):
Hot Reload Benefits:
Multi-Tenant Usage:
# Different tokens access different databases automatically curl -H "Authorization: Bearer $TOKEN_TENANT_ALPHA" http://localhost:3000/mcp curl -H "Authorization: Bearer $TOKEN_TENANT_BETA" http://localhost:3000/mcp
A: External OAuth/OIDC delegates identity to an external provider such as Google, Azure AD, GitHub, GitLab, or Keycloak. Doris-backed OAuth is issued by this MCP server after the user signs in with Doris credentials. The server validates the Doris username/password, creates a per-user Doris connection pool, issues doa_ access and refresh tokens, and lets Doris RBAC decide what data and metadata that user can access.
These modes are mutually exclusive on one MCP URL. Do not enable ENABLE_DORIS_OAUTH_AUTH=true together with ENABLE_OAUTH_AUTH=true, OAUTH_ENABLED=true, or AUTH_TYPE=oauth; startup fails fast if both OAuth modes are configured.
Doris-backed OAuth currently exposes MCP resources with disabled resources metadata cache. It exposes reviewed metadata tools when DORIS_OAUTH_DB_TOOLS_ENABLED=true, exec_query when DORIS_OAUTH_QUERY_TOOLS_ENABLED=true, and SQL explain when DORIS_OAUTH_EXPLAIN_TOOLS_ENABLED=true. Normal clients do not need to pass a long scope list; omitted OAuth scope grants the configured Doris OAuth capability envelope. Doris RBAC remains the final data authorization backend for these MySQL-channel operations.
A: Not in the current implementation. Doris-backed OAuth uses a memory-only OAuth store and process-local per-user Doris pools. Access tokens, refresh tokens, authorization codes, DCR clients, and pools are not shared between workers, processes, or nodes.
Use WORKERS=1 with Doris-backed OAuth. WORKERS=0 expands to CPU count and fails because it would create multiple effective workers. Stateless horizontal scaling, shared token storage, shared encrypted Doris credentials, sticky-session recovery, and pool reconstruction are future designs, not current capabilities.
A: The hot reload system is designed for enterprise production environments with comprehensive safety measures:
How It Works:
tokens.json.lock protects every managed read-modify-write operation across local worker processesrevoked_tokens stores only token digests and also disables matching TOKEN_<ID> environment credentials in every workerSafety Features:
0600Best Practices:
# Monitor hot reload activity tail -f logs/doris_mcp_server_info.log | grep "hot reload" # Test configuration before applying cp tokens.json tokens.json.backup # Make changes to tokens.json # System will automatically validate and apply or rollback
A: Token management uses a secure, file-based approach with optional administrative endpoints that have comprehensive security controls.
Primary Token Management Method (Recommended):
# 1. Keep the management endpoint disabled unless local administration is needed. # 2. When enabled, create a token through the protected localhost endpoint. curl -X POST \ -H "Authorization: Bearer $TOKEN_MANAGEMENT_ADMIN_TOKEN" \ -H "Content-Type: application/json" \ --data '{"token_id":"service-a","expires_hours":720}' \ http://127.0.0.1:3000/token/create # 3. Capture the returned bearer token once and place it in the client secret store. # 4. The server writes only token_digest to tokens.json. # 5. Monitor hot reload in logs. tail -f logs/doris_mcp_server_info.log | grep "hot reload"
For deployments without HTTP token management, use a high-entropy TOKEN_<ID> environment secret or generate a bearer/digest pair offline as shown above. Manual tokens.json entries must use token_digest; plaintext token entries exist only for one-way migration from version 1.
The file backend coordinates multiple worker processes on one host. Every worker must use the same TOKEN_FILE_PATH, and the underlying filesystem must provide reliable file locking and atomic rename semantics. Multiple hosts or containers without a shared locking filesystem require an external transactional state backend; copying separate tokens.json files does not provide cluster-wide revocation.
Administrative Endpoints (Secure, Local Access Only):
🛡️ SECURITY: These endpoints are protected by comprehensive security controls and are disabled by default.
# Security Requirements (ALL must be met): # ✓ HTTP token management explicitly enabled in configuration # ✓ Access only from localhost (127.0.0.1/::1) - IP restrictions enforced # ✓ Valid admin authentication token required # ✓ Admin authentication enabled in configuration # Enable HTTP token management (disabled by default) export TOKEN_MANAGEMENT_ADMIN_TOKEN="$(python -c 'import secrets; print(secrets.token_urlsafe(32))')" export ENABLE_HTTP_TOKEN_MANAGEMENT=true export REQUIRE_ADMIN_AUTH=true export TOKEN_MANAGEMENT_ALLOWED_IPS=127.0.0.1,::1 # Access with proper authentication curl -H "Authorization: Bearer $TOKEN_MANAGEMENT_ADMIN_TOKEN" http://127.0.0.1:3000/token/stats # Demo page (local access only, with authentication) # Access: http://127.0.0.1:3000/token/demo
Recommended Token Management Workflow:
Development/Testing:
// tokens.json { "version": "2.0", "tokens": [ { "token_id": "dev-token", "token_digest": "sha256:0000000000000000000000000000000000000000000000000000000000000000", "created_at": "2026-07-29T00:00:00Z", "expires_at": "2026-07-30T00:00:00Z", "last_used": null, "description": "Development environment access", "is_active": true } ] }
Replace the all-zero digest with one derived from a securely generated bearer token, and keep that bearer token outside the server file.
Production Deployment:
# Use secure token generation openssl rand -hex 32 # Generate secure token # Store in secure configuration management # Never commit tokens to version control # Use environment variables for sensitive tokens
Security Features:
0600 permissionsSecurity Best Practices:
tokens.json files owner-readable only; managed writes enforce 0600For other issues, please check GitHub Issues or submit a new issue.