An MCP server for RawTree, an analytics database for unstructured data. Query data with SQL, insert JSON, inspect table schemas, review RawTree logs, and manage database credentials from MCP clients like Claude Code, Cursor, and Claude Desktop.
- Queries — Execute SQL reads and supported writes, including inserts from queries, table configuration, and access management, with permissions enforced by the Query API.
- Workflows — Create, inspect, update, pause, resume, and delete scheduled SQL workflows with HTTP or table sinks.
- Saved queries — List, create, update, and delete saved SQL definitions. Run a saved query by passing its SQL and database to
run-query. - Ingest — Insert a single JSON object, arrays of JSON objects, or public URL data.
- Tables — List tables, describe table columns, sizes, and sorting keys, set and change a table's sorting key, and delete tables after explicit confirmation.
- Logs — Inspect RawTree query and insert history with structured filters for type, status, origin, table, hints, time window, and pagination.
- API Keys — List, create, and revoke RawTree API keys for a cluster. Creation responses include the one-time API key value and expiration.
- Organizations — List organizations and manage their members and roles with an OAuth-authenticated user.
- Databases — List, create, verify S3 access for, and delete databases in a cluster.
- Clusters — List, inspect, pause, resume, discover current creation options, verify optional customer-owned S3 access, configure independent per-database S3 access, and provision vertically autoscaling dedicated clusters after explicit confirmation where required. RawTree enforces user and organization-admin authorization.
- Apps — List the app catalog for a cluster, inspect installation state, and install or uninstall apps after explicit confirmation.
- Transports — Supports stdio for local MCP clients and dual-era Streamable HTTP for remote or multi-client deployments, including stateless MCP 2026-07-28 requests and legacy initialize-handshake clients.
Create a RawTree API key from the RawTree CLI, dashboard, or API. A database API key starts with rt_ and is enough for data tools such as run-query, insert-json, list-tables, and list-logs.
The server supports two transport modes: stdio (default) and HTTP.
npx add-mcp @rawtree/mcp --name rawtree --env "RAWTREE_API_KEY=rt_xxxxxxxxx"claude mcp add rawtree -e RAWTREE_API_KEY=rt_xxxxxxxxx -- npx -y @rawtree/mcpOpen the command palette and choose "Cursor Settings" > "MCP" > "Add new global MCP server".
{
"mcpServers": {
"rawtree": {
"command": "npx",
"args": ["-y", "@rawtree/mcp"],
"env": {
"RAWTREE_API_KEY": "rt_xxxxxxxxx"
}
}
}
}Open Claude Desktop settings > "Developer" tab > "Edit Config".
{
"mcpServers": {
"rawtree": {
"command": "npx",
"args": ["-y", "@rawtree/mcp"],
"env": {
"RAWTREE_API_KEY": "rt_xxxxxxxxx"
}
}
}
}Run the server over HTTP for remote or web-based integrations. In HTTP mode, each MCP client authenticates by passing its RawTree API key in the Authorization header.
Start the server:
npx -y @rawtree/mcp --http --port 3000The server listens on http://127.0.0.1:3000 and exposes the MCP endpoint at /mcp using Streamable HTTP.
claude mcp add rawtree --transport http http://127.0.0.1:3000/mcp --header "Authorization: Bearer rt_xxxxxxxxx"{
"mcpServers": {
"rawtree": {
"url": "http://127.0.0.1:3000/mcp",
"headers": {
"Authorization": "Bearer rt_xxxxxxxxx"
}
}
}
}You can also set the port via the MCP_PORT environment variable:
MCP_PORT=3000 npx -y @rawtree/mcp --http--api-key: RawTree database API key for stdio mode--api-url: RawTree API base URL, defaulthttps://api.rawtree.com--database: Database name for scoped routes--org: Organization name for scoped routes--http: Use HTTP transport instead of stdio--port: HTTP port when using--http, default3000orMCP_PORT
Environment variables:
RAWTREE_API_KEY: RawTree database API keyRAWTREE_API_URL: RawTree API base URL, defaulthttps://api.rawtree.comRAWTREE_DATABASE: Database name for scoped routesRAWTREE_ORG: Organization name for scoped routesMCP_PORT: HTTP port when using--http
run-query— Execute one supported SQL statement and return RawTree's JSON query response. Supports reads and writes described below, with organization, cluster, and database overrides.query-format— Format complete SQL without execution or database query logs. Accepts the same scope overrides and forwards cancellation to the API.insert-json— Insert JSON object(s) into a table.insert-from-url— Ingest data from a public URL and wait for completion, returning the inserted row count (ornullwhen unavailable).
run-query calls POST /v1/query using the existing connection. An API key with
sufficient permission can execute supported writes without browser login. The
tool advertises readOnlyHint: false, destructiveHint: true, and
idempotentHint: false because SQL can change persistent data and access controls.
The backend validates syntax, scope, and permissions; MCP does not implement a
separate SQL allowlist.
| Statement family | Permission | Supported forms and limits |
|---|---|---|
| Reads | read_only, read_write, admin, or signed-in session; database-role keys use their grants |
SELECT, WITH, EXPLAIN, DESCRIBE. Use bounded, ordered reads for exploration. |
| Insert from query | read_write, admin, or signed-in session |
INSERT INTO <table> [(columns)] SELECT ..., including a WITH source. Existing target in the selected user database; no VALUES, FORMAT, url(), or urlCluster(). SQL SETTINGS are validated by the engine; default execution time is 30 seconds. |
| Create table | admin or signed-in session |
CREATE TABLE [IF NOT EXISTS] <table> ENGINE = ReplicatedRawMergeTree [ORDER BY <expressions>]. Selected user database only. Optional ON CLUSTER 'default' is accepted. No columns, engine arguments, other engines, AS SELECT, or other table clauses. |
| Table configuration | admin or signed-in session |
ALTER TABLE <table> MODIFY ORDER BY ..., or ADD/MATERIALIZE/CLEAR/DROP PROJECTION or INDEX, including text indexes. One operation, in the selected user database; no ON CLUSTER or IN PARTITION. ADD accepts IF NOT EXISTS; the other operations accept IF EXISTS. Index creation requires an expression and TYPE, with optional positive integer GRANULARITY; no AFTER. |
| Cancel mutations | admin or signed-in session |
KILL MUTATION WHERE table = '<table>' [AND mutation_id = '<id>']. The backend adds the selected user database filter. Only these equality conditions are accepted, in either order; doubled quotes are supported, backslash escapes are not. |
| Roles and grants | admin or organization administrator session |
CREATE ROLE <name>, DROP ROLE [IF EXISTS] <name>, GRANT SELECT/INSERT [(columns)] ON <database>.<table> TO <role>. Grants may combine both privileges and use database.* or *.*. The grant target must be an existing role. No user grants, inheritance, delegation, REVOKE, or role replacement. |
| Workloads and resources | admin or organization administrator session |
CREATE [OR REPLACE] WORKLOAD/RESOURCE, CREATE WORKLOAD/RESOURCE IF NOT EXISTS, DROP WORKLOAD/RESOURCE [IF EXISTS]. Workloads accept IN <parent> and numeric/string SETTINGS, optionally FOR <resource>. Resources accept MASTER THREAD, WORKER THREAD, READ/WRITE DISK <name>, and READ/WRITE ANY DISK; no memory or query-slot resources. |
| Settings profiles | admin or organization administrator session |
CREATE SETTINGS PROFILE [OR REPLACE | IF NOT EXISTS] and DROP SETTINGS PROFILE [IF EXISTS]. Only workload = '<name>' and use_concurrency_control = 0/1, optionally READONLY/CONST, assigned TO 1–32 distinct existing roles. No user targets, inheritance, other settings, or ALTER. |
| Row policies | admin or organization administrator session |
CREATE ROW POLICY [OR REPLACE | IF NOT EXISTS] and DROP ROW POLICY [IF EXISTS], on one table in the selected user database. A scalar SELECT USING filter, optional AS PERMISSIVE/RESTRICTIVE, and 1–32 distinct existing roles. No subqueries, wildcard/user targets, access-storage selectors, or ALTER. |
| Named collections | admin or organization administrator session |
CREATE NAMED COLLECTION [IF NOT EXISTS] <name> AS key = literal, .... Unique keys with string, numeric, or boolean literals and optional OVERRIDABLE/NOT OVERRIDABLE. No expressions, replacement, ALTER, or DROP. |
write_only keys cannot use the Query API. Database-role keys support reads here,
even if their roles have insert grants; their inserts use the JSON insert API.
Grant, profile, and policy targets must be existing roles without same-named
database users. Profiles and policies reject selectors such as ALL, NONE,
and CURRENT_USER. All write families reject RawTree's internal organization.
Table writes and row policies reject system databases. Other mutations, including
UPDATE, DELETE, DROP TABLE, and arbitrary ALTER, are unsupported; use
delete-table for table deletion.
Role, workload/resource, profile, policy, and named-collection management accept
optional ON CLUSTER 'default', preserving engine replication semantics. No
other cluster name is accepted. Row policies with that clause require an explicit
database.table matching the selected database. Management can affect the cluster
beyond the selected database: grant scopes are explicit, while roles, workloads,
resources, profiles, and collections are not database-local. Consult the
API reference and
role reference
for syntax, local versus replicated storage, and distributed DDL limitations.
Named-collection credentials in ON CLUSTER statements are visible to readers of
the distributed DDL queue; use Keeper-backed collection storage without that
clause for credentials.
Index and projection definitions use storage paths such as __raw_data.field,
with casts where a concrete type is required. ADD applies to new data;
MATERIALIZE builds existing data. CLEAR removes stored index/projection data
while keeping its definition; DROP removes the definition. MATERIALIZE and
CLEAR run in the background. Inspect system.mutations for progress and mutation
IDs, and system.projections or system.data_skipping_indices for definitions.
Responses contain meta, data, rows, statistics, and optional hints.
DDL normally returns an empty result; inserts also report
statistics.written_rows; KILL MUTATION returns matched mutations. Writes are
not generally idempotent, and distributed DDL is not transactional: inspect
state before retrying an error or timeout. A successful materialization request
does not mean its background build has finished.
This contract is derived from Platform's backend/src/validation.rs, Query API
dispatch, and each statement family's validators, checked against the canonical
public API and authentication docs. It describes Query API execution, not the
separate workflow SQL restrictions or the formatter's broader syntax support.
These tools target the /v1/workflows API with cron schedules. The backend must have the workflow schedule, query, and sinks with nested settings contracts deployed; top-level enabled and interval_seconds are rejected. The former workflow destinations field is rejected; connector destinations are unchanged. Flattened sink settings are rejected. There are no trigger aliases or workflow-specific telemetry tools.
All five tools require explicit organization and cluster names, including standalone/API-key usage. Configured defaults do not replace these required tool arguments. Workflow definitions have their own query.database; the MCP's default database is never added as a workflow query parameter or substituted for that field.
| Tool | Required parameters | Optional parameters | Result |
|---|---|---|---|
list-workflows |
organization, cluster |
None | { "workflows": [...] }; no pagination |
get-workflow |
organization, cluster, id |
None | Complete workflow object |
create-workflow |
organization, cluster, name, query.database, query.sql |
description, schedule, sinks |
Complete saved workflow object |
update-workflow |
organization, cluster, id, and at least one changed field |
name, description, query, schedule, sinks |
Complete saved workflow object |
delete-workflow |
organization, cluster, id |
None | { "id": "...", "deleted": true } after confirmed deletion |
API-key access requires an admin key bound to the selected organization and cluster. With OAuth, organization members can read and organization admins can create/update/delete. The backend enforces authorization. Workflows execute with organization credentials; expiration or revocation of the creating key does not stop scheduled execution.
Create (create-workflow):
{
"organization": "acme",
"cluster": "production",
"name": "recent-events",
"query": {
"database": "default",
"sql": "SELECT 1 AS value"
},
"description": "Recent events every 30 seconds",
"schedule": {"cron": "*/30 * * * * *", "enabled": false},
"sinks": [
{"type": "http", "settings": {"url": "https://example.com/events", "headers": {"Authorization": "Bearer example-only"}}},
{"type": "table", "settings": {"database": "default", "table": "alerts"}}
]
}Defaults on creation are schedule: {"cron": "* * * * * *", "enabled": true} and sinks: []; schedule.enabled defaults to true when a schedule is supplied, and schedule: null creates a manual-only workflow. Enabled schedules activate recurring SQL execution and sink delivery. SQL supports reads and INSERT INTO ... SELECT ...; validation is performed by the API. schedule.cron is a UTC expression with five fields (minute hour day-of-month month day-of-week) or six with a leading seconds field; the API validates it. If creation has an uncertain result, inspect list-workflows before retrying.
Create, get, and update return the same complete object shape:
{
"id": "00000000-0000-4000-8000-000000000001",
"name": "recent-events",
"query": {
"database": "default",
"sql": "SELECT 1 AS value"
},
"description": "Recent events every 30 seconds",
"schedule": {"cron": "*/30 * * * * *", "enabled": false, "next_run_at": null},
"revision": 1,
"created_at": "2026-10-05T10:00:00Z",
"updated_at": "2026-10-05T10:00:00Z",
"sinks": [
{"type": "http", "id": "00000000-0000-4000-8000-000000000002", "settings": {"url": "https://example.com/events", "url_configured": true, "header_names": ["Authorization"]}},
{"type": "table", "id": "00000000-0000-4000-8000-000000000003", "settings": {"database": "default", "table": "alerts"}}
]
}List (list-workflows):
{ "organization": "acme", "cluster": "production" }Returns { "workflows": [<workflow objects as above>] }, or { "workflows": [] } when empty.
Get (get-workflow):
{ "organization": "acme", "cluster": "production", "id": "00000000-0000-4000-8000-000000000001" }Update / resume (update-workflow):
{
"organization": "acme",
"cluster": "production",
"id": "00000000-0000-4000-8000-000000000001",
"schedule": {"cron": "0 * * * *", "enabled": true}
}The returned object includes the new schedule, its next_run_at, and the backend's updated revision and timestamp. Set schedule.enabled: false to pause; omitted schedule.cron or schedule.enabled keep their saved values, and schedule: null makes the workflow manual-only. Scheduling a manual-only workflow again requires schedule.cron. description: null clears the description. Other omitted fields stay unchanged; empty updates are rejected. Already buffered deliveries may continue after pausing.
Supplying sinks replaces the whole list (maximum five); omission preserves it and [] removes all. New sinks omit id; new HTTP sinks require settings.url. When editing an existing sink, preserve its id and type. Header values are write-only: returned HTTP settings contains url (null when the stored configuration is unreadable), url_configured, and header_names. For an existing HTTP sink, omit settings, settings.url, or settings.headers to preserve the stored configuration. Within a supplied settings.headers map, null preserves the existing value for that name, omitted header names are removed, and {} clears all headers.
For example, this update keeps one existing HTTP sink and its Authorization value, replaces its header list, and removes every other sink:
{
"organization": "acme",
"cluster": "production",
"id": "00000000-0000-4000-8000-000000000001",
"sinks": [
{"type": "http", "id": "00000000-0000-4000-8000-000000000002", "settings": {"headers": {"Authorization": null, "X-Source": "workflow"}}}
]
}Delete (delete-workflow):
{ "organization": "acme", "cluster": "production", "id": "00000000-0000-4000-8000-000000000001" }Returns { "id": "00000000-0000-4000-8000-000000000001", "deleted": true }. Deletion removes the definition and schedule; it does not undo previous SQL writes or completed deliveries. API failures are returned as MCP tool errors rather than successful deletion results.
As with existing tools, the JSON results above are serialized in the MCP content text block.
Workflow SQL and its database belong to query: {"query":{"database":"default","sql":"SELECT 1"}}. Create requires both fields. Updates preserve omitted query fields. Top-level database and sql are rejected.
list-saved-queries— Return all visible saved queries in a cluster, across databases, as{ "queries": [...] }. There is no cursor or pagination.save-query— Create a saved query whenidis omitted, or update one whenidis supplied. Returns the saved query as a flat object.delete-saved-query— Delete a query you own. Returns{ "id": "...", "deleted": true }after the API confirms deletion.run-query— Execute SQL separately using the saved query'ssqlanddatabase, with the same organization and cluster. Saving and listing never execute SQL.
User sessions and OAuth can list their own private queries and cluster-shared queries. Standard API keys with admin, read_write, or read_only permission can list only cluster-shared queries in their bound cluster. Write-only and database-role keys cannot read saved queries. Reading a definition does not grant permission to execute its SQL or access its database.
Creating, updating, and deleting require a user session or OAuth. The API assigns new queries to the authenticated user; callers do not supply a user ID. Only the owner can update or delete a query, including a cluster-shared query. With an API key, these actions return: “This action requires a user session. API keys aren’t supported.”
All examples below show explicit organization and cluster names, as required by hosted deployments configured with requireExplicitScope. Standalone clients may omit names already configured on RawTreeClient. Saved-query requests never inherit the client's database: it is a field of the saved definition, not a list filter.
List saved queries
{
"organization": "acme",
"cluster": "production"
}Response:
{
"queries": [
{
"id": "00000000-0000-4000-8000-000000000001",
"organization_id": "00000000-0000-4000-8000-000000000002",
"cluster_id": "00000000-0000-4000-8000-000000000003",
"user_id": "00000000-0000-4000-8000-000000000004",
"name": "Recent events",
"sql": "SELECT * FROM events LIMIT 10",
"database": "analytics",
"visibility": "private",
"created_at": "2026-10-02T10:00:00Z",
"updated_at": "2026-10-02T10:00:00Z"
}
]
}No visible queries returns { "queries": [] }.
Save query: create
{
"organization": "acme",
"cluster": "production",
"name": "Recent events",
"sql": "SELECT * FROM events LIMIT 10",
"database": "analytics",
"visibility": "private"
}name, sql, and database are required when creating. visibility is optional and defaults to private; use cluster to share the definition. The response is the flat query object shown inside the list above, including the generated id and authenticated user_id.
Save query: update
{
"organization": "acme",
"cluster": "production",
"id": "00000000-0000-4000-8000-000000000001",
"name": "Shared recent events",
"visibility": "cluster"
}Supply at least one of name, sql, database, or visibility. Omitted fields stay unchanged; null is rejected. The response is the complete updated query, with the same id and an updated updated_at.
Delete saved query
{
"organization": "acme",
"cluster": "production",
"id": "00000000-0000-4000-8000-000000000001"
}Response:
{
"id": "00000000-0000-4000-8000-000000000001",
"deleted": true
}This removes the saved definition only; the underlying data is unchanged. A failed deletion returns an MCP tool error, not a success confirmation.
Run query
{
"organization": "acme",
"cluster": "production",
"database": "analytics",
"sql": "SELECT count() AS events FROM events"
}Example response:
{
"meta": [{ "name": "events", "type": "UInt64" }],
"data": [{ "events": "42" }],
"rows": 1,
"statistics": { "elapsed": 0.001, "rows_read": 42, "bytes_read": 336 }
}run-query also forwards optional query hints from the API. All responses above are JSON encoded in the MCP text content, following the server's existing response format.
Call query-format with the complete SQL document:
{ "sql": "select 1; select 'hello'", "organization": "acme", "cluster": "production" }The MCP text content contains JSON with the complete result, including trailing newlines:
{ "formatted": "SELECT 1;\n\nSELECT 'hello';\n\n" }Organization, cluster, and database follow the usual tool defaults and overrides;
hosts configured with requireExplicitScope require organization and cluster.
The native formatter supports multiple statements and preserves literal values.
Inline INSERT ... VALUES data is copied verbatim without completeness or schema
validation. The API accepts up to 50 MiB of JSON, allows 100 MiB of formatted
output, and times formatting out after 30 seconds. Errors return no partial SQL.
The bundled HTTP and stdio transports accept MCP requests up to 51 MiB to allow
for the API payload plus the MCP envelope. Your MCP client or hosting gateway
may impose smaller message limits, including on responses.
Requires deployment of Platform PR #1468.
The tool calls POST /v1/query-format; it does not execute SQL or fall back to a query.
list-tables— List tables in the configured database.create-table— Create an empty table with an optional sorting key, using the storage configured on its database or cluster.describe-table— Inspect columns, row count, byte count, sorting key, database, and organization.update-table— Change a table's sorting key. Requires admin permission.delete-table— Delete a table after explicit confirmation. Requires admin permission.
sortingKey is an optional string on create-table. Omit it and the table picks a sorting key per part from the ingested data; set it to comma-separated SQL expressions in key order, such as region, ifNull(cityHash64(host, instanceId), 0). A bare name such as user.id is read as a path into the ingested JSON. describe-table reports the current key as a string, and update-table changes it: the new key applies to newly inserted parts and wins later merges, so existing parts are re-sorted in the background rather than rewritten by the call. A key must contain at least one expression; a table's sorting key cannot be removed once set.
Tables have no storage configuration of their own: a table uses its database's storage when the database configures one, and the cluster's default storage otherwise. Customer-owned S3 is configured with create-cluster.s3Storage or create-database.s3Storage.
list-logs— Read RawTree query and insert logs. Defaults to the last hour when no time window is provided.
Structured log filters include:
{
"statuses": ["error"],
"types": ["insert"],
"tables": ["events"],
"origins": ["api"],
"hints": "any",
"limit": 50
}list-api-keys— List API keys for a cluster, includingexpires_at(UTC timestamp or null for never).create-api-key— Create a key withadmin,read_write,write_only, orread_onlypermission. Optionalexpires_ataccepts a future RFC 3339 timestamp with a timezone, validated by the API. Omit it or pass null to never expire. Creation returns the expiration and one-time token. Expiration is fixed at creation; no API-key update tool is available.delete-api-key— Revoke a key after explicit confirmation.
list-databases— List databases in an organization and cluster.verify-database-s3-access— Verify a customer-owned S3 configuration before creating a database.create-database— Create a database with cluster-default storage or an optional customer-owned S3 configuration.delete-database— Delete a database and all its data after explicit confirmation.
create-database.s3Storage is optional. Omit it to use the cluster's default storage. When provided, tables in that database inherit its storage. Call verify-database-s3-access with the identical configuration before creation. Database listings expose s3_storage metadata with data and backups destinations, each containing bucket and path; credentials are never returned.
list-organizations— List organizations available to the authenticated user. Requires a user credential such as OAuth.list-organization-members— List accepted members of an organization with their user IDs and roles.add-organization-member— Send an organization member invitation after confirming the organization and email address. Membership starts after acceptance.update-organization-member— Change an accepted member's role toadminormemberafter explicit confirmation.remove-organization-member— Remove an accepted member and revoke organization access after explicit confirmation.
list-service-accounts— List an organization's service account IDs, names, descriptions, and stored roles.create-service-account— Create a service account with an immutable name, optional description, and optionalmember(default) oradminrole.
Both tools require an explicit organization name and an organization admin
using a user session or OAuth. API keys cannot manage service accounts. No
cluster or database is required. Names contain 1–128 lowercase letters or
digits separated by single hyphens; descriptions accept up to 2000 UTF-8 bytes
without null characters. Creation returns the account object; listing returns
an array of accounts. These tools create identities without credentials. API
key permissions are configured separately.
These tools manage organization issuers and cluster rules. Every call requires
an explicit organization; rule tools also require cluster. Use an
organization-admin user session or OAuth token with full_access. API keys and
workload tokens cannot manage this configuration.
| Tool | Input beyond scope | Response |
|---|---|---|
list-federation-issuers |
None | Array of issuer objects |
get-federation-issuer |
id |
Issuer object |
create-federation-issuer |
Issuer configuration | Issuer object |
update-federation-issuer |
id and complete issuer configuration |
Issuer object |
delete-federation-issuer |
id |
null |
list-federation-rules |
None | Array of rule objects |
get-federation-rule |
id |
Rule object |
create-federation-rule |
Rule configuration | Rule object |
update-federation-rule |
id and complete rule configuration |
Rule object |
delete-federation-rule |
id |
null |
Create an issuer through discovery:
{
"organization": "acme",
"name": "github",
"issuer_url": "https://token.actions.githubusercontent.com",
"jwks_source": "discovery"
}Alternatively, set jwks_source to explicit_url and supply jwks_url.
Saving an issuer does not fetch signing keys. Create a rule using the returned
issuer ID and an existing service account ID (illustrative UUIDs below):
{
"organization": "acme",
"cluster": "production",
"name": "ci-read",
"issuer_id": "11111111-1111-4111-8111-111111111111",
"allowed_audiences": ["rawtree"],
"claim_conditions": {"sub": "repo:acme/app:ref:refs/heads/main"},
"principal_type": "service_account",
"principal_id": "22222222-2222-4222-8222-222222222222",
"permission": "read_only",
"database": "default",
"enabled": true
}A user principal must be your own user ID. Use exactly one access mode:
permission (admin, read_only, or read_write) or a nonempty
database_roles array. Claim conditions are exact string matches, not
expressions or row filters. The database and enabled state must be explicit.
Updates replace the entire configuration (PUT). Add id to the complete
create input; omitted optional fields are cleared. Changes invalidate existing
workload access tokens. Delete referencing rules before deleting their issuer;
an issuer still in use returns 409.
Responses preserve the API's snake_case fields and nullable values. Issuers
include configuration, id, organization_id, created_at, and updated_at.
Rules also include cluster_id and the resolved principal details. Empty lists
are []; successful deletions are null.
These tools require the Platform federation-management OAuth routes. Deploy that backend change together with the hosted MCP dependency pin.
list-clusters— List dedicated clusters accessible in an organization. User-authenticated discovery spans enabled regions with a five-second limit per deployment. Partial outages return healthy clusters plusunavailable_regions(IDs and names); retry before concluding a missing cluster does not exist. Authentication failures and total regional outages remain errors.list-regions— List public and BYOC regions accessible to an organization.list-cluster-sizes— List current replica limits, supported per-replica sizes, and default vertical autoscaling bounds.verify-cluster-s3-access— Verify an optional customer-owned S3 configuration before cluster creation. The check temporarily writes, reads, and removes a probe object in both configured destinations.create-cluster— Provision a dedicated cluster after confirming its organization, replica count, minimum size, maximum size, autoscaling behavior, optional idle timeout, optional customer-owned default S3 configuration, and immutable cluster identity.get-cluster— Get one dedicated cluster and its current lifecycle status by ID.update-cluster— Change a dedicated cluster's idle timeout after confirming the organization, cluster ID, and new value. Use0to disable idling.pause-cluster— Pause a dedicated cluster after explicit confirmation. Its databases become unavailable until the cluster is resumed.resume-cluster— Resume a paused dedicated cluster after explicit confirmation.
Cluster tools are advertised to every MCP client. To choose a region, call list-regions with organization, then pass the same organization and regionId to list-cluster-sizes, verify-cluster-s3-access (when needed), and create-cluster. These requests use the selected deployment; creation also saves its region_id. Omitted regionId preserves the default backend. Explicit unavailable regions fail before creation, except the supported legacy public default. Region selection requires a user credential such as OAuth; API keys retain their embedded routing. Call list-cluster-sizes before create-cluster; creation starts at the selected minimum per-replica size and can vertically autoscale to the selected maximum. idleTimeoutMinutes accepts 0 to disable idling or a value from 15 through 43200; omit it during creation to use the server default.
create-cluster accepts optional s3StorageAccess containing externalId and resourceTag. The API generates omitted values independently as rawtree-<UUID> using secure randomness; MCP forwards optional values without generating identities. Both are persisted when the cluster is created and remain immutable. Cluster list/get responses expose s3_storage_access containing external_id and resource_tag; older clusters without identity may return null. resourceTag is the value of the rawtree.com/cluster AWS resource tag on eligible customer buckets and IAM roles.
s3Storage is optional. Omit it for RawTree-managed default storage. Custom storage requires data and backups destinations (each with bucket and optional path), plus roleArn. When specifying custom cluster storage, you must supply an explicit s3StorageAccess.externalId matching the IAM trust policy. Call verify-cluster-s3-access first, passing that same s3StorageAccess.externalId and the identical s3Storage, then reuse both for creation. Repeat verification after changing either.
Example create-cluster tool arguments (choose replica counts and sizes from list-cluster-sizes):
{
"organization": "acme",
"name": "production",
"replicas": 2,
"minimumSize": { "cpuCores": 2, "memoryGiB": 8 },
"maximumSize": { "cpuCores": 4, "memoryGiB": 16 },
"s3StorageAccess": {
"externalId": "rawtree-5b71c457-7d83-474b-9db9-6dc48a1ec042",
"resourceTag": "rawtree-dc1fb78e-341d-48f8-a183-9ecea12eb41d"
},
"s3Storage": {
"data": { "bucket": "acme-data", "path": "production/data" },
"backups": { "bucket": "acme-backups", "path": "production/backups" },
"roleArn": "arn:aws:iam::123456789012:role/RawTreeS3"
}
}For verify-database-s3-access and create-database, pass only the database name, scope, and s3Storage. The backend reuses the cluster's saved External ID and resource tag; neither tool accepts identity overrides. Tables inherit database storage, falling back to cluster storage when the database has none.
The old databaseS3Access block, flat dataBucket/backupBucket fields, and s3Storage.externalId are rejected. These tools target the API introduced by Platform PR #1482 and must be released with that server contract.
The RawTree API remains the authorization boundary: cluster access requires a user access token, and cluster creation, S3 verification, updates, pausing, and resuming additionally require organization-admin access.
list-apps— List the available apps and installation state for one cluster.install-app— Install an app on a cluster after confirming the organization, cluster name, and app ID.uninstall-app— Uninstall an app and disable its native endpoints after confirming the organization, cluster name, and app ID. Existing cluster data is not deleted.
App tools require a user credential. Organization members can list apps; installing and uninstalling require organization-admin access. Use the app IDs returned by list-apps.
list-connectors— List the managed connectors and destinations in a cluster.get-connector— Get one connector's status and sanitized configuration.create-connector— Create an active Kafka connector with one or more destinations.get-connector-metrics— Read connector and per-destination health, lag, buffers, delivery counters, errors, source lag, and HTTP latency counters.add-connector-destination— Preserve the existing routes and add another topic-to-table destination.set-connector-status— Pause or resume all destinations in a connector.
Connector tools use the same nested field names as the RawTree API. A minimal Kafka connector request looks like:
{
"organization": "acme",
"cluster": "production",
"name": "orders",
"type": "kafka",
"destinations": [
{
"topics": ["orders"],
"database": "default",
"table": "orders"
}
],
"settings": {
"bootstrap_servers": "kafka.example.com:9092",
"auto_offset_reset": "largest",
"tls": { "enabled": true },
"sasl": {
"enabled": true,
"mechanism": "PLAIN",
"username": "connector-user",
"password": "secret"
},
"batch": { "max_events": 1000, "timeout_secs": 1 }
}
}Creating connectors, adding destinations, and changing status require
organization-admin access. Credentials are encrypted by RawTree and omitted
from connector responses. get-connector-metrics returns cumulative counters;
take two samples and divide counter differences by elapsed time to calculate
event rates.
Programmatic hosted deployments can require explicit resource selection on
every applicable tool. Organization and cluster identify the resource boundary;
database remains an optional override and defaults to default when omitted.
This lets one OAuth-backed MCP connection switch between organizations,
clusters, and databases without encoding context in the MCP URL:
const server = createMcpServer(client, { requireExplicitScope: true });{
"organization": "acme",
"cluster": "production",
"database": "analytics",
"sql": "SELECT count() AS rows FROM events"
}{
"table": "events",
"data": [
{
"event": "signup",
"user_id": "user_123",
"source": "mcp"
}
]
}{
"statuses": ["error"],
"types": ["insert"],
"startTime": "2026-05-28T09:00:00.000Z",
"endTime": "2026-05-28T10:00:00.000Z",
"limit": 25
}- Install and build:
pnpm install
pnpm build- Use the local build from an MCP client:
claude mcp add rawtree -e RAWTREE_API_KEY=rt_xxxxxxxxx -- node /absolute/path/to/rawtree-mcp/dist/index.jsThe default suite uses mocked HTTP responses. To exercise all ten federation tools against a bootstrapped Platform with the OAuth management routes, set:
export RAWTREE_LIVE_API_URL=http://localhost:8206
export RAWTREE_LIVE_ORGANIZATION=your-local-organization
export RAWTREE_LIVE_CLUSTER=your-local-cluster
export RAWTREE_LIVE_USER_ID=your-user-uuid
# Supply RAWTREE_LIVE_OAUTH_TOKEN securely from a user OAuth grant with full_access.
pnpm test tests/live/federation.test.tsThe live test creates temporary issuers and rules, verifies replacement and
in-use deletion behavior, and removes its resources. It is skipped when
RAWTREE_LIVE_API_URL is unset. Never use production credentials for this test.
Run TypeScript in watch mode, then point a separate MCP client at the built server:
pnpm tsc --watch{
"mcpServers": {
"rawtree-dev": {
"command": "node",
"args": ["/absolute/path/to/rawtree-mcp/dist/index.js"],
"env": {
"RAWTREE_API_KEY": "rt_xxxxxxxxx"
}
}
}
}Restart the MCP client session after each rebuild.
The package root exposes the reusable MCP server layer. The Node HTTP transport
is available separately from @rawtree/mcp/http, so hosted adapters can use
the server factory without importing the local process entrypoint.
import { createMcpServer, RawTreeClient } from '@rawtree/mcp';
const client = new RawTreeClient({ apiKey: process.env.RAWTREE_API_KEY! });
const server = createMcpServer(client);Publishing is handled by the GitHub Actions Publish workflow.
Required repository secret:
NPM_TOKEN: npm automation token with permission to publish@rawtree/mcp.
Release flow:
- Update
package.jsonto the new version. - Push the change to
main. - Create and publish a GitHub release with a tag that matches the package version, such as
v0.2.0.
The workflow verifies that the release tag matches package.json, runs lint, tests, and build, then publishes with npm provenance:
npm publish --provenance --access publicBuild first:
pnpm buildStart the inspector:
RAWTREE_API_KEY=rt_xxxxxxxxx pnpm inspectorIn the Inspector UI, choose stdio:
- Command:
node - Args:
dist/index.js - Environment:
RAWTREE_API_KEY=rt_xxxxxxxxx