duckle-mcp is a Model Context Protocol
server. It lets any MCP client - Claude Desktop, Claude Code, or any other LLM
agent that speaks MCP - drive Duckle directly: browse the component catalog,
generate a pipeline straight into a folder you choose, validate it, run it, read
existing pipelines and their run logs, build a standalone artifact, and manage
saved connections.
It speaks newline-delimited JSON-RPC over stdio and reuses the DuckDB engine in-process, so there is no GUI and no Node runtime involved at run time.
cargo build -p duckle-mcp --release
# binary at target/release/duckle-mcp (.exe on Windows)The server has no network dependencies and embeds the full component catalog at build time. To run and build pipelines it needs two binaries it locates at run time:
- DuckDB CLI - for
run_pipelineandbuild_pipeline. PointDUCKLE_DUCKDB_BINat it (or passduckdbper call, or haveduckdbon PATH). - duckle-runner - for
build_pipelineonly. PointDUCKLE_RUNNER_BINat aduckle-runnerbinary (or have it on PATH / next toduckle-mcp). Use a release-profile runner so built artifacts stay small.
Easiest: in Duckle, click the MCP button (robot icon) in the designer top bar. The popup bundles the server, fills in the real paths, and offers a one-click Connect to Claude Code plus copy buttons for the command and the mcpServers config. The steps below are the manual equivalent (or for a server built from source).
claude mcp add duckle -- /path/to/duckle-mcpSet the engine paths in the environment Claude Code launches the server with, or inline:
claude mcp add duckle --env DUCKLE_DUCKDB_BIN=/path/to/duckdb --env DUCKLE_RUNNER_BIN=/path/to/duckle-runner -- /path/to/duckle-mcpAdd to the client's mcpServers config:
{
"mcpServers": {
"duckle": {
"command": "/path/to/duckle-mcp",
"env": {
"DUCKLE_DUCKDB_BIN": "/path/to/duckdb",
"DUCKLE_RUNNER_BIN": "/path/to/duckle-runner"
}
}
}
}| Tool | What it does |
|---|---|
list_components |
List components, optionally filtered by kind (source/transform/sink/control/quality/custom) or a query substring. |
get_component_schema |
Full property schema (form fields + input/output ports) for one componentId. |
create_pipeline |
Validate a pipeline and write <name>.json into a chosen directory. Fails without writing if it does not compile (unless validate:false). |
validate_pipeline |
Compile a pipeline to SQL without running it. Returns per-stage SQL or a structured error. |
run_pipeline |
Run a pipeline headlessly. Returns per-node status, row counts, errors and a small result preview. |
list_pipelines |
List pipeline .json files in a directory with node/edge counts. |
read_pipeline |
Read and return a pipeline file. |
read_run_logs |
Read the tail of a pipeline's NDJSON run log (component-level events). |
build_pipeline |
Build a pipeline into ONE self-contained executable for server deployment (see scheduler.md). |
list_connections |
List the workspace's saved connections (secret fields masked). |
create_connection |
Create a saved connection JSON so pipelines can reference its fields. |
Also exposed: resources duckle://catalog (the full catalog) and
duckle://pipeline-format, and a generate_pipeline prompt.
A pipeline is JSON with name, nodes, and edges:
{
"name": "orders to csv",
"nodes": [
{ "id": "src", "type": "source", "position": {"x": 0, "y": 0},
"data": { "label": "orders", "componentId": "src.csv",
"properties": { "path": "orders.csv", "hasHeader": true } } },
{ "id": "snk", "type": "sink", "position": {"x": 300, "y": 0},
"data": { "label": "out", "componentId": "snk.csv",
"properties": { "path": "out.csv", "mode": "overwrite" } } }
],
"edges": [
{ "id": "e1", "source": "src", "target": "snk",
"sourceHandle": "main", "targetHandle": "main",
"data": { "connectionType": "main" } }
]
}Use list_components + get_component_schema to discover component ids and
their property keys. Transforms add ports beyond main (e.g. reject,
lookup_1, case_1, main_1); the edge data.connectionType mirrors the
handle.
Never inline real secrets. Put a ${ENV:KEY} placeholder in a property and
provide the value through the environment at run time. The headless runner (and
any built artifact) resolves ${ENV:KEY} from the process environment first,
then a secrets.env file, then an encrypted secrets.enc. See
scheduler.md for the build-time secret modes.
The component catalog the server embeds is exported from the frontend manifest
(the single source of truth) into crates/duckle-mcp/catalog.json:
npm --prefix frontend run export-catalogRe-run this whenever components or their property schemas change, then rebuild the crate.