npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@millicast/imply-druid-mcp

v1.0.0

Published

Read-only Model Context Protocol (MCP) server for Apache Druid / Imply over stdio. Runnable via npx or Docker.

Readme

imply-druid-mcp

A read-only Model Context Protocol (MCP) server for Apache Druid / Imply.

It speaks MCP over stdio, so it is launched on demand by an MCP client (such as Devin) and exits when the session ends. There is no long-running HTTP MCP server to host or keep alive — you just point the client at a command (either npx or docker run).

Features

  • Read-only by design. Only SELECT, WITH, and EXPLAIN statements are allowed. Writes (INSERT/REPLACE), deletes, and schema changes are rejected before they ever reach Druid, and string literals are ignored during keyword scanning to avoid false positives on data values.
  • Single statement per call. The Druid SQL endpoint runs one statement; multiple statements are rejected rather than silently truncated.
  • Runs via npx or Docker. No global install needed.
  • Stdio transport. No ports to expose, no server to babysit.
  • Schema discovery tools for schemas, datasources, tables, and columns via INFORMATION_SCHEMA.
  • Flexible auth: basic auth (username/password) or bearer token (Imply Polaris API key / auth proxy).

Tools

| Tool | Description | Underlying query | | --- | --- | --- | | druid_sql_query | Run any read-only Druid SQL query | SELECT / WITH / EXPLAIN ... | | explain_query | Return the plan for a query without running it | EXPLAIN PLAN FOR ... | | list_schemas | List SQL schemas | INFORMATION_SCHEMA.SCHEMATA | | list_datasources | List queryable datasources (the druid schema) | INFORMATION_SCHEMA.TABLES | | list_tables | List tables across schemas (optionally one schema) | INFORMATION_SCHEMA.TABLES | | describe_table | List a table's columns and types | INFORMATION_SCHEMA.COLUMNS | | status | Check connectivity and report the Druid version | GET /status |

Configuration

All configuration is via environment variables:

| Variable | Required | Default | Description | | --- | --- | --- | --- | | DRUID_URL | no | http://localhost:8888 | Base URL of the Druid Router/Broker that serves the SQL API | | DRUID_SQL_PATH | no | /druid/v2/sql/ | Path to the SQL endpoint (override if behind a proxy/prefix) | | DRUID_USERNAME | no | — | Basic auth username | | DRUID_PASSWORD | no | — | Basic auth password | | DRUID_TOKEN | no | — | Bearer token; overrides basic auth | | DRUID_TIMEOUT_MS | no | 30000 | Per-request timeout in milliseconds | | DRUID_CONTEXT | no | — | Inline datasource reference appended to the MCP instructions (see below) | | DRUID_CONTEXT_FILE | no | — | Path to a file appended to the instructions (handy for Docker mounts) |

See .env.example.

Which port? The SQL API is served by the Druid Router (default 8888) and Broker (default 8082). TLS deployments and Imply often use 8280/9088 or a gateway hostname — set DRUID_URL accordingly.

Giving the AI persistent datasource knowledge (DRUID_CONTEXT)

The server sends an instructions block to the client on every connect. Anything you put in DRUID_CONTEXT (or a file referenced by DRUID_CONTEXT_FILE) is appended to it, so the AI knows your datasources, dimensions, metrics, units, and semantics without rediscovering them each session.

Set it in the MCP connector's env block, e.g.:

"env": {
  "DRUID_URL": "https://druid.example.com:8888",
  "DRUID_CONTEXT": "Datasources:\n- requests: per-request events. dimensions: service, endpoint, region. metrics: count, latency_ms (p95 via APPROX_QUANTILE_DS). __time is event time. High cardinality \u2014 always filter __time and service.\n- billing_daily: daily rollup. dimensions: account_id, plan. metrics: revenue_usd."
}

For large references, mount a file into the container and point at it:

docker run --rm -i \
  -e DRUID_URL -e DRUID_CONTEXT_FILE=/config/druid-context.md \
  -v /path/to/druid-context.md:/config/druid-context.md:ro \
  your-registry/imply-druid-mcp:latest

Usage with Devin (and other MCP clients)

The server is configured as an MCP server using a command + args + env block. Pick one of the two methods below.

Option A — npx (no Docker)

{
  "mcpServers": {
    "druid": {
      "command": "npx",
      "args": ["-y", "@millicast/imply-druid-mcp"],
      "env": {
        "DRUID_URL": "https://druid.example.com:8888",
        "DRUID_USERNAME": "readonly_user",
        "DRUID_PASSWORD": "••••••"
      }
    }
  }
}

npx requires the package to be published to a registry (see Publishing). For a private/local checkout, you can instead point at the built file: "command": "node", "args": ["/abs/path/dist/index.js"].

Option B — Docker image (recommended for hosting)

Build and host the image once, then have the client run it on demand:

{
  "mcpServers": {
    "druid": {
      "command": "docker",
      "args": [
        "run", "--rm", "-i",
        "-e", "DRUID_URL",
        "-e", "DRUID_USERNAME",
        "-e", "DRUID_PASSWORD",
        "your-registry/imply-druid-mcp:latest"
      ],
      "env": {
        "DRUID_URL": "https://druid.example.com:8888",
        "DRUID_USERNAME": "readonly_user",
        "DRUID_PASSWORD": "••••••"
      }
    }
  }
}

The -i flag is required — MCP communicates over stdin/stdout. --rm cleans up the container after each session. Each variable is passed through with -e NAME (value taken from the client's env block).

Building and running the Docker image

This project uses pnpm (pinned via packageManager in package.json and installed through corepack inside the build).

# Build
docker build -t imply-druid-mcp .

# Connectivity self-test (no MCP session; verifies config + reaches Druid)
docker run --rm -e DRUID_URL=https://druid.example.com:8888 \
  imply-druid-mcp --self-test

# Run as an MCP server over stdio (normally launched by the client, not by hand)
docker run --rm -i -e DRUID_URL=https://druid.example.com:8888 imply-druid-mcp

Push to a registry to host it

docker tag imply-druid-mcp your-registry/imply-druid-mcp:1.0.0
docker push your-registry/imply-druid-mcp:1.0.0

Once pushed, any host with Docker can run it on demand via the Option B config above — nothing needs to stay running between sessions.

Local development & testing

  1. Install + build:

    corepack enable          # makes pnpm available
    pnpm install
    pnpm run build           # compile TypeScript to dist/
  2. Add your connection details to the gitignored .env file (URL, username/password, etc.). See .env.example for the full list of variables.

  3. Verify connectivity (hits Druid /status using .env):

    pnpm run selftest
  4. Full end-to-end test — launches the server, runs an MCP handshake, then calls status and list_datasources against your Druid cluster:

    pnpm run test:local
  5. Run the server directly (stdio, config from .env):

    pnpm run start:local

These scripts load .env via Node's built-in --env-file flag (Node 20.6+), so no extra dependency is needed. pnpm run dev runs tsc in watch mode.

Test the Docker image locally

Once Docker is running, the same .env file works with --env-file:

docker build -t imply-druid-mcp .
docker run --rm --env-file .env imply-druid-mcp --self-test   # connectivity check
docker run --rm -i --env-file .env imply-druid-mcp            # run as MCP server

Quick manual MCP check (no Druid needed)

printf '%s\n' \
  '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0.0"}}}' \
  '{"jsonrpc":"2.0","method":"notifications/initialized"}' \
  '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' \
  | node dist/index.js

Publishing to npm

This package is published under the @millicast scope. To enable the npx @millicast/imply-druid-mcp workflow:

npm whoami    # ensure you're logged in with access to the @millicast org
pnpm run build
npm publish   # publishConfig.access=public + prepublishOnly (rebuilds dist/) are already set

Read-only guarantees

  1. Statement allow-list: the statement must start with SELECT, WITH, or EXPLAIN.
  2. Single statement: stacked statements are rejected; the SQL endpoint runs one statement per request.
  3. Keyword deny-list: INSERT, REPLACE, UPSERT, UPDATE, DELETE, MERGE, DROP, CREATE, ALTER, TRUNCATE, GRANT, REVOKE, KILL, CALL, and INTO are rejected anywhere outside of string literals.

Druid SQL has no transport-level read-only mode (queries are sent via HTTP POST), so for maximum safety also point the server at a Druid user that only holds READ permissions on the datasources you want to expose.

License

MIT