Skysay

Conecta asistentes de IA a tu espacio de trabajo de Skysay para inspeccionar y gestionar agentes de voz, números de teléfono, llamadas, SMS y campañas mediante OAuth o claves de API con permisos limitados.

Servidor MCP alojado

npx add-mcp 'https://api.skysay.ai/mcp'

Se instala en Claude Code, Codex, Cursor y más

Documentación

MCP overview

Connect an assistant with OAuth or an API key, inspect tool schemas, and control workspace permissions.

Skysay speaks MCP (Model Context Protocol). An MCP-capable client points at the hosted endpoint, connects with OAuth or a scoped API key, and the Skysay tools appear in tools/list — ready to call. This is the universal path; the OpenClaw connector and the REST API are convenience wrappers around the same tools.

The endpoint

POST https://api.skysay.ai/mcp
Authorization: Bearer sky_your_scoped_key

Standard JSON-RPC: tools/list to discover, tools/call to invoke.

Connect with OAuth

For an assistant that supports remote MCP authorization, enter https://api.skysay.ai/mcp as the server URL and choose OAuth. Sign into Skysay, check the client and workspace shown on the consent screen, and approve the permissions you want to grant. Adding a custom connection does not require Skysay to be listed in that assistant's public directory.

The initial permissions read workspace, agent, call metadata, coverage, number and onboarding information, and evaluate policy. Reading conversation content or performing supported writes requires additional consent. Check each consent screen: some permissions allow actions that contact people or spend your Skysay balance. A connection cannot access a different workspace by passing its organization ID.

Manage and revoke connections from Connected apps on the API keys page in the Skysay workspace. Revocation invalidates the connection's tokens. Existing API keys continue to work independently and must be revoked separately.

OAuth client configuration

SettingValue
MCP resourcehttps://api.skysay.ai/mcp
Authorization serverhttps://api.skysay.ai
Resource discoveryhttps://api.skysay.ai/.well-known/oauth-protected-resource/mcp
Authorization server discoveryhttps://api.skysay.ai/.well-known/oauth-authorization-server
Authorization endpointhttps://api.skysay.ai/oauth/authorize
Token endpointhttps://api.skysay.ai/oauth/token
Dynamic registration endpointhttps://api.skysay.ai/oauth/register
Revocation endpointhttps://api.skysay.ai/oauth/revoke

Use the authorization-code flow with PKCE S256 and public-client token authentication (none); no client secret is required. Both Client ID Metadata Documents (CIMD) and dynamic client registration (DCR) are supported. CIMD documents may advertise supported authentication methods as a list containing none, even when their legacy preference is a different method. Redirect URIs must match the client's metadata or registration. Authorization responses include the issuer (iss).

Access tokens expire after one hour. Refresh tokens rotate, expire after 30 days without renewal, and have a maximum family lifetime of 90 days. Reconnect when the client asks you to sign in again. An unauthenticated MCP request returns a 401 discovery challenge; a supported tool that needs more consent returns a 403 scope challenge. Only the scopes advertised by the authorization server can be granted through OAuth; the API-key catalogue is broader. Skysay does not currently provide OpenID Connect UserInfo or the openid and email scopes for ChatGPT Enterprise workspace-domain restrictions.

Read-only connector for Claude

Use https://api.skysay.ai/mcp/read-only for a connection that can inspect workspace data without generating audio or performing writes. It exposes exactly five tools:

  • account_overview: a short workspace summary — wallet, usage totals, and for each collection its total with the newest few items.
  • list_agents: list your agents, one page at a time.
  • get_agent: read one agent's configuration.
  • list_numbers: list your phone numbers, one page at a time.
  • list_calls: inspect call metadata one page at a time, without recordings or transcripts.

Each of the four collection tools answers with at most 25,000 characters, so a large workspace is read in pages rather than in one oversized answer; see Bounded results and paging.

All other tool calls are refused at this endpoint, even with a broad API key. OAuth consent accepts only account:read, agents:read, calls:read, and numbers:read. Its discovery document is https://api.skysay.ai/.well-known/oauth-protected-resource/mcp/read-only, and its OAuth resource is https://api.skysay.ai/mcp/read-only. Tokens for this resource cannot be used at /mcp, and tokens for /mcp cannot be used at the read-only endpoint. Both connections use the same sign-in, PKCE, consent, refresh and revocation flow described above.

Try “Show my Skysay workspace overview,” “List my Skysay agents,” or “Show my Skysay phone numbers.” A new workspace may return empty lists; the connector will not create sample data or provision numbers to fill them.

Connect from the workspace

You do not have to assemble the configuration by hand. The API keys page in the workspace carries a Connect an AI assistant card: create a key with only the scopes the assistant should have, then copy the ready-made server entry it shows and paste it into the assistant's MCP settings, replacing the placeholder with that key.

{
  "url": "https://api.skysay.ai/mcp",
  "headers": {
    "Authorization": "Bearer sky_YOUR_KEY"
  }
}

Use this canonical API URL for every new connection. The assistant can do exactly what the key allows and nothing more, and revoking the key disconnects it.

Results, errors and tool metadata

Tool calls return a text content block containing JSON and the same data in result.structuredContent. A tool that answers with a plain list uses { "items": [...] } in structured content; the paged collection tools (list_agents, list_calls, list_numbers) answer with a named array and a pagination object instead. A refused tool returns result.isError: true and structuredContent.error with type and message. Unexpected failures use internal_error and a request_id to quote to support. An unknown tool, or a params, name or arguments that is not the right shape, remains a JSON-RPC error (-32602); anything that goes wrong once the tool runs is a refused tool result.

Every tool has a title and read-only or destructive hints. Required scopes are in _meta["ai.skysay/required_scopes"]; alternatives are in _meta["ai.skysay/alternative_required_scopes"]. The catalogue lists all tools, even when your credential lacks their scopes. A call whose credential lacks a tool's scopes fails closed with a refused tool result naming the missing scopes.

organization_id is optional on all tools and defaults to your credential's workspace. A different workspace is refused. Streamable HTTP negotiates 2025-11-25, 2025-06-18 or 2025-03-26 through initialize. Notifications receive 202 with no body; ping is supported; GET and HEAD return 405, with no SSE stream.

The endpoint also speaks MCP 2026-07-28, which has no handshake. A client using it sends MCP-Protocol-Version: 2026-07-28 and Mcp-Method headers on every request (plus Mcp-Name for tools/call and resources/read), with the protocol version and client capabilities in params._meta. server/discover lists the supported revision; every result carries resultType, and list and read results carry ttlMs and cacheScope. A request that revision does not define, such as ping, returns 404 with -32601; a header that disagrees with the body returns 400 with -32020, and an unsupported version returns 400 with -32022 and the supported list. Requests without that header keep the handshake-era behavior only when their body does not explicitly claim a modern version. A modern body with a missing or legacy version header is rejected before any tool runs; removing the header cannot bypass modern validation.

Non-browser clients may omit Origin. If a client supplies it, either MCP endpoint accepts only the origins of the deployment's configured MCP resource URL and OAuth consent URL; an invalid, duplicate or unapproved origin returns HTTP 403 before the request body is read. The hosted defaults are https://api.skysay.ai and https://skysay.ai. Self-hosted deployments derive these origins from SKYSAY_MCP_RESOURCE_URL and SKYSAY_OAUTH_CONSENT_URL, never from a request's Host or forwarded headers.

These refusals follow the official Streamable HTTP transport specification: supplied origins are validated and contradictory version headers/body claims are refused before tools run.

Bounded results and paging

account_overview, list_agents, list_calls and list_numbers grow with your workspace, so each answers with at most 25,000 characters of result text, however large the workspace is.

The three list tools return one newest-first page:

{
  "calls": [ ... ],
  "pagination": {
    "limit": 10,
    "returned": 7,
    "has_more": true,
    "next_cursor": "<opaque>",
    "omitted": []
  }
}
  • limit takes 1 to 50. list_calls and list_numbers default to 10, list_agents to 50. A larger value is lowered to 50 and a smaller one raised to 1; the page reports the limit it used.
  • A page holds at most limit items and at most 25,000 characters, so it can hold fewer. returned says how many it holds. Use has_more, never returned < limit, to decide whether to continue.
  • Pass next_cursor back as cursor to read the next page. It is meaningful only while has_more is true, and it is opaque: pass it back exactly as given. Leave cursor out for the first page.
  • An item too large to fit any page on its own is not returned. It is named in omitted as { "id", "reason": "exceeds_result_size_budget", "read_with" }, and the next page continues after it. read_with names the tool that reads that one item on this connection (get_agent for an agent, get_call for a call on the full /mcp endpoint), or is null when the connection has none.
  • A listing is not a snapshot: an item created while you page through it may not appear, and nothing is repeated or skipped because of it.
  • list_calls items are the same call records GET /v1/calls returns, without runtime_config (the agent configuration the call ran with). get_call returns the complete record.

account_overview is a summary, not a page. It returns wallet; usage with its totals and up to 10 line items; and, for agents, numbers, number_requests, calls and conversations, a section with:

  • total: how many there are;
  • preview: up to 5 of the newest, with identifying fields only (for example an agent's id, name, mode and status; a call's id, direction, status, numbers and created_at; a conversation's numbers, status, last_message_at and message_count);
  • returned, has_more (total is larger than returned) and omitted;
  • list_tool: the tool on this connection that pages through the complete records, or null when there is none (number requests; conversations on the read-only connector). It needs its own scope.

The overview never includes SMS message content. Read messages with list_conversations (sms:read).

Migration for custom MCP clients

Read result.structuredContent instead of result.content[0].json, inspect result.isError and structuredContent.error.type instead of error.type, and read scope metadata from _meta instead of annotations.required_scopes. Standard MCP clients already understand these result types.

Since the bounded results above:

  • list_calls and list_numbers return { calls | numbers, pagination } instead of a list (structuredContent.items). Follow next_cursor while has_more is true to read everything.
  • account_overview returns sections with total and preview instead of complete lists of agents, numbers, number requests, calls and conversations; its usage carries totals and up to 10 line items.
  • list_calls items no longer carry runtime_config; use get_call.
  • list_agents pages can now stop before limit and can name items in pagination.omitted.
  • limit must be an integer (10, 10.0 or "10"). A boolean, a fraction or other text is refused instead of being coerced. A cursor must be one this tool returned.
  • account_overview, list_calls and list_numbers are refused for a credential that belongs to no workspace, as list_agents already was.

Prefix scopes

Some scopes are grouped by prefix — for example sms:* covers both sms:read and sms:send. Grant the specific leaf scope for least privilege, or the prefix when an agent legitimately needs the whole group.

Capabilities that a deployment can switch off

A few tools belong to capabilities an operator enables per deployment — the Simulations Lab is one. tools/list publishes them regardless, because it is a catalog of what Skysay can do rather than an inventory of what your instance has switched on. Calling one where the capability is off returns the same not-found the equivalent REST route returns.

When a tool call is refused

A refused call is a tool result with isError: true, so the assistant reads it and can correct itself. structuredContent.error names the refusal's type and message. When the same request over REST would answer with a structured body, that body is under error.body: for example, the issues of a refused configuration, or the current revision after a conflict. A missing MCP scopes or workspace refusal carries no body.

Configure an agent from MCP

An organization-scoped key with agents:read and agents:write can complete the normal customer configuration loop without switching to REST:

  1. Call get_agent_catalog to discover selectable STT, LLM, and TTS options plus conversation engines. It accepts either agents:read or coverage:read; use metered_only: true when you need only component-metered choices. Pass voice_frontend: "gpt_live" on create_agent or set_agent_voice_stack to select GPT-Live 1; omit it to keep Standard.
  2. Call create_agent, then use list_agents for a bounded newest-first page or get_agent for one current safe agent projection.
  3. Use update_agent for the ordinary mutable fields and set_agent_voice_stack to publish a selected stack. Read the published configuration back with get_agent_voice_stack.
  4. Use preview_voice only as an explicit Listen action. It can contact a provider or validate a BYOK credential, is rate-limited per workspace, and may write safe preview-rate or voice-availability state. It is not a read-only or free preview.

list_agents is deliberately a bounded MCP collection: it returns the same safe agent items as REST inside { agents, pagination }, with up to 50 agents per page (the default and maximum), and fewer when the page reaches its 25,000-character limit. Follow pagination.next_cursor only while pagination.has_more is true. REST also returns a named bounded page; its limits and migration contract are documented in REST endpoints.

list_audio_environments is the same catalogue as GET /v1/audio-environments, with the same account:read scope. It is a read-only, provider-independent discovery call: it returns Off plus the currently packaged versioned presets and never changes an agent or starts a preview. Use its exact preset ID and asset version when configuring output ambience or a Simulation Lab caller condition; do not maintain a client-side copy of the catalogue.

The key's organization is authoritative. You may omit the redundant organization_id input from create, list, and preview calls; if supplied, it must match the key's workspace. Agent IDs, optional project filters, saved voice pins, preview rate limits, credential ownership, and provider/BYOK validation stay in the same services as the REST workflow.

Browser sessions stay a browser workflow

For local stdio MCP, set both SKYSAY_MCP_SCOPES and SKYSAY_MCP_ORGANIZATION_ID before calling any tool. An absent organization fails closed; tools/list remains schema discovery only.

Superseded: /docs/agents

Content fields

Conversation content needs its content scope wherever it appears, over MCP and REST. Tools that combine content with other data stay callable at their base scope and omit the content fields:

  • account_overview (account:read) previews conversations without any message, so it never carries a message's body, metadata, error or idempotency_key, whatever the scopes.
  • send_sms (sms:send) and send_agent_template_message (sms:send + agents:read) return the message ID and delivery status; body, metadata, error and idempotency_key come back only with sms:read. This includes a retry that replays an existing idempotency_key.
  • get_simulation_run (agents:read) returns status, verdicts and pagination. Without recordings:read it omits each attempt's transcript, recording_ref and facts, assertion actual values, rubric rationale and linked-evidence note, because each can quote the conversation.

Content-bearing webhook subscriptions

Creating or changing a webhook subscription for sms.received requires sms:read. Subscribing to call.extraction.ready requires recordings:read, because extracted results can quote the conversation. Changing any existing webhook destination requires both sms:read and recordings:read: queued deliveries and concurrent subscription changes can contain either kind of content. Removing a subscription does not make its queued content safe to redirect. Agent names and lifecycle-only subscriptions remain editable with agents:write. Over MCP, a call lacking one of these content scopes receives a refused tool result naming the missing scope, before anything is written.

[

Post-call results

Verify signed lifecycle and artifact events, then optionally read a bounded advisory summary from a completed call.

](https://skysay.ai/docs/post-call-results)[

Tool reference

Every Skysay MCP tool with its required scope, generated from the server's TOOL_SCOPES.

](https://skysay.ai/docs/tool-reference)