MCP Server (Beta)
Beta status. The 4-tool surface, OAuth 2.1 + PKCE auth, MCP SQL Policy enforcement, and per-org admin
controls described below are stable. One piece is still maturing: server-to-client streaming for
ask_grovestreams partial tokens — clients currently see the full response when the call
completes, which is fully MCP-spec-compliant, just less interactive. Tool schemas, response shapes, and
admin settings are not expected to change.
GroveStreams exposes a Model Context Protocol server so AI agents — Claude Code, Cursor, Claude Desktop, ChatGPT Desktop, and any MCP-compatible client — can talk to GroveStreams natively. Schema design, GS SQL authoring, dashboard help, troubleshooting, and data ingestion all flow through one standard interface.
Endpoint: /api/mcp?org=<org_uid>
Transport: Streamable HTTP (MCP spec 2025-11-25)
Auth: OAuth 2.1 with PKCE + Dynamic Client Registration (RFC 7591)
Discovery: /.well-known/oauth-protected-resource +
/.well-known/oauth-authorization-server (host-root, per RFC 8414 §3.1 / RFC 9728 §3.1)
The ?org=<org_uid> query parameter on the MCP URL binds the session to a GroveStreams
organization. Get the org UID from the GroveStreams web UI URL when you have the org open, or from the
org_users system table. One MCP entry per org; add multiple entries if you work in multiple orgs.
?org= at connect time and cannot switch mid-session. This is deliberate:
- Prompt-injection blast radius. If hostile content reaches the agent's context, it still can't reach a different org through the same session — the binding is server-side, not agent-chosen.
- Audit clarity. Every call in a session attributes to one org. No per-call ambiguity.
- Mental-model drift. LLMs are unreliable at tracking "which org am I in?" across long conversations. Pinning at connect time removes the failure mode entirely.
list_orgs tool is available in-session as a discovery aid (look up another org's
UID) but does not switch the session.
If you connect without ?org=, the server returns ERR_INVALID_PARAMS at
initialize with the user's accessible org UIDs embedded in the error message — so a
stuck agent can show the user which UID to copy onto the URL.
ODBC vs. MCP. Use the GS ODBC driver when your application reads or writes data at runtime. Use MCP when your AI agent needs to design schemas, author queries, build dashboards, or troubleshoot the platform. ODBC for your app, MCP for your AI.
Tool Surface
Five tools, intentionally small. New GroveStreams capability flows through ask_grovestreams without server-side churn.
| Tool | Read-only | Use it for |
describe_org | yes | Bootstrap. Returns org/user identity, capability flags reflecting your org's MCP SQL Policy and tool toggles, plus an orientation blurb the agent reads before its first real call. Always available when MCP is enabled. |
list_orgs | yes | Returns the GroveStreams orgs the authenticated user belongs to: org_uid, org_name, whether the user holds the MCP Access capability there, and which one is the current session's org. Discovery aid for looking up another org's UID to copy onto a new MCP URL. Does not switch the session (binding is sticky — see safety note above). Always available when MCP is enabled. |
run_gsql | depends on policy | Direct GS SQL execution. The agent passes a single statement; the server executes through the standard pipeline and returns {columns, rows, row_count, elapsed_ms}. Subject to the org's MCP SQL Policy (see below). |
ask_grovestreams | configurable per call | Delegate a natural-language request to the GroveStreams AI Assistant — schema design, query authoring, FK derivations, dashboards, troubleshooting. Multi-turn within an MCP session. Bills LLM tokens under the org's MCP usage line item. |
send_samples | no (additive) | Ingest sample data via the standard Temporal Wire code path. No LLM in the loop, so high-volume writes don't bill tokens. |
Switching orgs. Each MCP session is bound to one org via the ?org= URL parameter.
To switch, call list_orgs to see what's available, then update the MCP server URL in
your client (or add a second MCP entry pointing at the other org). Sessions stay clean and
single-tenant; no mid-session re-binding.
Resources
MCP Resources let the agent read GroveStreams documents directly without burning LLM tokens to ground itself.
| URI template | Content |
gs://help/{topic} | Curated GS help docs (gsql, ddl, teq, llm_reference, llm_ddl_reference, forecasting, ai_assistant, api, connectors, tables). |
gs://org/{orgUid}/template/{templateId} | Component template definition (streams, types, FK relationships) as JSON. |
gs://org/{orgUid}/dashboard/{dashboardUid} | Dashboard definition (widgets, queries) as JSON. |
gs://org/{orgUid}/saved-query/{queryUid} | A saved GS SQL query — text plus name. Useful for the agent to learn your team's query patterns. |
Org-scoped URIs are validated against the requesting MCP session's org so a token authorized for org A cannot read org B by URI smuggling.
Authentication
GroveStreams uses standard OAuth 2.1 authorization-code flow with PKCE (RFC 7636). MCP clients that support Dynamic Client Registration (RFC 7591) can register themselves at runtime; static client IDs also work.
Endpoints (relative to your GroveStreams base URL):
| Endpoint | Purpose |
/.well-known/oauth-protected-resource | RFC 9728 — points the client at the authorization server. Served at host root so spec-compliant MCP clients find it on the first probe. |
/.well-known/oauth-authorization-server | RFC 8414 — advertises supported grants, PKCE methods, scopes, the registration endpoint. Issuer is the host root; all OAuth endpoints sit under /api/ on that host. |
/api/oauth/register | RFC 7591 Dynamic Client Registration — clients self-register here. |
/api/oauth_login | Authorization endpoint. GET with OAuth params redirects the browser to the GroveStreams sign-in page; the page POSTs back here as JSON to mint the authorization code. |
/api/oauth_access_token | Token endpoint — exchanges the code (plus PKCE verifier) for a bearer access token. |
Unauthenticated requests to /api/mcp respond with 401 Unauthorized and a
WWW-Authenticate: Bearer ... resource_metadata="<url>" challenge per
RFC 9728 §5.3, so clients that follow the spec can bootstrap discovery directly from the challenge
without relying on host-root probes.
The bearer token resolves to a GroveStreams user; that user's RBAC continues to apply on top of the MCP-channel SQL Policy (see below). No parallel identity model — MCP is just another way to authenticate into the same platform.
Per-Org Settings
MCP requires two things to be in place before a user can connect:
- Org-level: Enable MCP. Owners and admins flip this on the AI Assistant & MCP tab in Organization Settings (see below).
- User-level:
MCP AccessRBAC capability. Edit the user's group under Manage Users and Groups and check the MCP Access capability on the Capabilities tab. The Administrators group has this capability by default. Without it,initializerejects the session even if the bearer token is valid.
Once those are set, owners and admins control the rest of the MCP surface from the AI Assistant & MCP tab in Organization Settings. The MCP Access section contains:
- Enable MCP — master switch. When off, MCP clients are rejected at
/api/mcpregardless of authentication. - Tools exposed — checkboxes for
run_gsql,ask_grovestreams,send_samples. Disablingask_grovestreamsturns MCP into a SQL-only surface with no LLM cost on the GroveStreams side.describe_organdlist_orgsare always on. - Agent profile for ask_grovestreams — pick which configured Agent Profile (model, system prompt, tool subset) the GroveStreams AI Assistant uses when invoked through MCP. Empty selection falls back to the org's default profile.
- MCP SQL Policy — see next section.
- Limits — token budget per session (loop-guard), max concurrent sessions, idle session timeout, per-call query timeout.
Admins can also flip toggles via DDL without opening the UI — useful for ops:
ALTER ORG SET mcpEnabled = TRUE; ALTER ORG SET mcpToolRunGsql = TRUE; ALTER ORG SET mcpToolAskGrovestreams = TRUE; ALTER ORG SET mcpSqlPolicyReadOnly = TRUE; ALTER ORG SET mcpSqlPolicyDdl = FALSE; |
MCP SQL Policy
All SQL routed through MCP — whether submitted to run_gsql directly or executed by the GS
Assistant inside ask_grovestreams — passes through the org's MCP SQL Policy. The
policy is a small set of flags:
| Flag | Allows |
allowReadOnly | SELECT, WITH (CTE) |
allowDdl | CREATE / ALTER / DROP / PLACE / RELATIONSHIP |
allowDml | INSERT / UPDATE / DELETE |
allowSystemTables | STREAM, ACTIVEEVENT, USERNOTIFICATION, SYSTEMNOTIFICATION, JOBNOTIFICATION (gates the target, not the operation) |
The MCP SQL Policy is a fence — it can only tighten the org-wide SQL controls and existing user RBAC, never loosen them. The default is Read-Only, which is the right starting point for any new MCP integration. Vibe-coding sandbox orgs typically open it to allow DDL.
When ask_grovestreams calls into the AI Assistant, the assistant's SQL still runs through the
same fence — the policy travels with the delegation. An MCP client cannot smuggle DDL through the assistant
that run_gsql would have rejected.
Billing
MCP usage is tracked as a separate line item on your billing page so you can see exactly what AI agents
are spending. run_gsql and send_samples bill the same as any other SQL execution
or sample write. ask_grovestreams bills LLM tokens under your existing AI Assistant rate plan,
tagged with origin mcp so it is reported separately from in-app chat.
A per-session token budget acts as a runaway guard. When the cumulative LLM tokens consumed in a single
MCP session exceed the configured limit (default 1,000,000 tokens), the session terminates with an
over_budget error. The agent can open a fresh session to continue.
Client Setup
How the OAuth handshake works
The client configs below are deliberately minimal — just the GroveStreams MCP URL. No credentials, API keys, client IDs, or tokens are placed in the config file. All of that is handled by the standard OAuth 2.1 + PKCE flow on first connect:
- You add the GroveStreams MCP URL (including
?org=<org_uid>) to your client (see snippets below). - On its first request to
/api/mcp, the client receives a401 Unauthorizedwith aWWW-Authenticateheader pointing at the discovery documents at/.well-known/oauth-protected-resourceand/.well-known/oauth-authorization-server(host-root, per RFC 8414 / RFC 9728). - The client fetches discovery, then registers itself dynamically (RFC 7591 DCR) or uses a configured
client_idto obtain one. - The client opens a browser tab to the GroveStreams authorization endpoint. You log in to GroveStreams using your own email and password (2FA if enabled) and explicitly grant the MCP client access. The PKCE code challenge keeps the exchange tamper-proof end to end.
- The browser redirects back to the client with an authorization code. The client exchanges the code
(plus the PKCE verifier) for a bearer access token at
/api/oauth_access_token. - The client stores the bearer token locally (Claude Desktop and Cursor use the OS keychain;
Claude Code stores it under
~/.claude/). Subsequent MCP requests includeAuthorization: Bearer <token>. - The MCP server resolves the bearer to your GroveStreams user UID, and every tool call —
describe_org,list_orgs,run_gsql,ask_grovestreams,send_samples— runs under your identity. Your existing RBAC (folder permissions, group capabilities) applies exactly as it does in the web UI. - Tokens refresh automatically via the standard refresh-token grant. To revoke access, sign out of the client (which deletes the local token) or rotate your GroveStreams password.
So if Alice and Bob each install Claude Desktop and connect it to the same GroveStreams MCP URL, each authenticates as themselves. Their MCP sessions see exactly the data and tools their GroveStreams accounts permit — no shared service account, no API-key sharing.
Claude Desktop
Add an entry to your Claude Desktop config (~/Library/Application Support/Claude/claude_desktop_config.json
on macOS, %APPDATA%\Claude\claude_desktop_config.json on Windows). On first connect,
Claude Desktop pops up a browser window to GroveStreams — sign in there with the user account
that owns or belongs to the org you want to access.
{
"mcpServers": {
"grovestreams": {
"url": "https://<your-grovestreams-host>/api/mcp?org=<your-org-uid>"
}
}
} |
Claude Code
Register the MCP server from your shell:
claude mcp add --transport http grovestreams \
'https://<your-grovestreams-host>/api/mcp?org=<your-org-uid>' |
Quote the URL so your shell doesn't interpret the ? and & characters.
Then complete the OAuth handshake inside Claude Code:
- Start Claude Code:
claude - Run
/mcpto open the MCP servers panel. - Select the grovestreams entry and choose Authenticate. Claude Code opens your default browser to the GroveStreams sign-in page.
- Sign in with your GroveStreams email and password (plus 2FA if enabled). The browser tab will show Authentication Successful and you can close it.
- Back in Claude Code,
/mcpwill now show the server as connected / authenticated, and the five GroveStreams tools (describe_org,list_orgs,run_gsql,ask_grovestreams,send_samples) are available to the agent.
Try it: ask Claude something like
"Call describe_org from grovestreams and summarize the org."
If the connection fails with MCP is disabled for org <uid> or
User lacks the MCP Access capability, see Per-Org Settings —
both gates must be on.
Cursor
In Cursor settings, add a new MCP server with the URL
https://<your-grovestreams-host>/api/mcp?org=<your-org-uid>. Cursor opens its
built-in browser to the GroveStreams login page when you first invoke a GroveStreams tool.
Custom client
Any MCP-compatible client that follows MCP spec 2025-11-25 (Streamable HTTP, OAuth 2.1 with PKCE) will
work. On first connect, the client should fetch the discovery documents at
/.well-known/oauth-protected-resource and
/.well-known/oauth-authorization-server (or follow the resource_metadata
hint in the WWW-Authenticate challenge on its first 401 response), register itself via DCR
(or use a configured client_id), drive the user through the authorization-code flow with
a PKCE S256 challenge, and then send JSON-RPC 2.0 requests to
/api/mcp?org=<org_uid> with Authorization: Bearer <token> on
every request.
If you are scripting against MCP without an interactive browser available — CI jobs, server-side tooling — you can run the authorization-code flow once interactively to obtain a long-lived refresh token, then have the script exchange the refresh token for fresh access tokens as needed. The script still runs under the identity of the user who completed the interactive step.
Loop Guard
Two complementary mechanisms keep MCP convos from spiraling:
- Origin tag. Convos created over MCP carry an
origin=mcptag. The GS Assistant's tool registry refuses any tool that would call back into MCP from such a convo. - Per-session token budget. Cumulative LLM tokens per MCP session are capped (default 1,000,000). On exceed, the session terminates with
over_budget; the client opens a new session.
This pair leaves precision-mode (run_gsql) unaffected — those calls don't bill LLM tokens and
don't traverse the recursion path at all.
Related: AI Assistant | Scheduled Agents | DDL — AI Agents | GS SQL Overview
