Publisher is the open-source analytics engine for Malloy. It lets you define data models once — and use them everywhere.
102
stars
1,595
commits
TypeScript
primary language
Sep 15, 2026
updated
A post modern data stack — built for the AI era.
One data model, served over MCP and REST to AI agents, applications, and BI tools.
Created and maintained by Credible, the company behind the AI Analytics Engine.
AI agents: read AGENTS.md first (raw: https://raw.githubusercontent.com/malloydata/publisher/main/AGENTS.md).
It covers starting the server, connecting over MCP, the bundled skills, and the package format. Fetch the raw file, not a summary of this page.
A 60-second walkthrough — model in your IDE with the Malloy skills, serve with Publisher, build a data app, materialize on a schedule, and analyze. Watch the video for playback controls.
Modeling, a query engine, materialization, access control, and an API — the pieces you used to assemble from five projects — ship as one server, built assuming the first builder or consumer is an agent.
Write down what your data means, in Malloy: the sources, the joins, the measures, who may see what. The open-source Malloy skills ship alongside, so an agent can do the writing — build the model, then the dashboards, notebooks, and data apps on top of it.
Publisher serves that model to every surface — over MCP to Claude, Cursor, Codex, or an agent you build; over REST to applications and BI tools. Agents compose queries against the model instead of writing SQL from scratch, so there is no wrong join, no invented column, no fan-out that double-counts but looks plausible — and the same question returns the same numbers tomorrow.
#(authorize) decide who sees what; discovery curation
decides what is even visible.#@ persist annotation materializes an expensive source into a table and
#@ preaggregate rolls it up, rebuilt on demand or on a schedule.npx, Docker, or Compose, in minutes.Node.js 20 or newer (the server refuses to start on anything older and says so). Building from a clone also needs Bun 1.3.13+. The Docker image carries its own runtime and needs neither.
npx @malloy-publisher/server@latest --port 4000
Open http://localhost:4000. Three example packages are bundled — storefront
(a complete ecommerce model), governed-analytics (access control), and
html-data-app (a no-build dashboard) — all DuckDB-backed, no credentials
required. The first run fetches them from GitHub.
mkdir my-data && cd my-data
npm create @malloy-publisher/malloy-package@latest sales
npm start
This writes a package to ./sales — plus, in the current directory, a small workspace: start and
reset scripts, an MCP config, agent instructions, and the Malloy skills as files your agent can read.
npm start serves the package in watch mode, so edits take effect as you save. Keep the @latest:
without it npm may reuse a cached, older version. The finer points of npm create — caching, workspace
layout, the bare npx form — are in docs/scaffolding.md.
Agents: AGENTS.md sections 1 and 2 have the same steps plus the pitfalls around @latest,
the -- before --data, and reconnecting the MCP client after the server starts.
npm create @malloy-publisher/malloy-package@latest sales -- --data ./orders.csv
CSV, Parquet, JSON, newline-delimited JSON, or Excel .xlsx — DuckDB reads all of them in place. The
-- is required, and the path is relative to where you run the command. A seeded package starts
small: a row count and an overview, which is the moment to point an agent at it.
A package is just Malloy, so it is not limited to local files. Add a
connection — BigQuery, Snowflake, Postgres, Databricks, MotherDuck, and more —
and point the model at it; the same workspace serves a warehouse. Have the warehouse but no model yet?
Ask the agent what is in it: search_database_schema ranks a connection's tables against a
plain-English description and hands back the source: line for each. Ranking needs no API key; the
optional embedding-backed mode is in
docs/configuration.md.
Keep the server from Quick start running — or npm start from
Create a package. On startup it wrote a .mcp.json into the directory you ran it
in, pointing at the MCP port it bound. Open a second terminal, in that same directory, and start
the agent:
claude
Say yes when the agent asks to trust the folder, use the server it found, and approve the first tool call — the trust prompt is asked once per directory, and only interactively, so a headless run can't clear it. Then ask, in plain English:
"Use Malloy to explore the storefront sales data and chart revenue by category."
The agent discovers what exists (get_context), grounds itself in real source, view, and field
names, runs the query (execute_query), and answers from your model — no schema spelunking, no
hallucinated columns.
If the agent reports no Malloy tools, register the server for yourself instead of relying on that file:
claude mcp add --transport http malloy http://127.0.0.1:4040/mcp -s user
The server skips writing the file in some directories (a git working tree, your home directory, one
that already has a .mcp.json) and says so in its startup log. When it is written, why it can go
stale, and how to turn it off:
docs/configuration.md.
Cursor, VS Code, Codex, and Claude Desktop take the same endpoint through their own config; see docs/ai-agents.md. An agent working unattended that started the server itself uses the same loop over REST:
curl -s -X POST \
http://localhost:4000/api/v0/environments/examples/packages/storefront/models/storefront.malloy/query \
-H 'content-type: application/json' \
-d '{"query":"run: order_items -> by_category","compactJson":true}' | jq -r .result
The running server serves its full OpenAPI spec at http://localhost:4000/api-doc.yaml.
Security. The server — MCP and REST alike — is stateless and unauthenticated, and it can read any data your models connect to. Bind it to loopback (
--host 127.0.0.1) for local use, and put an authenticating gateway in front before exposing it more widely.
search_database_schema ranks a connection's tables against a
plain-English description and returns the source: line for each.compile_model checks an edit without running it;
reload_package recompiles a package from disk. Watch mode does the same for a human editing
in an IDE.get_context, runs
execute_query, and answers from the model, never from raw tables. Analysis skills teach it the
pitfalls and how to write up a finding — docs/ai-agents.md..malloynb notebooks live inside a package, mix prose and queries, and run on
the same governed endpoints — docs/choosing-a-surface.md.dashboards/*.malloy file is the dashboard: filterable,
clickable, grid-laid-out, no code and no build step — docs/dashboards.md.packages/python-client, and the running server publishes its OpenAPI spec.#(authorize) gate
which rows a caller gets and whether they may query a source at all.#@ persist annotation turns an expensive source into a table, rebuilt on
demand, from the malloy-pub CLI, or on a cron with the opt-in scheduler —
docs/materialization.md.#@ preaggregate rolls a measure up to a coarse grain so covered queries read a
small table instead of the fact table — docs/preaggregation.md.npx, Docker, or Docker Compose, in minutes — docs/deployment.md.The fastest way to see all of the above is the examples/ directory. Three are packages
Publisher serves out of the box, and a fourth shows the SDK:
#(authorize) source gates.public/ directory, driven by Publisher.query().@malloy-publisher/sdk, embedding live results in your own UI. Not a served
package.The docs/ folder is the reference hub; start at its index. Beyond the
guides linked above: architecture for how the pieces fit together,
api-overview for the REST and MCP surface, packages for
the package format (publisher.json, models, data), and the
Docker runtime deep-dive for image layout, environment, and
tuning. The complete user guide also lives at
docs.malloydata.dev.
Publisher is created and maintained by Credible, the company behind the AI Analytics Engine. The two fit together like this:
Run Publisher yourself, or let Credible run it: the model is the same Malloy either way, and moving between them is a publish, not a rewrite. Where the open-source engine ends and the hosted one begins: credibledata.com/malloy · Inside the AI Analytics Engine.
Build and hack on Publisher from a clone with docs/development.md; contribution process and sign-off are in CONTRIBUTING.md.
Hacker News (1)
TypeScript
93.4%
Python
3.4%
C
1.2%
JavaScript
1.1%
Publisher is the open-source analytics engine for Malloy. It lets you define data models once — and use them everywhere.
102
stars
1,595
commits
TypeScript
primary language
Sep 15, 2026
updated
A post modern data stack — built for the AI era.
One data model, served over MCP and REST to AI agents, applications, and BI tools.
Created and maintained by Credible, the company behind the AI Analytics Engine.
AI agents: read AGENTS.md first (raw: https://raw.githubusercontent.com/malloydata/publisher/main/AGENTS.md).
It covers starting the server, connecting over MCP, the bundled skills, and the package format. Fetch the raw file, not a summary of this page.
A 60-second walkthrough — model in your IDE with the Malloy skills, serve with Publisher, build a data app, materialize on a schedule, and analyze. Watch the video for playback controls.
Modeling, a query engine, materialization, access control, and an API — the pieces you used to assemble from five projects — ship as one server, built assuming the first builder or consumer is an agent.
Write down what your data means, in Malloy: the sources, the joins, the measures, who may see what. The open-source Malloy skills ship alongside, so an agent can do the writing — build the model, then the dashboards, notebooks, and data apps on top of it.
Publisher serves that model to every surface — over MCP to Claude, Cursor, Codex, or an agent you build; over REST to applications and BI tools. Agents compose queries against the model instead of writing SQL from scratch, so there is no wrong join, no invented column, no fan-out that double-counts but looks plausible — and the same question returns the same numbers tomorrow.
#(authorize) decide who sees what; discovery curation
decides what is even visible.#@ persist annotation materializes an expensive source into a table and
#@ preaggregate rolls it up, rebuilt on demand or on a schedule.npx, Docker, or Compose, in minutes.Node.js 20 or newer (the server refuses to start on anything older and says so). Building from a clone also needs Bun 1.3.13+. The Docker image carries its own runtime and needs neither.
npx @malloy-publisher/server@latest --port 4000
Open http://localhost:4000. Three example packages are bundled — storefront
(a complete ecommerce model), governed-analytics (access control), and
html-data-app (a no-build dashboard) — all DuckDB-backed, no credentials
required. The first run fetches them from GitHub.
mkdir my-data && cd my-data
npm create @malloy-publisher/malloy-package@latest sales
npm start
This writes a package to ./sales — plus, in the current directory, a small workspace: start and
reset scripts, an MCP config, agent instructions, and the Malloy skills as files your agent can read.
npm start serves the package in watch mode, so edits take effect as you save. Keep the @latest:
without it npm may reuse a cached, older version. The finer points of npm create — caching, workspace
layout, the bare npx form — are in docs/scaffolding.md.
Agents: AGENTS.md sections 1 and 2 have the same steps plus the pitfalls around @latest,
the -- before --data, and reconnecting the MCP client after the server starts.
npm create @malloy-publisher/malloy-package@latest sales -- --data ./orders.csv
CSV, Parquet, JSON, newline-delimited JSON, or Excel .xlsx — DuckDB reads all of them in place. The
-- is required, and the path is relative to where you run the command. A seeded package starts
small: a row count and an overview, which is the moment to point an agent at it.
A package is just Malloy, so it is not limited to local files. Add a
connection — BigQuery, Snowflake, Postgres, Databricks, MotherDuck, and more —
and point the model at it; the same workspace serves a warehouse. Have the warehouse but no model yet?
Ask the agent what is in it: search_database_schema ranks a connection's tables against a
plain-English description and hands back the source: line for each. Ranking needs no API key; the
optional embedding-backed mode is in
docs/configuration.md.
Keep the server from Quick start running — or npm start from
Create a package. On startup it wrote a .mcp.json into the directory you ran it
in, pointing at the MCP port it bound. Open a second terminal, in that same directory, and start
the agent:
claude
Say yes when the agent asks to trust the folder, use the server it found, and approve the first tool call — the trust prompt is asked once per directory, and only interactively, so a headless run can't clear it. Then ask, in plain English:
"Use Malloy to explore the storefront sales data and chart revenue by category."
The agent discovers what exists (get_context), grounds itself in real source, view, and field
names, runs the query (execute_query), and answers from your model — no schema spelunking, no
hallucinated columns.
If the agent reports no Malloy tools, register the server for yourself instead of relying on that file:
claude mcp add --transport http malloy http://127.0.0.1:4040/mcp -s user
The server skips writing the file in some directories (a git working tree, your home directory, one
that already has a .mcp.json) and says so in its startup log. When it is written, why it can go
stale, and how to turn it off:
docs/configuration.md.
Cursor, VS Code, Codex, and Claude Desktop take the same endpoint through their own config; see docs/ai-agents.md. An agent working unattended that started the server itself uses the same loop over REST:
curl -s -X POST \
http://localhost:4000/api/v0/environments/examples/packages/storefront/models/storefront.malloy/query \
-H 'content-type: application/json' \
-d '{"query":"run: order_items -> by_category","compactJson":true}' | jq -r .result
The running server serves its full OpenAPI spec at http://localhost:4000/api-doc.yaml.
Security. The server — MCP and REST alike — is stateless and unauthenticated, and it can read any data your models connect to. Bind it to loopback (
--host 127.0.0.1) for local use, and put an authenticating gateway in front before exposing it more widely.
search_database_schema ranks a connection's tables against a
plain-English description and returns the source: line for each.compile_model checks an edit without running it;
reload_package recompiles a package from disk. Watch mode does the same for a human editing
in an IDE.get_context, runs
execute_query, and answers from the model, never from raw tables. Analysis skills teach it the
pitfalls and how to write up a finding — docs/ai-agents.md..malloynb notebooks live inside a package, mix prose and queries, and run on
the same governed endpoints — docs/choosing-a-surface.md.dashboards/*.malloy file is the dashboard: filterable,
clickable, grid-laid-out, no code and no build step — docs/dashboards.md.packages/python-client, and the running server publishes its OpenAPI spec.#(authorize) gate
which rows a caller gets and whether they may query a source at all.#@ persist annotation turns an expensive source into a table, rebuilt on
demand, from the malloy-pub CLI, or on a cron with the opt-in scheduler —
docs/materialization.md.#@ preaggregate rolls a measure up to a coarse grain so covered queries read a
small table instead of the fact table — docs/preaggregation.md.npx, Docker, or Docker Compose, in minutes — docs/deployment.md.The fastest way to see all of the above is the examples/ directory. Three are packages
Publisher serves out of the box, and a fourth shows the SDK:
#(authorize) source gates.public/ directory, driven by Publisher.query().@malloy-publisher/sdk, embedding live results in your own UI. Not a served
package.The docs/ folder is the reference hub; start at its index. Beyond the
guides linked above: architecture for how the pieces fit together,
api-overview for the REST and MCP surface, packages for
the package format (publisher.json, models, data), and the
Docker runtime deep-dive for image layout, environment, and
tuning. The complete user guide also lives at
docs.malloydata.dev.
Publisher is created and maintained by Credible, the company behind the AI Analytics Engine. The two fit together like this:
Run Publisher yourself, or let Credible run it: the model is the same Malloy either way, and moving between them is a publish, not a rewrite. Where the open-source engine ends and the hosted one begins: credibledata.com/malloy · Inside the AI Analytics Engine.
Build and hack on Publisher from a clone with docs/development.md; contribution process and sign-off are in CONTRIBUTING.md.
Hacker News (1)
TypeScript
93.4%
Python
3.4%
C
1.2%
JavaScript
1.1%