Connect with MCP
Give an AI assistant read access to Sensorhub through the Model Context Protocol. The assistant sees only what your account can see.
Authentication
There are two ways to authenticate:
- OAuth (Claude.ai, Claude Desktop, ChatGPT). Add Sensorhub as a connector, sign in, and pick one project or all projects to share. No key is needed.
- API key (Claude Code, Cursor, scripts). Create a key in the MCP section of a project, or an all-projects key on the workspace MCP page in Sensorhub, and send
Authorization: Bearer sk_sh_....
A project key or single-project connection covers one project. An all-projects key or connection is workspace-wide: it reaches every project in the workspace, including new ones, and only members with access to every project can create one. API access is not available on Trial or Basic plans. A key follows the current access of the person who created it: if their access to a project is removed, so is the key's.
Some collections and reports need Perception enabled on the project. They are marked Requires Perception in this reference.
The older /api/v1/products/... endpoints are deprecated and stop working on 10 April 2027. Use /api/v1/query and the named reads instead.
Set up your client
Claude Code
Create an API key in the MCP section of a project (or an all-projects key in Workspace → MCP), then run:
claude mcp add --transport http sensorhub https://app.sensorhub.ai/mcp \
--header "Authorization: Bearer sk_sh_YOUR_KEY"Claude Desktop
- Go to Settings > Connectors
- Click "Add custom connector"
- Name: Sensorhub
- URL: https://app.sensorhub.ai/mcp
- Click "Add"
You'll be prompted to sign in, choose the project to connect, and authorize access. The connection uses OAuth, so no API key is needed. If we update the tools later, refresh the list (your permissions are kept): Customize > Connectors > Sensorhub > three-dot menu > Refresh tools list.
Claude.ai
- Open Settings > Connectors
- Click "Add custom connector"
- Name: Sensorhub
- URL: https://app.sensorhub.ai/mcp
- Sign in, choose the project and authorize access
The connection uses OAuth, so no API key is needed.
ChatGPT
- Turn on Developer mode in ChatGPT settings. It is usually under Apps & Connectors > Advanced settings; OpenAI moves this toggle between releases. On Business, Enterprise and Edu plans a workspace admin has to allow it.
- Create a new connector (shown as Create or Create MCP App once Developer mode is on)
- Name: Sensorhub
- MCP server URL: https://app.sensorhub.ai/mcp
- Authentication: OAuth
- Sign in with Sensorhub when prompted and choose the project
Developer mode is required because Sensorhub offers its own read tools (discover, describe, query, read) rather than ChatGPT's search and fetch pair. The connection uses OAuth, so no API key is needed.
Cursor
- Create an API key in the MCP section of a project (or an all-projects key in Workspace → MCP)
- Add this server to your Cursor MCP configuration (mcp.json):
{
"mcpServers": {
"sensorhub": {
"url": "https://app.sensorhub.ai/mcp",
"headers": {
"Authorization": "Bearer sk_sh_YOUR_KEY"
}
}
}
}Other clients that support remote MCP servers use the same URL and Authorization header.
cURL
Check that a key works by sending an MCP initialize request:
curl -X POST https://app.sensorhub.ai/mcp \
-H "Authorization: Bearer sk_sh_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"test","version":"1.0.0"}},"id":1}'Tools
The server exposes a small, fixed set of tools. Assistants find what they need with discover and describe instead of loading every schema up front.
discover({ "search": "workspaces" })
describe({ "name": "workspaces" })
query({"sql":"SELECT id, name, plan FROM workspaces ORDER BY id ASC LIMIT 25"})discover
Search authorized capabilities by words in names, descriptions and readable field names. Any matching word qualifies; more word matches rank first. No match returns the authorized catalog with a note. Returns compact summaries in pages; limit is clamped to 50. Describe a name to load its schema.
| Parameter | Type | Description |
|---|---|---|
search | string | max length 200 |
offset | integer | min 0 · max 100000 · default 0 |
skillOffset | integer | min 0 · max 100000 · default 0 |
limit | integer | min 1 · max 9007199254740991 · default 20 |
describe
Get a capability schema/examples, skill, errors, or the SQL dialect. Supply name, or up to 10 names to get {results:[...]} in request order where each unavailable name has its own {name, error}; kind=dialect needs no name. Hidden definitions remain unavailable.
| Parameter | Type | Description |
|---|---|---|
name | string | max length 100 |
names | string[] | max 10 items |
kind | "capability" | "skill" | "errors" | "dialect" | default "capability" |
query
Run a bounded SELECT against one collection. Bind values as $1, $2, etc., for example {"sql":"SELECT id FROM records WHERE id = $1 LIMIT 10","params":["value"]}. Describe it for supported filters, ranked search, aggregates and GIS operations. Use cursor=null to begin supported keyset pagination and pass meta.nextCursor to continue. Set preflight=true to validate access and adapter support without reading rows; estimate=false skips the optional app estimate. format=columns (default) returns {columns, rows: value[][], meta}; rows returns row objects; toon returns TOON text. queries runs up to 5 entries in order and returns {results:[...]} with a per-entry {error} on failure. No writes, joins or arbitrary functions.
| Parameter | Type | Description |
|---|---|---|
sql | string | One SELECT. Bind values with $1, $2, etc.; for example WHERE id = $1 with params=["value"]. max length 4096 |
preflight | boolean | |
estimate | boolean | |
cursor | string | null | |
params | string | number | boolean | null[] | Values for $1, $2, etc. in SQL order; for example ["value"]. Question-mark and named placeholders are unsupported. max 100 items |
queries | object[] | max 5 items |
format | "columns" | "rows" | "toon" | default "columns" |
read
Execute an authorized named computed read. Call describe for its input schema; collection filters belong in query.
| Parameter | Type | Description |
|---|---|---|
name required | string | max length 100 |
input | object | default {} |
Writing good queries
- Ask for only the fields you need and add a
LIMIT. - Filter on indexed fields for large collections; the field reference marks them.
- Use a report from Reports and reads when you need the exact numbers shown in the app.
- Errors explain how to fix the query, including suggested field names.