MCP server
Connect an MCP client and query your Steerd data in natural language.
The Public API also speaks the Model Context Protocol (MCP). Point an MCP-capable
client (Claude Desktop, IDE assistants, and others) at the endpoint and it can read and
update your Steerd data through tools, authenticated by the same strd_ API key as REST
and GraphQL. Every tool is scoped to your key's team and permissions.
Endpoint & authentication
The endpoint is POST /api/public/v1/mcp over streamable HTTP, on the app origin for your
environment:
| Environment | Base URL |
|---|---|
| Local development | http://localhost:3100 |
| Staging | https://app.staging.steerd.io |
| Production | https://app.steerd.io |
So a local client connects to http://localhost:3100/api/public/v1/mcp and production is
https://app.steerd.io/api/public/v1/mcp. A strd_ key authenticates only against the
environment it was created in.
Requests require a bearer token:
Authorization: Bearer strd_<key>The token resolves your team and the tools you may call. Create a key in the Steerd app under Settings → API keys, granting only the scopes the integration needs (see authentication). A key handed to an assistant can read everything its scopes allow, so prefer a least-privilege key.
Connect a client
Use the base URL for your environment (see the table above). The examples below use
production; for local development swap in http://localhost:3100, and for staging
https://app.staging.steerd.io.
Clients that support remote MCP servers over HTTP can use the URL and header directly:
{
"mcpServers": {
"steerd": {
"url": "https://app.steerd.io/api/public/v1/mcp",
"headers": { "Authorization": "Bearer strd_YOUR_KEY" }
}
}
}For a client that only speaks stdio, bridge to the remote server with mcp-remote:
{
"mcpServers": {
"steerd": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"https://app.steerd.io/api/public/v1/mcp",
"--header",
"Authorization: Bearer strd_YOUR_KEY"
]
}
}
}Tools
Tools mirror the REST and GraphQL resources and return the same public projections (no
team_id, and so on). Names follow a verb_object convention.
- List and get:
list_<resource>and, where available,get_<resource>for organizations, contacts, employees, projects, tasks, activities, assignments, and cvs. Tasks, activities, and assignments are list only. - Documents and attachments:
list_documents/get_documentandlist_attachments(by parent entity). Metadata only, no file bytes. - Search: a global
searchthat matches across the resource types your key can read. - Time entries:
list_time_entries,get_time_entry, andget_time_summary. These honor your role: a time tool reflects exactly what its key's creator may see and do in the app, and refuses when that access is revoked. - Create and update:
create_<resource>andupdate_<resource>for organizations, contacts, employees, projects, tasks, and cvs;create_activityandcreate_assignment(create only); andcreate_time_entry/update_time_entry. Plusarchive_projectandrestore_project. Write tools require a<resource>:writescope and validate exactly as REST does.
Each tool mirrors the same team-scoping, validation, and public projections as the equivalent REST request.
List results are paged
List tools return at most 25 items by default and 50 at most. To page, pass the
nextCursor from a result back as the cursor argument, the same cursor connection REST
and GraphQL use.
What is not a tool
- No delete. Destructive deletes are never exposed to an assistant. To set a project
aside, use
archive_project(andrestore_projectto bring it back); for permanent deletion, use the REST or GraphQL API, or the app. - Writes are at-least-once. A retried
create_call may create a duplicate; there is no idempotency key on MCP writes yet. - Documents and attachments are metadata only. Their bytes are binary and are not
returned through MCP.
get_documentreturns the record; fetch the bytes from the REST download endpoint with the same key. - No uploads. File uploads are multipart and go through REST.
A note on trust
An assistant reads your CRM data through these tools, and some of that data is free text that other people wrote (a project note, an incoming request, a contact's details). Treat tool results as data, not instructions. Grant write scopes only when the integration needs them, and keep the key least-privilege, so a surprising or manipulated result can only ever do what the key's scopes allow.
Errors
Tool errors carry a stable machine code (forbidden, not_found, bad_request,
limit_reached, internal) and a safe message. A limit_reached error includes which
plan limit was hit, so an assistant can suggest an upgrade rather than retry. See
errors for the shared code list.