Skip to content

Expose Lumen as an MCP server (server half of #1212) #2005

Description

@ghostiee-11

#1212 shipped the client half. Lumen consumes MCP servers through MCPTool (lumen/ai/tools/mcp.py:90, PR #1686). The server half was set aside on purpose:

I have a prototype at ghostiee-11/lumen-mcp driving Lumen from Claude Code and Cursor, and two of the upstream changes it needed are already merged (#1909, #1910). #1212 itself looks closeable now that #1686 is in.

Why a frontier model should call this

If the answer is that Lumen makes charts, this is a wrapper and should not exist. A frontier model writes matplotlib perfectly well.

The real answer is that Lumen checks its work against real engines, and a model working alone cannot. Everything below already exists in Lumen. The MCP surface only has to expose it.

What the caller wants Doing it alone Through Lumen Gain
SQL that runs One shot, and the traceback lands on the user Executed against the real backend, the exception goes back to the model (ai/agents/sql.py:440) More precise
A read on a large table head(), then a guess at cardinality and range SQLCount, SQLDistinct and SQLMinMax over the whole table (sources/base.py:1134) Cheaper, and correct
A chart that renders A malformed Vega-Lite spec renders blank and reports nothing vl_convert.vegalite_to_vega returns a compiler error to act on (ai/editors.py:435) More precise
Snowflake, BigQuery, Zarr No engine to reach them, and too large to load anyway Dialect aware sources, including xarray and Zarr (sources/xarray_sql.py:21) Possible at all
A follow-up question Re-reads the file on every call The derived query becomes a source, so pushdown continues (ai/agents/sql.py:483) Faster
Something to hand a human A PNG in the chat log A spec that round trips and re-runs (base.py:626), with parents recorded (ai/agents/sql.py:512) Reusable

Given one small DuckDB file and a generic SQL tool, a frontier model covers the first row by itself. The rest is the durable gap: dialect breadth, profiling that never moves a row, a chart loop that fails loudly instead of silently, and a spec instead of a screenshot.

What I am asking for

Two things. The first is much smaller and stands on its own.

A headless path through Coordinator. Today Coordinator.__init__ builds a Panel ChatInterface when interface is None (lumen/ai/coordinator/base.py:354), and respond streams into it (:542, :562), so the planner cannot run without a UI attached. vega_lite.py:478 raises UserCancelledError waiting on a human.

That seam is worth opening whether or not the MCP server lands. It is what a REST API around Lumen would sit on. It makes the planner testable in CI. It makes a stalled deployment legible instead of silent, which may be the class of problem behind #1034. I would like a read on this part first, since everything else depends on it and nothing else does.

Tool surface

Fewer tools than my prototype has today. list_tables and describe_table return subsets of what connecting a source already gives you, and get_chart is subsumed by view. Shipping those is what makes a wrapper look like a wrapper.

What is worth exposing is the table above: connect a source, profile it by pushdown, query it with validation, chart it with compilation, serve it live, and export a spec or a report.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions