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
| Method | Purpose |
|---|---|
POST /mcp | JSON-RPC requests (tool calls, initialize, ping) |
GET /mcp | SSE streaming |
DELETE /mcp | Session teardown |
GET /mcp/health | Health 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:
- Register a client (one-time)
- Authorize via browser redirect
- Exchange code for tokens
- Use access token in
Authorization: Bearerheader - 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
| Scope | Access |
|---|---|
mcp:read | List, get, history, status — all read-only operations |
mcp:chat | Send messages, abort, async chat operations |
mcp:write | Modify config, sessions, cron jobs, install skills |
mcp:admin | Admin-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 /mcpaccepts JSON-RPC requests and returns JSON-RPC responsesGET /mcpopens 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.comchatgpt.com,chat.openai.commcpcentral.ioand*.mcpcentral.io*.clawcentral.iovscode-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
| Tool | Scope | Description |
|---|---|---|
chat_send | mcp:chat | Send a message and wait for the full response |
chat_history | mcp:read | Retrieve message history (supports limit, offset) |
chat_abort | mcp:chat | Stop an in-progress response |
Async chat
| Tool | Scope | Description |
|---|---|---|
chat_send_async | mcp:chat | Send a message, returns runId immediately |
chat_run_get | mcp:read | Poll an async run's status and result |
chat_run_list | mcp:read | List active and completed runs |
chat_run_cancel | mcp:chat | Cancel a running async job |
Sessions
| Tool | Scope | Description |
|---|---|---|
sessions_list | mcp:read | List all sessions (max 50) |
sessions_get | mcp:read | Get session details |
sessions_patch | mcp:write | Update session label or thinking level |
sessions_delete | mcp:write | Delete a session and optionally its transcript |
sessions_usage | mcp:read | Token usage report by date range |
Config
| Tool | Scope | Description |
|---|---|---|
config_get | mcp:read | Read a tenant config value |
config_set | mcp:write | Update a tenant config value |
Models
| Tool | Scope | Description |
|---|---|---|
models_list | mcp:read | List available AI models |
Skills
| Tool | Scope | Description |
|---|---|---|
skills_list | mcp:read | Installed skills and their status |
skills_browse | mcp:read | Browse available skills from registries |
skills_install | mcp:write | Install a skill from a registry |
skills_registries | mcp:read | List configured skill registries |
Agents
| Tool | Scope | Description |
|---|---|---|
agents_list | mcp:read | Available agents and their configs |
Channels
| Tool | Scope | Description |
|---|---|---|
channels_status | mcp:read | Check connectivity for each channel |
channels_action | mcp:write | Send message, react, or pin in a channel |
channels_history | mcp:read | Message history with search and filtering |
Cron
| Tool | Scope | Description |
|---|---|---|
cron_list | mcp:read | List scheduled jobs |
cron_add | mcp:write | Create a cron, at, or interval job |
cron_remove | mcp:write | Delete a scheduled job |
cron_run | mcp:write | Execute a job immediately |
cron_runs | mcp:read | View job execution history |
System
| Tool | Scope | Description |
|---|---|---|
status | mcp:read | Health check, version, and protocol info |
usage_cost | mcp:read | Usage 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 code | Meaning |
|---|---|
unknown_tool | Tool name not in registry |
insufficient_scope | Token missing required scopes |
gateway_error | Internal dispatch failed |
internal_error | Unexpected 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);