MCP endpoint
Long Tail exposes its full tool surface through the MCP (Model Context Protocol) Streamable HTTP transport at /mcp. External MCP clients (Claude Desktop, Cursor, other agents or custom applications) connect to this endpoint to discover and invoke tools.
Every request carries Authorization: Bearer <token>. The endpoint is mounted at /mcp, outside the /api prefix. See MCP for the concepts and the MCP reference for the tools.
Connection
Section titled “Connection”POST /mcpSends a JSON-RPC message (initialize, tools/list, tools/call). The endpoint runs in stateless mode: each request is self-contained and carries its own credential.
Headers
| Header | Value |
|---|---|
Authorization |
Bearer <api-key> |
Content-Type |
application/json |
Accept |
application/json, text/event-stream |
Example request
curl -X POST https://lt.acme.dev/mcp \ -H "Authorization: Bearer $LT_TOKEN" \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -d '{"jsonrpc":"2.0","method":"tools/list","params":{},"id":1}'Response 200: A JSON-RPC result (or an event stream when the client accepts one).
Connecting with the MCP SDK
Section titled “Connecting with the MCP SDK”import { Client } from '@modelcontextprotocol/sdk/client/index.js';import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
const transport = new StreamableHTTPClientTransport( new URL('https://lt.acme.dev/mcp'), { requestInit: { headers: { 'Authorization': `Bearer ${apiKey}` }, }, },);
const client = new Client({ name: 'my-app', version: '1.0.0' });await client.connect(transport);
// Discover toolsconst { tools } = await client.listTools();console.log(`${tools.length} tools available`);
// Invoke a toolconst result = await client.callTool({ name: 'list_roles', arguments: {},});Connecting with curl
Section titled “Connecting with curl”# Initializecurl -X POST https://lt.acme.dev/mcp \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -d '{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"curl","version":"1.0.0"}},"id":1}'
# List toolscurl -X POST https://lt.acme.dev/mcp \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -d '{"jsonrpc":"2.0","method":"tools/list","params":{},"id":2}'
# Call a toolcurl -X POST https://lt.acme.dev/mcp \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -d '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"get_settings","arguments":{}},"id":3}'Methods
Section titled “Methods”| Method | Description |
|---|---|
GET /mcp |
Returns 405: stateless mode serves no SSE streams |
POST /mcp |
JSON-RPC messages: initialize, tools/list, tools/call |
DELETE /mcp |
Returns 405: stateless mode keeps no sessions |
Error responses
Section titled “Error responses”| Status | Meaning |
|---|---|
| 401 | Missing or invalid Bearer token |
| 405 | GET or DELETE request (stateless mode accepts POST only) |
| 500 | Internal server error |
Claude Code integration
Section titled “Claude Code integration”Connect Claude Code to your Long Tail instance as an MCP server. Once connected, Claude Code can discover and invoke all 110 tools: query escalations, manage workflows, store knowledge and more.
Step 1: Get an API key
Section titled “Step 1: Get an API key”Start Long Tail (docker compose up -d --build). On first start, docker compose sets LT_SEED_SERVICE_KEYS=true, so the container’s stdout shows two seeded API keys:
[seed-mcp] read key: lt_bot_<read-key>[seed-mcp] full key: lt_bot_<full-key>The read key exposes 57 read-safe tools (queries, listings). The full key exposes all 110 tools.
Step 2: Configure Claude Code
Section titled “Step 2: Configure Claude Code”Copy the example config and add your API key:
cp .mcp.example.json .mcp.jsonEdit .mcp.json and replace <your-api-key> with the key from step 1:
{ "mcpServers": { "long-tail": { "type": "http", "url": "https://lt.acme.dev/mcp", "headers": { "Authorization": "Bearer lt_bot_<your-api-key>" } } }}.mcp.json is gitignored because it holds secrets. .mcp.example.json is the committed template, the same pattern as .env and .env.example.
Or use the CLI:
claude mcp add --transport http long-tail https://lt.acme.dev/mcp \ --header "Authorization: Bearer lt_bot_<your-key>"Step 3: Verify
Section titled “Step 3: Verify”Restart Claude Code, then try:
“How’s the system doing?”
Claude Code calls get_system_overview, and one call returns triage, throughput, trends, infrastructure and process status. Try more:
“What roles exist in the system?”
“Show me the most recent worker stream message”
“List all pending escalations”
“Store a knowledge entry: domain=notes, key=today, data={message: ‘shipped MCP’}”
“What MCP servers are registered?”
Authentication
Section titled “Authentication”The credential travels in the Authorization: Bearer header; the host’s session cookie does not authenticate /mcp. Three token types are accepted:
| Type | Format | Use case |
|---|---|---|
| OAuth access token | Issued by the Long Tail OAuth server for this resource (when the OAuth server is enabled) | MCP clients that sign in a person |
| JWT | Signed user token, such as the one from POST /api/auth/login |
Interactive sessions, testing |
| API key | lt_bot_* prefix |
Service accounts, automation |
When the OAuth server is enabled, a 401 response carries a WWW-Authenticate: Bearer resource_metadata="..." challenge so MCP clients can discover how to sign in. An access token authenticates only while its grant is live. See OAuth and delegation.
Service account setup
Section titled “Service account setup”- Create a service account:
POST /api/bot-accounts. - Generate an API key with scopes:
POST /api/bot-accounts/:id/api-keys. - Use the key as a Bearer token for
/mcprequests.
Scopes
Section titled “Scopes”API key scopes control which tools are visible:
| Scope | Tools | Description |
|---|---|---|
mcp:read |
57 | Read-safe tools only: queries, listings, discovery |
mcp:read + mcp:full |
110 | All tools, including those that change state |
A read key cannot invoke write operations (create, update, delete, invoke workflows). Use the read key for monitoring and exploration and the full key for automation.
Seeded service account
Section titled “Seeded service account”Long Tail seeds an mcp-service account at startup with two API keys:
- read: scopes
["mcp:read"] - full: scopes
["mcp:read", "mcp:full"]
With LT_SEED_SERVICE_KEYS=true (local docker compose), the keys are minted at first start and printed once to stdout, never through the logger; they cannot be retrieved again. Otherwise the account is created with no keys, and a superadmin mints them from the dashboard or the bot-accounts API.
Exposure control
Section titled “Exposure control”Deployment-wide configuration controls which servers and tools are available on the /mcp endpoint. Set it through startConfig.mcp.exposure:
| Option | Type | Default | Description |
|---|---|---|---|
readOnly |
boolean | false | Expose only read-safe tools to all callers |
hideAiWhenUnavailable |
boolean | true | Hide AI-dependent servers when no API key is configured |
allowServers |
string[] | all shipped | Explicit server allowlist |
denyServers |
string[] | none | Server denylist |
Exposure config is deployment-wide and scopes are per API key. Both layers apply: exposure filters first, then scope.
