Back to Devoured - July 09, 2026
DEVOURED
Malloyyo (GitHub Repo)

Malloyyo (GitHub Repo)

Data GitHub
Malloyyo provides a lightweight MCP server and web UI to serve governed Malloy data models to AI agents, ensuring consistent query results.
What: Malloyyo allows developers to publish Malloy data models that AI (like Claude) can query via the Model Context Protocol (MCP), using DuckDB or cloud warehouses to enforce consistent business logic and definitions.
Why it matters: When AI writes raw SQL against a database, it often fails to account for complex join logic or metric definitions; a semantic layer ensures the AI queries pre-vetted business logic instead.
Takeaway: Use the Malloyyo CLI to register your local model with Claude desktop for assisted authoring.
Deep dive
  • Acts as a thin middleware between AI agents and analytical databases.
  • Supports local DuckDB files (via S3/GCS) and major cloud warehouses like BigQuery and Snowflake.
  • Uses the Malloy language to define reusable metrics, dimensions, and joins.
  • Exposes data models via MCP for remote AI clients.
  • Includes a web UI for auditing, sharing, and re-running queries generated by AI.
Decoder
  • MCP (Model Context Protocol): An open standard that allows AI assistants to securely access data and tools from local or remote servers.
  • Semantic Layer: A logical representation of data that defines business metrics and relationships, preventing teams from defining the same calculation differently.
Original article

malloyyo

A natural-language interface to any corpus of data, however complex — accurate and consistent, served to any AI over MCP.

Problem: AI + document context + your analytical database = inconsistent results. Pointed at a raw database, an AI writes SQL from scratch — so the same question tomorrow yields a different query and different numbers, with wrong joins, invented columns, or fan-out double-counts that still look right.

Solution: AI + a Malloy semantic layer + your analytical database = consistent results. Measures, dimensions, and joins are defined once, correctly; the AI composes queries against the model instead of writing SQL, so numbers come back right by construction.

Malloyyo is the thin layer that serves that model:

  • Thin by design — it sits between the AI and your data, nothing more.
  • Develop, then publish — build the model locally and malloyyo publish it (or point Malloyyo at a GitHub repo).
  • Claude already knows Malloy — the same way it knows Python — so authoring is incredibly fast and assisted.
  • Readable, full-featured queries — Malloy is a complete query language (join, nest, aggregate, filter) that stays legible: you can read a query and see at a glance it's doing the right thing.
  • DuckDB built in — query Parquet over plain HTTP (S3, GCS, any web server) with no warehouse required, or attach your own (BigQuery, Snowflake, MotherDuck, Databricks, …).
  • Tight control — the AI can only query what's in the semantic model; nothing outside it is reachable.
  • A web interface, too — every query is logged, so you can browse, edit, favorite, re-run, and share them in the browser (ltool), and hand any one off to "Explore further with Claude." Because Malloy renders a whole dashboard as a single query, one saved query can be a full report.
  • Deploy in minutes — one-click to Vercel, or self-host with Docker.

Try the demo server and "Explore further with Claude" — sign in with any Google account.

Questions, or built something cool? We'd love to hear from you. Come say hi on Slack, and learn more about Malloy at malloydata.dev and in the documentation.

How it works

   MCP client (claude.ai)            Browser (you)
           │ OAuth 2.1                     │ Google sign-in
   ┌───────▼──────────┐          ┌─────────▼─────────┐
   │  MCP server /mcp │          │      Web UI       │
   │ analytical tools │          │  datasets · ltool │
   └───────┬──────────┘          └─────────┬─────────┘
           └──────────────┬────────────────┘
                          │  compile · run             ┌────────────────────┐
                          │                            │  Authoring Models  │
                          │                            │   • Malloyyo CLI   │
                          │                            │   • Claude         │
                          │                            └─────┬───────────┬──┘
                          │                          push    │           │ develop
   ┌──────────────────────▼───────────────────────┐  (deploy)|           │
   │                   Malloyyo                   │◀────────┘           │
   │        load · compile · store · serve        │                      │
   └──────┬─────────────────────────────┬─────────┘                      │
          │                             │                                │
   ┌──────▼──────┐             ┌────────▼────────┐                       │
   │    Neon     │             │  Analytical DB  │◀─────────────────────┘
   │  Postgres   │             │  • BigQuery     │  direct (dev)
   │  metadata   │             │  • MotherDuck   │
   │             │             │  • Snowflake    │
   │  datasets   │             │  • Databricks   │
   │  malloy_    │             │   (or S3/GCS)   │
   │    models   │             │                 │
   │  users      │             │  your data      │
   └─────────────┘             └─────────────────┘

Adding a dataset

Develop your model in a repo with an index.malloy at its root, then publish it with the malloyyo CLI:

malloyyo login <target>     # one-time browser sign-in
malloyyo publish <target>   # bundle *.malloy + malloy-config.json and push

The CLI records the git commit it published from; Malloyyo compiles and introspects the model and stores a new version. If it doesn't compile, the push is rejected and the live model is left unchanged.

Alternatively, point Malloyyo at a GitHub repo and it pulls index.malloy (and any imports) directly — a webhook endpoint (/api/datasets/<id>/webhook/github) refreshes it on every push.

The two databases

Database What lives there
Cloud database (BigQuery, Snowflake, MotherDuck, MySQL, Postgres, Presto, Trino) or S3/GCS Your analytical data
Neon Postgres Metadata — datasets, malloy_models, malloy_model_files, users, accounts, sessions, OAuth clients and tokens

MCP tools served at /mcp

Tool What it does
list_sources List the Malloy sources you can query on this endpoint
describe_source A source's semantic model — measures, dimensions, views, joins
query Run a Malloy query; returns rows + a shareable link (execute: false for SQL only)
open_share_link Resolve a shared link back to its source, question, and Malloy

The MCP endpoint speaks OAuth 2.1, so claude.ai's remote MCP integration can connect after a one-time authorization flow.

Developing Malloy models

You don't have to know Malloy — or wire up the database by hand. Install the malloyyo CLI, register it with Claude, then ask Claude to do the rest: connect to your data, turn your existing SQL / dbt / Looker definitions into a Malloy model, and test it against real data before you publish. One tool for the whole loop.

1. Install and register the local test window.

npm install -g @malloydata/malloyyo        # one tool for the whole loop
claude mcp add malloyyo -- malloyyo mcp    # register it with Claude Code

malloyyo mcp runs a local stdio MCP server over the Malloy model in the current directory — the same explore surface (list_sources, describe_source, query) the hosted instance serves, so what you test locally is exactly what consumers get. It also registers the writing-malloy-with-mcp skill and yo_help, which teach Claude how to author Malloy, set up connections, and recover from compiler errors. (For Claude Desktop or another client, add the same malloyyo mcp command to its MCP config instead.)

2. Build your model with Claude. Start claude in your model directory and just describe what you have:

claude

> connect to my Postgres warehouse and build a Malloy model from these dbt sources
> add a "net revenue" measure and verify it against last quarter's numbers

Claude sets up the connection and writes the .malloy for you. It follows a compiler-in-the-loop discipline — validate against the compiler, run real queries through the test window, confirm the numbers — and you steer from the results. You rarely write Malloy by hand.

The connection lives in malloy-config.json at the model root, and Claude knows how to write it. Two things worth knowing:

  • DuckDB needs no config at all — with no malloy-config.json, an in-memory duckdb connection just works, so Claude can read your local CSV / Parquet files immediately. Other backends get a small malloy-config.json entry.
  • Secrets stay out of the committed file — any value can be { "env": "VAR_NAME" } (e.g. ANALYTICAL_DATABASE_SECRET), resolved from the environment when the connection opens.

3. Publish. Once the model queries cleanly, sign in once and push:

malloyyo login <your-instance>     # one-time browser sign-in
malloyyo publish <your-instance>   # bundle *.malloy + malloy-config.json and push

Stack

  • Next.js 16 App Router
  • Your Analytical Database
  • Neon Postgres + DrizzleORM
  • Malloy
  • NextAuth v5 + Google OAuth
  • OAuth 2.1 provider
  • malloyyo CLI

Deploy your own

The button forks the repo into your GitHub and creates a Vercel project. The schema self-initializes on first boot (RUN_MIGRATIONS_ON_BOOT=1), so you never run a migration.

Your model's malloy-config.json references your analytical database's secret from an env var (e.g. ANALYTICAL_DATABASE_SECRET — see Developing Malloy models). Set that var on the project so the server can connect. GITHUB_TOKEN is optional (private-repo model pulls).

Self-host with Docker

Prefer containers to Vercel? The repo ships a production Dockerfile. See Self-hosting with Docker for build and run instructions.

Running locally

Copy .env.local.example to local/<instance> and fill in the blanks:

DATABASE_URL=postgresql://...          # Neon (or any Postgres)
APP_BASE_URL=http://localhost:3000
APP_ADMIN_EMAILS=you@example.com
AUTH_SECRET=...                        # openssl rand -base64 32
AUTH_GOOGLE_ID=...                     # Google OAuth client ID
AUTH_GOOGLE_SECRET=...                 # Google OAuth client secret

Google sign-in needs a Google OAuth app. Add this Authorized redirect URI:

http://localhost:3000/api/auth/callback/google
npm install
npx dotenv-cli -e local/main -- npx drizzle-kit push   # first run only
npx dotenv-cli -e local/main -- npm run dev

Code map

  1. packages/cli + src/app/api/datasets/[id]/model/push — the malloyyo publish path.
  2. src/lib/github.ts + src/lib/github-refresh.ts — the GitHub pull path.
  3. src/lib/malloy.ts — single-file and multi-file Malloy compilation and execution via InMemoryURLReader.
  4. src/lib/mcp-tools.ts + src/app/mcp/route.ts — the MCP server.
  5. src/db/schema.ts — Drizzle schema for all Postgres tables.
Explore all 57 articles in today's digest

Fresh dev news, AI-digested daily. Never miss a trend at devoured.fyi