Add a read-only MCP server for SpacetimeDB#5382
Conversation
Set up the initial `spacetimedb-mcp` crate: a Model Context Protocol server on the official `rmcp` 1.7 SDK, communicating over stdio. The crate is registered as a workspace member, with `rmcp` and `schemars` added to the workspace dependencies. Includes a single `ping` health-check tool to confirm the end-to-end JSON-RPC flow (initialize, tools/list, tools/call) works. Logging is routed to stderr only, so it never corrupts the stdout protocol stream. SpacetimeDB-specific tools (schema and reducer introspection, SQL, subscriptions, and the CLI workflow) will follow in later commits.
Add get_schema, list_tables, and list_reducers tools backed by a small
HTTP client that queries a running SpacetimeDB host's schema endpoint
(GET /v1/database/{name}/schema) and decodes it into the in-tree
RawModuleDefV9 type, reusing the same mechanism as `spacetime describe`.
The host and optional auth token are read from SPACETIMEDB_HOST and
SPACETIMEDB_TOKEN; the target database is a per-call argument, so one
server can introspect any database on the host.
Write operations, SQL, and subscriptions remain out of scope.
Split the schema-to-output shaping into pure functions (introspect module) so it can be unit-tested without a server, and add a Client::new constructor so the HTTP client can target an arbitrary host. Add tests: unit tests for table/reducer shaping and the SerdeWrapper / DeserializeWrapper round trip the client depends on, plus an integration test that serves a canned schema over a throwaway HTTP server and exercises the full fetch-and-decode path (no running instance required). Improve error messages for unreachable hosts, missing databases (404), and non-success responses.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ab09760d68
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| /// may be either a database name or an identity. | ||
| pub async fn module_def(&self, database: &str) -> anyhow::Result<RawModuleDefV9> { | ||
| let host = self.host.trim_end_matches('/'); | ||
| let url = format!("{host}/v1/database/{database}/schema"); |
There was a problem hiding this comment.
Escape the database path segment before issuing requests
Because the tool argument is interpolated directly into the URL, an MCP caller can include / or ? and make this authenticated request hit a different endpoint than /schema; for example database = "mydb/logs?follow=true&" becomes a GET to /v1/database/mydb/logs?... with the bearer token, and the existing logs route streams when follow=true (crates/client-api/src/routes/database.rs lines 620-652), so .json() can hang instead of returning schema. Validate the value as a SpacetimeDB name/identity or percent-encode it as a single path segment before building the URL.
Useful? React with 👍 / 👎.
Description of Changes
Adds
spacetimedb-mcp, a new workspace crate: a Model ContextProtocol
server that lets MCP-aware agents and editors introspect a SpacetimeDB
database. Built on the official Rust MCP SDK (
rmcp), it speaks JSON-RPCover stdio.
This is an intentionally small, read-only first cut, scoped per discussion
with @bradleyshep. It exposes four tools:
ping— health check.get_schema— full module definition (typespace, tables, reducers) as JSON.list_tables— table names.list_reducers— reducers with their lifecycle role.Schema is fetched from a running host's
GET /v1/database/{name}/schemaendpoint — the same path
spacetime describeuses — and decoded into thein-tree
RawModuleDefV9type, so the server stays in lockstep with theengine rather than reparsing text. Host and optional auth token come from
SPACETIMEDB_HOST/SPACETIMEDB_TOKEN; the target database is a per-callargument.
Deliberately out of scope for now (possible follow-ups): SQL, subscriptions,
reducer calls, logs, publish/generate, and any write operations.
Opening as a draft to get direction on whether this belongs in-tree and on
the approach before expanding scope.
API and ABI breaking changes
None. This is an additive new crate; no existing crates are modified beyond
registering the workspace member and two new workspace dependencies (
rmcp,schemars).Expected complexity level and risk
1 — a small, self-contained, additive crate. It only reads from an existing
public HTTP endpoint and touches no engine internals. Its one coupling point
is the
RawModuleDefV9schema type, which it consumes read-only.Testing
SerdeWrapper/DeserializeWrapperround trip the client relies on.server and exercises the full fetch-and-decode path (no running
instance required) —
cargo test -p spacetimedb-mcp.tools return correct results.
crates/) and that pullingrmcp+reqwestinto the workspace is acceptable.