This is an automated email from the ASF dual-hosted git repository.

jerryshao pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gravitino-playground.git


The following commit(s) were added to refs/heads/main by this push:
     new 74d8b42  Add Trino MCP server to the playground (#159)
74d8b42 is described below

commit 74d8b4218ffbf99cca7ac9e2872f74588fe873be
Author: Mark Hoerth <[email protected]>
AuthorDate: Tue Jul 21 02:47:53 2026 -0700

    Add Trino MCP server to the playground (#159)
    
    ### What changes were proposed in this pull request?
    
    Adds a second MCP server alongside the existing Gravitino MCP server,
    giving an AI agent a natural-language SQL path to the playground's Trino
    coordinator. The Gravitino MCP server exposes governed metadata; the
    Trino MCP server executes queries across the federated catalogs.
    
    The server (ghcr.io/tuannvm/mcp-trino) runs as a new service in
    docker-trino-mcp-override.yaml, published on host port 8001, and speaks
    streamable HTTP at /mcp. It rides the same MCP toggle as the Gravitino
    MCP server: on by default, excluded with --disable-mcp.
    
    Changes:
    - docker-trino-mcp-override.yaml: new Trino MCP service, depends on
    Trino
    - playground.sh: include the override when MCP is enabled
    - README.md: reorganized MCP section covering both servers, plus
    connection instructions for Claude Code, Claude Desktop, and other
    clients
    
    ### Why are the changes needed?
    
    The playground already ships a Gravitino MCP server for governed
    metadata, but there is no path for an agent to run SQL against the
    federated catalogs. This adds that query capability, so the playground
    demonstrates both governed metadata access and natural-language querying
    over the same catalogs.
    
    ### Does this PR introduce _any_ user-facing change?
    
    Yes:
    - A new Trino MCP server on host port 8001 (streamable HTTP at /mcp), up
    by default and excluded with --disable-mcp.
    - README documentation for connecting Claude Code, Claude Desktop, and
    other MCP clients to both servers.
    
    No changes to existing APIs or property keys.
    
    ### How was this patch tested?
    
    - Full stack comes up healthy with the new playground-trino-mcp
    container.
    - `curl -i http://localhost:8001/mcp -H "Accept: application/json,
    text/event-stream"` returns HTTP 200 with a text/event-stream response.
    - Connected and ran federated queries across catalogs from both Claude
    Code and Claude Desktop.
    - `bash -n playground.sh` and `docker compose -f docker-compose.yaml -f
    docker-mcp-override.yaml -f docker-trino-mcp-override.yaml config` both
    pass.
    
    Co-authored-by: Mark Hoerth <[email protected]>
---
 README.md                      | 130 ++++++++++++++++++++++++++++++++++++-----
 docker-trino-mcp-override.yaml |  50 ++++++++++++++++
 playground.sh                  |   1 +
 3 files changed, 165 insertions(+), 16 deletions(-)

diff --git a/README.md b/README.md
index 57cfbf9..e9c221a 100644
--- a/README.md
+++ b/README.md
@@ -21,7 +21,7 @@
 
 Apache Gravitino is a federated metadata catalog: it presents many different 
data systems, Hive, Iceberg, relational databases, object storage, files, as 
one governed namespace, so a single engine or agent can discover and query 
across all of them, with access control and audit applied at the source rather 
than bolted onto each tool.
 
-This playground is a complete, runnable environment for seeing that in action. 
It brings up `Hive`, `HDFS`, `Trino`, `Spark`, `MySQL`, `PostgreSQL`, `Ranger`, 
`Jupyter`, `Prometheus`, `Grafana`, and a `Gravitino` server, all wired 
together, so you can join data across catalogs, govern it with roles and 
policies, and let an AI agent reason over the same governed metadata through 
the [Model Context Protocol](#gravitino-mcp-server).
+This playground is a complete, runnable environment for seeing that in action. 
It brings up `Hive`, `HDFS`, `Trino`, `Spark`, `MySQL`, `PostgreSQL`, `Ranger`, 
`Jupyter`, `Prometheus`, `Grafana`, and a `Gravitino` server, all wired 
together, so you can join data across catalogs, govern it with roles and 
policies, and let an AI agent reason over the same governed metadata through 
the [Model Context Protocol](#mcp-servers).
 
 Depending on your network and computer, startup takes 3-5 minutes. Once it is 
running, open [http://localhost:8090](http://localhost:8090) for the Gravitino 
Web UI. The quickest way to see what the playground does is the [Jupyter 
notebooks](#jupyter-notebooks).
 
@@ -52,8 +52,8 @@ cd gravitino-playground
 ### Start Options
 
 ```bash
-./playground.sh start                 # full stack, MCP server on by default
-./playground.sh start --disable-mcp   # bring the stack up without the MCP 
server
+./playground.sh start                 # full stack, MCP servers on by default
+./playground.sh start --disable-mcp   # bring the stack up without the MCP 
servers
 ./playground.sh start --enable-ranger # add Ranger authorization
 ./playground.sh start --enable-auth   # enable authentication
 ```
@@ -99,7 +99,7 @@ The notebooks are the quickest way to see what the playground 
does, and for many
 
 ### AI
 
-- **`gravitino-mcp-demo.ipynb`** connects to the Gravitino MCP server, lists 
the tools Gravitino exposes, seeds governance tags, walks a governed table, and 
(with an LLM API key) answers plain-language questions by calling the tools 
itself. See the [Gravitino MCP Server](#gravitino-mcp-server) section.
+- **`gravitino-mcp-demo.ipynb`** connects to the Gravitino MCP server, lists 
the tools Gravitino exposes, seeds governance tags, walks a governed table, and 
(with an LLM API key) answers plain-language questions by calling the tools 
itself. See the [MCP Servers](#mcp-servers) section.
 - **`gravitino_llamaIndex_demo.ipynb`** is a retrieval-augmented generation 
demo that treats Gravitino as a unified data source. Structured city statistics 
live in MySQL (a relational catalog) and city descriptions live in PDF files (a 
fileset catalog); a single natural-language question is answered from both by 
joining a SQL index over the table with a vector index over the documents. It 
requires an `OPENAI_API_KEY` from an account with available credit. Set it the 
same way as the Anthr [...]
 
   ```bash
@@ -117,9 +117,16 @@ The notebooks are the quickest way to see what the 
playground does, and for many
 - **`gravitino-fileset-example.ipynb`** manages the fileset metadata 
lifecycle: creating a fileset catalog and schema, registering managed and 
external filesets, and observing how each behaves in HDFS.
 - **`gravitino-gvfs-example.ipynb`** reads unstructured data (PDFs) through 
the Gravitino Virtual File System, so file access goes through a governed 
fileset path rather than a raw storage location. No LLM required.
 
-## Gravitino MCP Server
+## MCP Servers
 
-The playground runs the **Gravitino MCP server**, which exposes Gravitino 
metadata to AI agents over the [Model Context 
Protocol](https://modelcontextprotocol.io). An agent connected here does not 
get raw files; it gets governed metadata (catalogs, schemas, tables, tags, and 
policies) through a uniform tool surface, with every call subject to 
Gravitino's authorization and recorded in its audit log. It is on by default 
and listens on port 8000; start with `--disable-mcp` to leave it out.  [...]
+The playground runs two [Model Context 
Protocol](https://modelcontextprotocol.io) servers, so an AI agent can both 
reason over governed metadata and run queries against it. Both are on by 
default and share the same toggle: start with `--disable-mcp` to leave them out.
+
+- **Gravitino MCP server** (port 8000) exposes Gravitino metadata: catalogs, 
schemas, tables, tags, and policies, through a uniform tool surface. An agent 
connected here does not get raw files; every call is subject to Gravitino's 
authorization and recorded in its audit log. For more information, see the 
[Gravitino MCP 
server](https://gravitino.apache.org/docs/latest/gravitino-mcp-server/) 
documentation.
+- **Trino MCP server** (port 8001, 
[tuannvm/mcp-trino](https://github.com/tuannvm/mcp-trino)) gives an agent a 
natural-language SQL path to the playground's Trino coordinator. Where the 
Gravitino server exposes metadata, the Trino server executes queries, so an 
agent answers analytical questions ("who is the employee with the largest total 
sales?") by generating SQL and running it across the federated catalogs. It 
exposes tools to list catalogs, schemas, and tables, describe and explain  [...]
+
+The two take different paths to the data. The Gravitino server serves governed 
metadata, with every call subject to Gravitino's authorization. The Trino 
server executes SQL against the coordinator, and the data it reads through 
Gravitino-managed catalogs is governed at the source, so with `--enable-auth` 
or `--enable-ranger` those reads are subject to the same authorization. The 
Trino server connects as a single fixed Trino user, so it does not distinguish 
one MCP caller from another.
+
+Both servers speak streamable HTTP, the Gravitino server at 
`http://localhost:8000/mcp` and the Trino server at `http://localhost:8001/mcp`.
 
 ### Try It with an Agent (Recommended)
 
@@ -133,25 +140,103 @@ To pre-set the key so there is no prompt (handy for your 
own iteration), put it
 echo 'ANTHROPIC_API_KEY=sk-ant-...' >> .env   # do not commit this file
 ```
 
-### Connect Your Own MCP Client
+### Connect with Claude Code
+
+[Claude Code](https://docs.claude.com/en/docs/claude-code) speaks the 
streamable HTTP transport natively, so it connects to either server with a 
single command. No proxy, stdio bridge, or TLS certificate is needed.
+
+In the commands below, use `localhost` if Claude Code runs on the same host as 
the playground, or the playground host's address (for example 
`http://<playground-host>:8000/mcp`) if it runs elsewhere. Both servers must 
point at the same place.
+
+Register the Gravitino MCP server:
+
+```bash
+claude mcp add --transport http gravitino http://localhost:8000/mcp
+```
+
+Register the Trino MCP server:
+
+```bash
+claude mcp add --transport http trino http://localhost:8001/mcp
+```
+
+Claude Code loads MCP servers at startup, so restart it (or run `/mcp`) after 
adding a server. `/mcp` shows connection status and lets you browse each 
server's tools. A healthy server appears connected and lists its tools; a 
failure there points at reachability rather than Claude Code.
+
+By default Claude Code prompts before every MCP tool call. To approve a whole 
server once instead of per tool, run `/permissions` and add a wildcard rule:
+
+```
+mcp__gravitino__*
+mcp__trino__*
+```
+
+You can also set these without opening Claude Code by editing its settings 
file directly. Use `~/.claude/settings.json` to apply the rules everywhere, or 
`<project-dir>/.claude/settings.json` to scope them to the directory the 
servers were registered under. Add a `permissions.allow` array, merging into 
any existing settings rather than overwriting them:
+
+```json
+{
+  "permissions": {
+    "allow": [
+      "mcp__gravitino__*",
+      "mcp__trino__*"
+    ]
+  }
+}
+```
+
+Claude Code reads the file at startup, so edit it before launching (or restart 
afterward). Validate the JSON before starting, since a syntax error stops the 
whole file from loading: `python3 -m json.tool ~/.claude/settings.json`.
+
+With both servers connected, ask questions in plain language and Claude Code 
calls the tools itself. The Gravitino server answers metadata and governance 
questions ("which tables carry a PII tag?"), and the Trino server runs 
analytical queries that federate across catalogs ("who is the employee with the 
largest total sales?"), joining `catalog_hive.sales` and `catalog_postgres.hr` 
the same way the [Trino federation example](#query-across-catalogs) does.
+
+### Connect with Claude Desktop
+
+Claude Desktop does not read HTTP MCP URLs from its config file directly, and 
its custom-connector UI opens connections from a cloud service that cannot 
reach a local or private-network playground. The way to connect Desktop to the 
playground is the [`mcp-remote`](https://www.npmjs.com/package/mcp-remote) 
stdio bridge: a small local process that Desktop launches, which speaks stdio 
to Desktop and HTTP to the playground.
+
+You need [Node.js](https://nodejs.org) 18 or higher installed on the machine 
running Claude Desktop, since the bridge runs through `npx`.
+
+Open the config file from **Settings -> Developer -> Edit Config** (which 
creates it if needed), or edit it directly:
+
+- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
+- Windows: `%APPDATA%\Claude\claude_desktop_config.json`
+- Linux: `~/.config/Claude/claude_desktop_config.json`
 
-The server speaks streamable HTTP at `http://localhost:8000/mcp`. Point any 
MCP-capable client (Claude Desktop, Cursor, a custom agent) at that URL:
+Add both servers under `mcpServers`, merging into any existing entries. Use 
`localhost` if Desktop runs on the same host as the playground, or the 
playground host's address otherwise:
 
 ```json
 {
   "mcpServers": {
-    "gravitino": { "url": "http://localhost:8000/mcp"; }
+    "gravitino": {
+      "command": "npx",
+      "args": ["-y", "mcp-remote", "http://localhost:8000/mcp";, "--allow-http"]
+    },
+    "trino": {
+      "command": "npx",
+      "args": ["-y", "mcp-remote", "http://localhost:8001/mcp";, "--allow-http"]
+    }
   }
 }
 ```
 
-If your client runs on a different machine than the playground, reach the 
endpoint over an SSH tunnel rather than exposing the port:
+Then fully quit Claude Desktop (quit the app, not just close the window) and 
reopen it so the new config loads. The gravitino and trino tools then appear in 
the connectors menu, and you can ask questions in plain language the same way 
as with Claude Code.
 
-```bash
-ssh -L 8000:localhost:8000 <user>@<playground-host>
+A few details specific to Desktop:
+
+- **`--allow-http` is required for a plain-HTTP playground.** `mcp-remote` 
refuses non-HTTPS URLs unless the host is `localhost` or the flag is set. The 
playground runs without TLS, so the flag is needed whenever you point at it by 
address rather than `localhost`. It signals that unencrypted transport is 
acceptable, which is fine for a local playground you control and not for 
anything carrying real credentials.
+- **When authentication is off (the default), send no token.** The bridge 
connects with just the URL and `--allow-http`. Only add an `Authorization` 
header (a `"--header", "Authorization: Bearer <token>"` pair in `args`) when 
the playground runs with `--enable-auth`; sending a token to a no-auth server, 
or a stale token to an auth server, causes the connection to drop.
+- **On Windows, a bare `npx` often fails.** Claude Desktop launches config 
commands with a minimal PATH, so set `command` to the full path to `npx.cmd`, 
for example `C:\\Program Files\\nodejs\\npx.cmd`. On Windows with WSL, note 
that Desktop is a Windows app and runs the bridge with Windows Node, not the 
Node inside WSL.
+- **Test the bridge by hand before restarting Desktop.** Running the same 
command in a terminal shows a clear error if something is wrong, where Desktop 
only shows "server disconnected". For example: `npx -y mcp-remote 
http://localhost:8001/mcp --allow-http`. A successful run prints "Proxy 
established successfully" and waits.
+- **Approving tools.** By default Desktop prompts before each tool call. To 
stop the prompts for a trusted server, open **Settings -> Connectors**, expand 
the connector, and set its tools to **Always Allow**.
+
+### Connect Another MCP Client
+
+Many MCP clients (Cursor, Windsurf, custom agents) accept the same 
`mcpServers` config shape, though each reads it from its own file, for example 
`~/.cursor/mcp.json` for Cursor or `~/.codeium/windsurf/mcp_config.json` for 
Windsurf. Consult your client's documentation for the exact location, then add 
both servers in that file:
+
+```json
+{
+  "mcpServers": {
+    "gravitino": { "url": "http://localhost:8000/mcp"; },
+    "trino": { "url": "http://localhost:8001/mcp"; }
+  }
+}
 ```
 
-When authentication is enabled (`--enable-auth`), the server forwards each 
request's `Authorization` header to Gravitino, which authorizes the call per 
principal. Add the header in your client to make MCP calls run under a specific 
identity:
+When authentication is enabled (`--enable-auth`), the Gravitino server 
forwards each request's `Authorization` header to Gravitino, which authorizes 
the call per principal. Add the header in your client to make MCP calls run 
under a specific identity:
 
 ```json
 {
@@ -164,15 +249,27 @@ When authentication is enabled (`--enable-auth`), the 
server forwards each reque
 }
 ```
 
-### Verify the Endpoint with MCP Inspector
+Clients that speak only stdio, rather than HTTP, reach these servers through 
the [`mcp-remote`](https://www.npmjs.com/package/mcp-remote) bridge instead, 
configured the same way as in the Claude Desktop section above.
+
+### Connecting from Another Machine
+
+A client reaches the servers by the playground host's address in place of 
`localhost`, for example `http://<playground-host>:8000/mcp`. Claude Code 
connects from wherever it runs, so this is all it needs.
+
+The playground has no TLS, so restrict who can reach the ports rather than 
exposing them broadly, for example with firewall or security-group rules scoped 
to the specific client address, by keeping the traffic on a private network, or 
over an SSH tunnel:
+
+```bash
+ssh -L 8000:localhost:8000 -L 8001:localhost:8001 <user>@<playground-host>
+```
+
+### Verify an Endpoint with MCP Inspector
 
-To confirm the server is reachable and browse its tools directly, use the [MCP 
Inspector](https://github.com/modelcontextprotocol/inspector):
+To confirm a server is reachable and browse its tools directly, use the [MCP 
Inspector](https://github.com/modelcontextprotocol/inspector):
 
 ```bash
 npx @modelcontextprotocol/inspector
 ```
 
-Open the URL it prints, set Transport Type to **Streamable HTTP**, enter 
`http://localhost:8000/mcp` (tunnel first if remote), and click **Connect**. 
Under the **Tools** tab, **List Tools** shows the full surface, and running 
`get_list_of_catalogs` returns the demo catalogs. A `400 Bad Request` to a 
plain browser `GET` on `/mcp` is expected: the protocol requires an initialize 
handshake, so that response confirms the server is up.
+Open the URL it prints, set Transport Type to **Streamable HTTP**, enter the 
server URL (`http://localhost:8000/mcp` for Gravitino, 
`http://localhost:8001/mcp` for Trino; tunnel first if remote), and click 
**Connect**. Under the **Tools** tab, **List Tools** shows the full surface. On 
the Gravitino server, running `get_list_of_catalogs` returns the demo catalogs. 
A `400 Bad Request` to a plain browser `GET` on `/mcp` is expected: the 
protocol requires an initialize handshake, so that res [...]
 
 ## Trino
 
@@ -476,6 +573,7 @@ The playground runs several services. The TCP ports used 
may clash with existing
 | playground-prometheus    | 19090                  |
 | playground-grafana       | 13000                  |
 | playground-gravitino-mcp | 8000                   |
+| playground-trino-mcp     | 8001                   |
 
 ## Environment Configuration
 
diff --git a/docker-trino-mcp-override.yaml b/docker-trino-mcp-override.yaml
new file mode 100644
index 0000000..7f7591e
--- /dev/null
+++ b/docker-trino-mcp-override.yaml
@@ -0,0 +1,50 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+# Trino MCP surface override for the Gravitino playground.
+#
+# Adds the tuannvm/mcp-trino server as an HTTP endpoint published on host port
+# 8001, giving an MCP client a direct natural-language SQL path to the
+# playground's Trino coordinator. Compose with the base file:
+#
+#   docker compose -f docker-compose.yaml -f docker-trino-mcp-override.yaml \
+#     -p gravitino-playground up --detach
+#
+# This path talks to Trino directly and is not subject to Gravitino's
+# authorization. It is the fast query lane, distinct from the governed metadata
+# lane the Gravitino MCP server provides. Point any MCP client at
+# http://localhost:8001/mcp.
+#
+services:
+  trino-mcp:
+    image: ghcr.io/tuannvm/mcp-trino:latest
+    container_name: playground-trino-mcp
+    environment:
+      - TRINO_HOST=trino
+      - TRINO_PORT=8080
+      - TRINO_USER=trino
+      - TRINO_SCHEME=http
+      - MCP_TRANSPORT=http
+      - MCP_PORT=8080
+    ports:
+      - "8001:8080"
+    depends_on:
+      trino:
+        condition: service_healthy
+
+
diff --git a/playground.sh b/playground.sh
index f8189d9..2b41c5c 100755
--- a/playground.sh
+++ b/playground.sh
@@ -234,6 +234,7 @@ start() {
   fi
   if [ "${enableMcp}" == true ]; then
     composeFiles="${composeFiles} -f docker-mcp-override.yaml"
+    composeFiles="${composeFiles} -f docker-trino-mcp-override.yaml"
   fi
 
   ${dockerComposeCommand} ${composeFiles} -p ${playgroundRuntimeName} up 
--detach

Reply via email to