MCP (AI Agents)
Connect Claude and other AI agents to Servicebay via the Model Context Protocol
Servicebay ships a remote MCP server so AI agents — Claude, ChatGPT, Cursor, VS Code, and anything else that speaks the Model Context Protocol — can work with an organisation's jobs, customers, quotes, and invoices on a user's behalf.
The tools proxy the same REST API: identical validation, sequential IDs, form gates, and calendar sync. MCP never talks to Firestore directly.
Endpoint and transport
https://oauth.servicebay.io/mcpStreamable HTTP only, dual-era: current spec plus legacy initialize.
There is no SSE lane. Clients that only speak HTTP+SSE should go
through mcp-remote.
Three credential paths
- OAuth (DCR or CIMD) — the client self-registers or presents a
Client ID Metadata Document, then runs the
authorization-code + PKCE flow.
resourceis optional and defaults to this MCP URL. - Pre-registered confidential client — create one in OAuth Clients when the host rejects public DCR (notably Perplexity). Max 10 per org; they never expire.
- Static API key —
Authorization: Bearer sk_live_…when the key'sallowedUsesincludesmcp. A REST-only key is rejected (use_not_allowed). New keys default to the read bundle, 90-day expiry, and REST+MCP. See Authentication.
Admins only
Only active organisation admins can authorize an agent. The
connection is bound to a single organisation. Grant read scopes
unless you genuinely want the agent creating and deleting records. On
scope v2, write does not include delete.
Tools
Tools appear only when the credential holds the matching scope.
tools/list order is deterministic: whoami, organisation, list, get,
search, fetch, lookup, then writes, then delete.
| Tool | Scopes | Annotations | What it does |
|---|---|---|---|
whoami | Always listed | readOnlyHint | Connected org, credential type (oauth / api-key), effective permission matrix, missingForFullAccess |
get_organisation | organisation:read | readOnlyHint | Organisation profile |
list_resources | {entity}:read | readOnlyHint | List a collection. limit 1–100, cursor = REST startAfter. Filters match the REST query params. kind switches inventory vs asset categories. collection switches form templates / rules / submissions, or recurring presets |
get_resource | {entity}:read | readOnlyHint | Fetch one record by ID |
search | Any of customers / tickets / invoices / quotes / services / inventory / devices / messages :read | readOnlyHint | Email/phone looks up a customer; a bare integer tries job / invoice / quote numbers; otherwise fans out. Result ids look like sb:{entity}:{id} |
fetch | Matching {entity}:read | readOnlyHint | Load a search hit by sb:{entity}:{id}. Malformed ids (unknown entity, extra colons) are rejected |
lookup_customer | customers:read | readOnlyHint | Lookup by email or phone |
create_resource | {entity}:write | not destructive, not idempotent | Create. Sequential IDs (jobs, invoices, quotes) are allocated server-side — never supply them |
update_organisation | organisation:write | destructiveHint, idempotentHint | PUT of the provided fields |
update_resource | {entity}:write | destructiveHint, idempotentHint | PUT by ID |
delete_resource | {entity}:delete | destructiveHint, idempotentHint | Delete by ID. confirm must be true. Cascades are described on the tool (jobs delete their calendar event; payments re-open the invoice) |
Entity keys: tickets (jobs), customers, devices, services,
inventory, quotes, invoices, payments, calendar-events,
stations, tax-rates, ticket-statuses, messages,
recurring-schedules, categories, forms, members.
get_organisation is listed only when the credential has
organisation:read. fetch rejects ids that are not
sb:{entity}:{id} with a known entity key.
search / fetch citations resolve to
https://developer.servicebay.io/r/{org}/{entity}/{id} — a
login-gated summary that requires Pro admin sign-in. Unknown
entities, missing records, and visitors who are not a Pro admin of
that org get 404.
devices needs customerId. messages needs ticketId. forms and
members are read-only via the API, so write/delete tools never
offer those entities.
Step-up
A 403 tool result includes:
{
"_meta": {
"mcp/www_authenticate": [
"Bearer error=\"insufficient_scope\", scope=\"tickets:write\", resource_metadata=\"https://oauth.servicebay.io/.well-known/oauth-protected-resource/mcp\""
]
}
}A 401 uses error="invalid_token". Clients that understand the MCP
WWW-Authenticate convention can re-run consent for the listed scopes.
Origin policy
Origin is checked before the handler runs:
| Origin | Result |
|---|---|
| Missing (native / CLI clients) | Allowed |
https://… | Allowed; value is reflected in CORS |
http://localhost, 127.0.0.1, ::1 | Allowed; reflected |
null, file:, non-local http | 403 Forbidden |
Optional deny list: Worker var MCP_BLOCKED_ORIGINS (hosts or full
origins).
Connecting from Claude Code
claude mcp add --transport http servicebay https://oauth.servicebay.io/mcpThen run /mcp inside Claude Code to trigger sign-in.
Client guides
Compatibility matrix
Auth path and transport for every supported client.
Troubleshooting
401, 403, invalid_target, PKCE, Origin, and legacy SSE.
Claude.ai
Custom connector in the Claude web app.
Claude Desktop
Custom connector or mcp-remote — not a url field.
Claude Code
claude mcp add --transport http.
ChatGPT
Developer mode; search + fetch for Deep Research.
OpenAI Responses / Agents
Hosted MCP with a static Bearer key.
Anthropic Messages API
mcp_servers + authorization_token.
Cursor
.cursor/mcp.json and the install deeplink.
VS Code / Copilot
.vscode/mcp.json, type http.
Gemini CLI
httpUrl — url is SSE-only and will fail.
Google ADK
StreamableHTTPConnectionParams.
Windsurf
serverUrl; a copied url field is ignored.
Zed
context_servers.url; headers are not env-expanded.
JetBrains AI
mcp-remote + Bearer.
Perplexity
Pre-registered confidential client — public DCR is blocked.
Copilot Studio
Custom connector + generative orchestration.
n8n
MCP Client node with Bearer or OAuth.
Managing access
Connected agents appear under
Connected Apps.
Revoking disconnects the agent within seconds. If the approving admin
leaves or is deactivated, subsequent calls return 403 and the grant is
revoked automatically.
Discovery:
https://oauth.servicebay.io/.well-known/oauth-authorization-server