Connect AI assistants to your Malloy semantic models using the Model Context Protocol (MCP). Publisher includes a built-in MCP server that exposes your models to AI agents.
Quick Start
This works for MCP clients that support HTTP endpoints directly.
1. Start Publisher
npx @malloy-publisher/server --server_root /path/to/your/packagesThe AI sees all packages under your server_root directory. The MCP server starts automatically at http://localhost:4040/mcp.
The endpoint does not require authentication, and Publisher binds 0.0.0.0 by default, so anyone who can reach port 4040 on your network can run malloy_executeQuery against the databases your models connect to. The surface is not read-only either: malloy_reloadPackage mutates server state, and for a package that carries an install location a reload re-fetches it, overwriting on-disk edits. The same effects are already reachable through the equivalent REST endpoints, so this is a reason to gate the deployment rather than a reason to avoid the tools. Bind it to loopback with --host 127.0.0.1 for local-only use, and put an authenticating gateway in front before exposing it more widely.
For other deployment options (Docker, git clone), see Publish Your Models.
2. Configure Your MCP Client
{ "mcpServers": { "malloy-publisher": { "url": "http://localhost:4040/mcp" } } }
3. Start Asking Questions
Once connected, try prompts like:
"What Malloy packages are available?"
"Show me the measures in the orders model"
"Query total revenue by month"
Stdio-only Clients (older Claude Desktop, etc.)
Some MCP clients speak only stdio, not HTTP. Bridge them to the HTTP endpoint with mcp-remote, which needs no extra script. In the client's MCP config add:
{ "mcpServers": { "malloy": { "command": "npx", "args": ["-y", "mcp-remote", "http://localhost:4040/mcp", "--allow-http"] } } }
--allow-http is required because the endpoint is plain HTTP on localhost. For Claude Desktop the config lives under Settings > Developer > Edit Config.
MCP Tools
Publisher serves these tools from the single MCP endpoint, alongside the bundled agent skills as prompts.
Discovery
malloy_getContext is the entry point. Every parameter is optional, so an agent can start with nothing and narrow down as it learns the names.
| Tool | Parameters | Description |
|---|---|---|
malloy_getContext |
all optional: environmentName, packageName, query, sourceName, limit |
Progressively discover what is on the server, and the model entities most relevant to a plain-English question. |
Call it with no arguments to list the environments and the packages in each, with an environmentName to list that environment's packages, with environmentName plus packageName to list that package's sources, and with environmentName plus packageName plus a plain-English query to get the sources, views, named queries, and dimension/measure fields most relevant to it, each with its model path and #(doc) description. Add sourceName alongside a query to focus retrieval on a single source.
Each step needs the ones before it: packageName without environmentName is ignored, and you get the environment list back rather than an error.
The environmentName, packageName, and modelPath it returns map directly onto malloy_executeQuery, so an agent can go from a question to a grounded query without guessing names.
Query Execution
| Tool | Parameters | Description |
|---|---|---|
malloy_executeQuery |
required: environmentName, packageName, modelPath, and then either query or queryName plus sourceName. Optional: filterParams, givens |
Run a Malloy query: ad hoc with query, or a named query or view with queryName plus the sourceName it belongs to. Exactly one of query and queryName is required. Returns JSON results. |
Authoring
These two close the edit-and-run loop, so an agent can change a model and query the result without restarting the server.
| Tool | Parameters | Description |
|---|---|---|
malloy_compile |
required: environmentName, packageName, modelPath, source. Optional: includeSql, givens |
Compile-check Malloy source against a model and get structured diagnostics back, without running a query. The source is appended to modelPath, so that model's imports, sources, and queries are in scope. |
malloy_reloadPackage |
required: environmentName, packageName |
Recompile a package from its on-disk model files, so a source or view added after boot becomes resolvable by name. Returns the mode it used, in-place or reinstalled. |
Publisher compiles each configured package at boot and serves that cached model, so a source you add afterwards is not queryable by name until the package is reloaded. malloy_compile does not need a reload: it validates against the model as it is on disk.
Compiling before reloading is a speed-up, not a safety measure. A reload that fails to compile leaves your files alone and keeps serving the previously compiled model, returning the compile errors, so nothing breaks either way. malloy_compile is simply the faster way to see diagnostics.
One caveat worth knowing: a package whose stored metadata carries an install location is re-fetched from that source on reload, which overwrites on-disk edits. Check the returned mode if you had edits you did not save elsewhere.
Documentation Search
| Tool | Parameters | Description |
|---|---|---|
malloy_searchDocs |
required: query. Optional: limit |
Keyword search over a bundled index of the Malloy documentation, returning matching pages with a short excerpt and a link. |
Skills as MCP prompts
Publisher also ships a set of agent skills: short guides that teach an agent how to write Malloy, choose charts, and verify results. Skill-aware hosts (such as Claude Code and Claude Desktop) load the skill files directly. For hosts that only ingest MCP, the server also exposes each skill as an MCP prompt, so the same guidance is available either way: list them with prompts/list and fetch one with prompts/get.
Example Prompts
Once connected, try these prompts:
Exploration:
"Use Malloy to run an exploratory analysis on the FAA dataset"
"Help me understand the ecommerce data and create charts"
Specific queries:
"How many flights were there by carrier last year?"
"What's the average order value by region?"
Analysis:
"Why did revenue drop in March? Investigate by region and product."
Best Practices for AI-Friendly Models
Use Descriptive Names
// Good - AI understands intent measure: customer_lifetime_value is ... measure: monthly_recurring_revenue is ... // Bad - cryptic abbreviations measure: clv is ... measure: mrr is ...
Add Annotations
Use tags to add metadata that AI agents can read:
source: orders is duckdb.table('orders.parquet') extend { # "Revenue after discounts and returns" measure: net_revenue is sum(amount - discount - returns) }
Define Common Views
Pre-built views guide AI toward valid queries:
view: executive_summary is { aggregate: total_revenue, order_count, customer_count } view: regional_breakdown is { group_by: region aggregate: total_revenue, order_count }