73 2026-01-17 · 7 MIN · SHORT-FORM

AI Data Warehouse Access with MCP and Trino

Building composable MCP servers for enterprise data

Diagram · 73
sequenceDiagram
    autonumber
    actor U as Analyst
    participant A as Claude / AI
    participant M as mcp-trino
    participant T as Trino
    participant PG as PostgreSQL
    participant S3 as S3 / Iceberg
    participant K as Kafka

    U->>A: Last quarter orders by region?
    A->>M: list_schemas()
    M->>T: SHOW SCHEMAS
    T-->>M: catalogs and tables
    M-->>A: schema graph
    A->>M: execute_query(SQL)
    M->>T: parse + federated plan
    T->>PG: SELECT customers
    T->>S3: scan parquet partitions
    T->>K: scan recent stream
    PG-->>T: rows
    S3-->>T: rows
    K-->>T: rows
    T-->>M: federated result set
    M-->>A: rows + metadata
    A-->>U: answer with chart

Data lives in PostgreSQL for transactions, MySQL for legacy systems, S3 for analytics, Kafka for streams. Writing a custom integration for each source does not scale. mcp-trino exposes Trino’s federated query engine to AI assistants through MCP.

§What is MCP?

MCP (Model Context Protocol) is a standard for AI assistants to interact with external tools and data sources. Instead of every AI vendor building proprietary integrations, MCP provides a common interface. OpenAI, Google, and Microsoft adopted it in 2025.

An MCP server exposes three primitives. Tools are functions the assistant can call (execute SQL, list schemas). Resources are data it can read (query results, table definitions). Prompts are pre-built instructions for common tasks.

The AI assistant spawns the MCP server as a subprocess, communicates over stdio, and calls tools as needed. MCP itself does not add an API key, a network listener, or a cloud dependency.

§What is Trino?

Trino is a distributed SQL query engine. It does not store data. Instead, it connects to your existing data sources through connectors and lets you query them with standard SQL.

graph TD
    T[Trino] --> PG[PostgreSQL]
    T --> MY[MySQL]
    T --> S3[S3/Object Storage]
    T --> KA[Kafka]
    T --> IC[Iceberg]

One SQL query can join data across PostgreSQL, MySQL, and S3. Trino handles the federation, optimization, and execution. Your AI assistant does not need to know where data lives; it just writes SQL.

Common connectors include PostgreSQL, MySQL, SQL Server, and Oracle on the relational side; Iceberg, Delta Lake, Hive, and S3 for data lakes; Kafka and Kinesis for streams; MongoDB, Elasticsearch, and Redis for NoSQL.

§mcp-trino

mcp-trino is an MCP server that exposes Trino to AI assistants. Install it, configure your Trino connection, and your AI can query your data warehouse.

Install:

go install github.com/txn2/mcp-trino@latest

Configure Claude Code:

claude mcp add --transport stdio mcp-trino -- \
  mcp-trino --host trino.example.com --port 8080 --user analyst

Your AI assistant now has access to every data source connected to your Trino cluster.

§How It Works

graph LR
    AI[AI Assistant] -->|MCP stdio| MCP[mcp-trino]
    MCP -->|SQL/HTTP| TR[Trino]
    TR --> DS[Data Sources]

The AI assistant spawns mcp-trino as a subprocess. When it needs data, it calls the execute_sql tool with a query. mcp-trino sends the query to Trino over HTTP, formats the results, and returns them to the AI.

Core tools:

  • execute_sql: Run any SQL query against Trino
  • list_catalogs: Show available data sources
  • list_schemas: Show schemas in a catalog
  • list_tables: Show tables in a schema
  • describe_table: Get column definitions

Read-only by default. The AI can explore and query but not modify data.

§Standalone Usage

Configure mcp-trino with environment variables or command-line flags:

export TRINO_HOST=trino.example.com
export TRINO_PORT=8080
export TRINO_USER=analyst
export TRINO_CATALOG=hive
export TRINO_SCHEMA=analytics

mcp-trino

Or pass flags directly:

mcp-trino --host trino.example.com --port 8080 --user analyst

§Example Conversation

Here is Claude Code with mcp-trino answering questions from a data warehouse:

> What were the top 10 products by revenue last quarter?

I'll query the sales data to find the top products.

⏺ mcp-trino:execute_sql
  ⎿  Running: SELECT product_name, SUM(revenue) as total_revenue
              FROM hive.analytics.sales
              WHERE sale_date >= DATE '2025-10-01'
                AND sale_date < DATE '2026-01-01'
              GROUP BY product_name
              ORDER BY total_revenue DESC
              LIMIT 10

Here are the top 10 products by revenue for Q4 2025:

| Product              | Revenue      |
|---------------------|--------------|
| Enterprise License  | $2,340,000   |
| Professional Plan   | $1,890,000   |
| Team Subscription   | $1,245,000   |
...

The Enterprise License drove the most revenue at $2.34M,
followed by Professional Plan at $1.89M.

> How does that compare to the same quarter last year?

⏺ mcp-trino:execute_sql
  ⎿  Running: WITH current AS (...), previous AS (...)
              SELECT c.product_name, c.revenue, p.revenue as prev_revenue,
                     (c.revenue - p.revenue) / p.revenue * 100 as growth
              FROM current c JOIN previous p ON ...

Year-over-year comparison for Q4:

| Product              | Q4 2025      | Q4 2024      | Growth  |
|---------------------|--------------|--------------|---------|
| Enterprise License  | $2,340,000   | $1,950,000   | +20.0%  |
| Professional Plan   | $1,890,000   | $2,100,000   | -10.0%  |
...

Enterprise License grew 20% year-over-year. Professional Plan
declined 10%, which may warrant investigation into pricing or
competitive pressure.

The AI writes SQL, executes it against Trino, and interprets the results. Nobody exported a CSV or waited for a dashboard.

§mcp-trino as a Go Library

This is where mcp-trino differs from most MCP servers. The published binary is a thin wrapper. The same code is a Go library: import it into your own MCP server and wrap the tools with middleware.

package main

import (
    "github.com/txn2/mcp-trino/pkg/trino"
    "github.com/mark3labs/mcp-go/server"
)

func main() {
    // Create the Trino MCP tools
    trinoTools := trino.NewTools(trino.Config{
        Host:    "trino.example.com",
        Port:    8080,
        User:    "analyst",
        Catalog: "hive",
        Schema:  "analytics",
    })

    // Create your MCP server
    s := server.NewMCPServer("my-data-server", "1.0.0")

    // Add Trino tools with your middleware
    for _, tool := range trinoTools.GetTools() {
        s.AddTool(tool, withAuditLog(withAuth(trinoTools.Handler(tool))))
    }

    // Add your own custom tools
    s.AddTool(myCustomTool, myCustomHandler)

    s.Start()
}
graph TD
    CS[Your Custom MCP Server] --> MT[mcp-trino library]
    CS --> YH[Your Handlers]
    MT --> AU[Auth Middleware]
    MT --> AD[Audit Middleware]
    MT --> FI[Query Filter]

§What You Can Add

Authentication before a query runs:

func withAuth(next server.ToolHandler) server.ToolHandler {
    return func(ctx context.Context, req server.ToolRequest) (*server.ToolResponse, error) {
        user := getUserFromContext(ctx)
        if !isAuthorized(user) {
            return nil, errors.New("unauthorized")
        }
        return next(ctx, req)
    }
}

Audit logging of every query, for compliance:

func withAuditLog(next server.ToolHandler) server.ToolHandler {
    return func(ctx context.Context, req server.ToolRequest) (*server.ToolResponse, error) {
        log.Printf("User %s executing: %s", getUser(ctx), req.Params["query"])
        return next(ctx, req)
    }
}

Query filtering to block dangerous patterns or enforce row-level security:

func withQueryFilter(next server.ToolHandler) server.ToolHandler {
    return func(ctx context.Context, req server.ToolRequest) (*server.ToolResponse, error) {
        query := req.Params["query"].(string)
        if containsDangerousPattern(query) {
            return nil, errors.New("query blocked by policy")
        }
        return next(ctx, req)
    }
}

Custom tools for domain-specific operations alongside Trino.

You control the server. No forking, no patching, no waiting for upstream features.

§Why Composable MCPs Matter

Most MCP servers are monolithic binaries. You configure them, run them, and hope they do what you need. When they do not, you fork the project or file a feature request.

Composable MCPs are building blocks. mcp-trino provides the Trino integration; you provide the policy layer. That split is what I need on client platforms: authentication without editing mcp-trino, audit logging without a logging patch, multiple MCP tools in one process, and company-specific tools next to the standard ones.

The MCP ecosystem is young. A library you can wrap in your own policy will outlast a binary you have to fork.

§Quick Trino Setup

If you do not have Trino running, start one locally with Docker:

docker run -d -p 8080:8080 --name trino trinodb/trino

This gives you a single-node Trino with memory and TPC-H connectors for testing. Point mcp-trino at localhost:8080 and you can immediately query sample data.

For production, see the Trino documentation for connector configuration. Common setups include:

  • PostgreSQL connector for transactional data
  • Hive/Iceberg connector for data lake tables
  • Kafka connector for streaming data

Each connector requires adding a properties file to Trino’s etc/catalog/ directory.

§What this replaces, and what it does not

APIs were the last integration wave. REST still requires someone to write the client, the endpoints, and the maintenance for every new source.

MCP is a tool interface the assistant can call. The integration is still code, but it lives in the MCP server, not a new dashboard or a new client library per source. An analyst who used to wait for a dashboard can ask a question; the SQL still runs in Trino because the assistant has execute_sql.

mcp-trino is that slice: federated SQL, no custom connector per warehouse source. CRM, ticketing, and observability are separate servers. This post does not wire them into one assistant.

§Resources

← back to all notes