StarRocks 数据库 MCP 服务

面向 AI 助手场景,提供通过 MCP 协议安全连接和操作 StarRocks 数据库的服务,支持 SQL 查询、表概览、性能分析、Plotly 图表生成,并通过 OAuth 2.1/JWT 实现身份认证。

  • by radiant-network
  • Python
  • 混合部署
  • 更新于
在 GitHub 查看 radiant-network/radiant-mcp

使用说明

StarRocks MCP Server Docker Image

A containerized Model Context Protocol (MCP) server for StarRocks, enabling AI assistants to interact with StarRocks databases.

Overview

This Docker image packages the mcp-server-starrocks Python package with a custom wrapper that adds:

  • Health check endpoints (/health, /ready) for container orchestration.
  • Mandatory OAuth 2.1 authentication via Keycloak (OIDC), in front of the MCP endpoint.
  • Per-request JWT pass-through — each MCP tool call runs its StarRocks queries under the authenticated user's identity. This component holds no StarRocks credentials.

It runs in streamable-http mode, exposing an HTTP endpoint for MCP clients. KEYCLOAK_REALM_URL is required — the server refuses to boot without it.

Quick Start

docker run -d \
  -p 8000:8000 \
  -e STARROCKS_HOST=your-starrocks-host \
  -e STARROCKS_PORT=9030 \
  -e STARROCKS_DB=your-database \
  -e KEYCLOAK_REALM_URL=https://kc.example.com/realms/radiant \
  -e MCP_BASE_URL=https://mcp.example.com \
  ghcr.io/radiant-network/radiant-mcp:latest

Endpoints

Endpoint Method Description
/mcp POST MCP protocol endpoint (JSON-RPC over HTTP)
/health GET Liveness check — the process is up (does not touch StarRocks)
/ready GET Readiness check - returns server status

Health Check Response

{"status": "healthy"}

Returns HTTP 200 while the process is running. It does not probe StarRocks — this component holds no StarRocks credentials.

Readiness Check Response

{"status": "ready"}

Environment Variables

Variable Required Default Description
STARROCKS_HOST Yes - StarRocks FE (Frontend) hostname or IP (connection target only)
STARROCKS_PORT No 9030 StarRocks FE query port
STARROCKS_DB No - Default database to use
STARROCKS_OVERVIEW_LIMIT No - Limit for overview queries (memory management)
RADIANT_API_URL No - Radiant portal API base URL. When set, registers the search_cases, get_case_context and list_tenants tools (the caller's JWT is forwarded as a Bearer token)

No STARROCKS_USER / STARROCKS_PASSWORD: this component authenticates to StarRocks with each caller's JWT, never a static credential. STARROCKS_HOST and STARROCKS_PORT only tell it where to connect.

Authentication (OAuth 2.1 + Keycloak)

Authentication is mandatory: the server requires KEYCLOAK_REALM_URL and refuses to boot without it. It supports two kinds of clients simultaneously:

  • Self-discovering MCP clients (e.g. Claude Desktop) — complete the full OAuth 2.1 + PKCE flow, with dynamic client registration, discovered automatically from the server's .well-known metadata.
  • Clients that already hold a Keycloak token (e.g. a web portal) — present the existing token directly as a Bearer header.

In both cases the user's Keycloak JWT is forwarded to StarRocks, which validates it against Keycloak's JWKS (the authentication_jwt plugin, v3.5.0+) and executes queries under that user's identity. There is no static-credential fallback: a request without a valid JWT is rejected. The user (the JWT sub) must exist in StarRocks as IDENTIFIED WITH authentication_jwt (principal_field: sub), and StarRocks must have SSL enabled (JWT auth requires it).

The server uses fastmcp's native KeycloakAuthProvider — a pure JWT resource server. It holds no client id/secret and mints no tokens; it only validates incoming Bearer JWTs against the realm's JWKS. Requires Keycloak ≥ 26.6.0 for the DCR path.

OAuth Environment Variables

Variable Required Default Description
KEYCLOAK_REALM_URL Yes - Bare realm URL, e.g. https://kc.example.com/realms/radiant
MCP_BASE_URL No http://localhost:8000 Server root URL (not /mcp); the provider derives the resource + metadata URLs from it
OAUTH_REQUIRED_SCOPES No openid Comma-separated scopes required on tokens
KEYCLOAK_AUDIENCE No - Expected JWT aud claim (leave unset unless you add a matching audience mapper)

Building the Image

docker build -t radiant-mcp:latest .

Container Configuration

Port Mapping

The server listens on port 8000 inside the container.

Health Check

For container orchestration (Docker Compose, Kubernetes, ECS):

curl -f http://localhost:8000/health

Example Docker Compose

services:
  radiant-mcp:
    image: ghcr.io/radiant-network/radiant-mcp:latest
    ports:
      - "8000:8000"
    environment:
      STARROCKS_HOST: starrocks-fe
      STARROCKS_PORT: 9030
      STARROCKS_DB: my_database
      KEYCLOAK_REALM_URL: https://kc.example.com/realms/radiant
      MCP_BASE_URL: https://mcp.example.com
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
      interval: 30s
      timeout: 10s
      retries: 3

Deployment Considerations

  • Memory: Recommend at least 1GB due to pandas/pyarrow dependencies
  • Credentials: None to store — the component holds no StarRocks user/password. Access is governed entirely by the caller's Keycloak JWT and the matching StarRocks JWT user.

MCP Client Configuration

Configure your MCP client to connect to the server:

{
  "mcpServers": {
    "starrocks": {
      "url": "http://localhost:8000/mcp"
    }
  }
}

Available MCP Tools

Tool Description
read_query Execute SELECT queries
write_query Execute DDL/DML commands
analyze_query Analyze query performance
table_overview Get table schema and sample data
db_summary Get database summary with table schemas
query_and_plotly_chart Execute query and generate Plotly chart

With RADIANT_API_URL set, two additional tools backed by the Radiant portal API are registered. They forward the caller's Keycloak JWT as a Bearer token, so the API applies its own per-user authorization:

Tool Description
search_cases Find cases by exact-match filters (status, type, panel, project, MRN, patient id, ...) and/or a free-text identifier prefix (resolved through the cases autocomplete endpoint). Returns case summaries and total count
get_case_context Full clinical context of a case: metadata, patients with phenotypes, tasks, sequencing experiments, and the (case_id, seq_id, task_id, data_type) keys needed to query its variant occurrences. tenant is optional and auto-resolved when the user belongs to a single tenant
list_tenants Tenants the authenticated user belongs to (codes to pass as tenant)

Local Development

A docker-compose.yml brings up a full local stack — Keycloak (IdP), StarRocks (with TLS + a JWT-authenticated testuser), and radiant-mcp wired to both:

docker compose up --build

The MCP server is then at http://localhost:8000/mcp and Keycloak at http://localhost:8080 (realm radiant).

Querying StarRocks directly with a JWT

The local stack ships a Keycloak user (testuser / testpass, in realm radiant) and a matching StarRocks user authenticated with authentication_jwt. You can use the same token the MCP server forwards to query StarRocks yourself with the mysql CLI.

1. Get an access token (the compose realm's radiant-mcp-server client allows the password grant) and save it to a file — the mysql client reads the token from a file:

TOKEN=$(curl -sf -X POST http://localhost:8080/realms/radiant/protocol/openid-connect/token \
  -d grant_type=password \
  -d client_id=radiant-mcp-server \
  -d client_secret=test-client-secret \
  -d username=testuser \
  -d password=testpass \
  -d scope=openid \
  | python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")

printf '%s' "$TOKEN" > /tmp/token.jwt
chmod 600 /tmp/token.jwt

To inspect the claims (notably sub):

echo "$TOKEN" | cut -d. -f2 | tr '_-' '/+' | base64 -d 2>/dev/null | python3 -m json.tool

2. Connect with the mysql CLI. The StarRocks username is the token's sub claim (a UUID), not the Keycloak login name. For testuser the sub is pinned in keycloak-import/radiant-realm.json and created by starrocks-init:

mysql -h127.0.0.1 -P9030 \
  -u 11111111-1111-1111-1111-111111111111 \
  --authentication-openid-connect-client-id-token-file=/tmp/token.jwt
SELECT current_user();
SELECT * FROM test_db.users;

Notes:

  • --authentication-openid-connect-client-id-token-file requires a MySQL client that ships the authentication_openid_connect_client plugin (MySQL client ≥ 9.1, e.g. Homebrew mysql-client). The 8.0 client bundled in the StarRocks image does not have it — run this from the host.
  • Passing the token as a password (--enable-cleartext-plugin -p"$TOKEN") does not work; StarRocks answers The path to ID token file is not set.
  • Keycloak access tokens are short-lived (5 minutes by default). On an authentication error, fetch a new token and reconnect.
  • The FE has TLS enabled; the client negotiates SSL automatically (--ssl-mode=REQUIRED also works).

Integration Tests

An end-to-end test exercises the OAuth 2.1 + JWT → StarRocks flow (health, OAuth metadata, token issuance, authenticated read_query, and the unauthenticated → 401 case):

docker compose --profile test run --rm test-runner

Use run --rm test-runner, not up --abort-on-container-exit — the one-shot starrocks-init container exits 0 on success, which would abort the whole stack before the test runs.

License

See the upstream repository for license information.