跳到主要内容

MCP Server

Every ClawCentral agent is a remote MCP server. IDE clients (Claude Desktop, Cursor, VS Code, Windsurf, Claude Code) connect over HTTP with OAuth 2.1 authentication. Custom clients can integrate using the same protocol.

For quickstart setup with supported IDEs, see MCP Quickstart.

Endpoint

https://<your-slug>.clawcentral.io/mcp
MethodPurpose
POST /mcpJSON-RPC requests (tool calls, initialize, ping)
GET /mcpSSE streaming
DELETE /mcpSession teardown
GET /mcp/healthHealth check

The server implements MCP protocol version 2025-11-25 with tools capability.

Discovery

Two standard discovery endpoints are available:

OAuth Authorization Server Metadata

GET /.well-known/oauth-authorization-server

Returns:

{
"issuer": "https://my-agent.clawcentral.io",
"authorization_endpoint": "https://my-agent.clawcentral.io/oauth/authorize",
"token_endpoint": "https://my-agent.clawcentral.io/oauth/token",
"registration_endpoint": "https://my-agent.clawcentral.io/oauth/register",
"response_types_supported": ["code"],
"grant_types_supported": ["authorization_code", "refresh_token"],
"scopes_supported": ["mcp:read", "mcp:write", "mcp:chat", "mcp:admin"],
"token_endpoint_auth_methods_supported": ["client_secret_post", "none"],
"code_challenge_methods_supported": ["S256"]
}

Protected Resource Metadata

GET /.well-known/oauth-protected-resource

Returns:

{
"resource": "https://my-agent.clawcentral.io/mcp",
"scopes_supported": ["mcp:read", "mcp:write", "mcp:chat", "mcp:admin"]
}

Authentication

ClawCentral uses OAuth 2.1 with PKCE (S256). The flow:

  1. Register a client (one-time)
  2. Authorize via browser redirect
  3. Exchange code for tokens
  4. Use access token in Authorization: Bearer header
  5. Refresh when the access token expires

1. Dynamic client registration

POST /oauth/register
Content-Type: application/json

{
"redirect_uris": ["http://localhost:3334/callback"],
"client_name": "My MCP Client",
"token_endpoint_auth_method": "none",
"grant_types": ["authorization_code"],
"scope": "mcp:read mcp:chat mcp:write"
}

Response (201):

{
"client_id": "mcp_a1b2c3d4-...",
"client_id_issued_at": 1712345678,
"redirect_uris": ["http://localhost:3334/callback"],
"token_endpoint_auth_method": "none",
"grant_types": ["authorization_code"],
"client_name": "My MCP Client",
"scope": "mcp:read mcp:chat mcp:write"
}

Public clients (localhost redirects or token_endpoint_auth_method: "none") must use PKCE. Confidential clients receive a client_secret and may optionally skip PKCE.

2. Authorization

Redirect the user's browser to:

GET /oauth/authorize
?client_id=mcp_a1b2c3d4-...
&redirect_uri=http://localhost:3334/callback
&response_type=code
&scope=mcp:read+mcp:chat+mcp:write
&code_challenge=<S256_HASH>
&code_challenge_method=S256
&state=<YOUR_STATE>

The user signs in via Microsoft Entra, and ClawCentral redirects back to your redirect_uri with ?code=<AUTH_CODE>&state=<YOUR_STATE>.

Authorization codes are single-use and expire after 10 minutes.

3. Token exchange

POST /oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code=<AUTH_CODE>
&client_id=mcp_a1b2c3d4-...
&redirect_uri=http://localhost:3334/callback
&code_verifier=<PKCE_VERIFIER>

Response:

{
"access_token": "eyJ...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "mcp_rt_...",
"scope": "mcp:read mcp:chat mcp:write"
}

4. Refresh tokens

Access tokens expire after 1 hour. Refresh tokens are valid for 30 days and rotate on each use (the old refresh token is invalidated).

POST /oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token
&refresh_token=mcp_rt_...
&client_id=mcp_a1b2c3d4-...

Scopes

ScopeAccess
mcp:readList, get, history, status — all read-only operations
mcp:chatSend messages, abort, async chat operations
mcp:writeModify config, sessions, cron jobs, install skills
mcp:adminAdmin-only operations (reserved)

Default when not specified: mcp:read mcp:chat.

Calling a tool without the required scope returns an insufficient_scope error listing the missing scopes.

Transport

ClawCentral uses the Streamable HTTP transport (MCP 2025-11-25):

  • POST /mcp accepts JSON-RPC requests and returns JSON-RPC responses
  • GET /mcp opens an SSE stream for server-initiated messages
  • Sessions are stateless — each request is independent
  • enableJsonResponse: true — tool results are returned as JSON, not SSE

All requests require Authorization: Bearer <access_token>.

CORS

The MCP endpoint allows cross-origin requests from:

  • claude.ai, claude.com
  • chatgpt.com, chat.openai.com
  • mcpcentral.io and *.mcpcentral.io
  • *.clawcentral.io
  • vscode-webview:// (VS Code extensions)

Exposed headers: Mcp-Session-Id.

Tools

31 tools in 10 categories. Each tool lists its required scope and behavior annotations.

Chat

ToolScopeDescription
chat_sendmcp:chatSend a message and wait for the full response
chat_historymcp:readRetrieve message history (supports limit, offset)
chat_abortmcp:chatStop an in-progress response

Async chat

ToolScopeDescription
chat_send_asyncmcp:chatSend a message, returns runId immediately
chat_run_getmcp:readPoll an async run's status and result
chat_run_listmcp:readList active and completed runs
chat_run_cancelmcp:chatCancel a running async job

Sessions

ToolScopeDescription
sessions_listmcp:readList all sessions (max 50)
sessions_getmcp:readGet session details
sessions_patchmcp:writeUpdate session label or thinking level
sessions_deletemcp:writeDelete a session and optionally its transcript
sessions_usagemcp:readToken usage report by date range

Config

ToolScopeDescription
config_getmcp:readRead a tenant config value
config_setmcp:writeUpdate a tenant config value

Models

ToolScopeDescription
models_listmcp:readList available AI models

Skills

ToolScopeDescription
skills_listmcp:readInstalled skills and their status
skills_browsemcp:readBrowse available skills from registries
skills_installmcp:writeInstall a skill from a registry
skills_registriesmcp:readList configured skill registries

Agents

ToolScopeDescription
agents_listmcp:readAvailable agents and their configs

Channels

ToolScopeDescription
channels_statusmcp:readCheck connectivity for each channel
channels_actionmcp:writeSend message, react, or pin in a channel
channels_historymcp:readMessage history with search and filtering

Cron

ToolScopeDescription
cron_listmcp:readList scheduled jobs
cron_addmcp:writeCreate a cron, at, or interval job
cron_removemcp:writeDelete a scheduled job
cron_runmcp:writeExecute a job immediately
cron_runsmcp:readView job execution history

System

ToolScopeDescription
statusmcp:readHealth check, version, and protocol info
usage_costmcp:readUsage stats and cost breakdown (day/week/month)

Error handling

Tool errors return JSON in the content field with isError: true:

{
"content": [{ "type": "text", "text": "{\"error\": \"insufficient_scope\", \"message\": \"Missing scopes: mcp:write\"}" }],
"isError": true
}
Error codeMeaning
unknown_toolTool name not in registry
insufficient_scopeToken missing required scopes
gateway_errorInternal dispatch failed
internal_errorUnexpected server error

Responses exceeding ~25,000 tokens are truncated with a marker suggesting pagination parameters.

Rate limiting

MCP endpoints are rate-limited to 60 requests per 60 seconds per client. Exceeding the limit returns HTTP 429.

Health check

GET /mcp/health
{
"status": "ok",
"version": "1.0.0",
"protocol": "2025-11-25"
}

Returns "status": "degraded" with an error field if the server is unhealthy.

Custom client example

A minimal Python client using the mcp SDK:

import asyncio
from mcp.client.streamable_http import streamablehttp_client
from mcp import ClientSession

MCP_URL = "https://my-agent.clawcentral.io/mcp"

async def main():
# After completing the OAuth flow and obtaining an access_token:
headers = {"Authorization": f"Bearer {access_token}"}

async with streamablehttp_client(MCP_URL, headers=headers) as (read, write, _):
async with ClientSession(read, write) as session:
await session.initialize()

# List available tools
tools = await session.list_tools()
for tool in tools.tools:
print(f"{tool.name}: {tool.description}")

# Send a chat message
result = await session.call_tool("chat_send", {
"message": "What's on my schedule today?"
})
print(result.content[0].text)

asyncio.run(main())

A TypeScript equivalent:

import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";

const transport = new StreamableHTTPClientTransport(
new URL("https://my-agent.clawcentral.io/mcp"),
{ requestInit: { headers: { Authorization: `Bearer ${accessToken}` } } }
);

const client = new Client({ name: "my-client", version: "1.0.0" });
await client.connect(transport);

const tools = await client.listTools();
console.log(tools.tools.map(t => t.name));

const result = await client.callTool({
name: "chat_send",
arguments: { message: "What's on my schedule today?" }
});
console.log(result.content);