MCP Server Author Guidelines for Experience Workspace

This document describes what a third‑party MCP (Model Context Protocol) server must do to work seamlessly inside Experience Workspace once it has been registered.

Experience Workspace connects to your server through the da-agent MCP client, which runs inside a Cloudflare Worker (fetch‑based, no Node.js runtime). That environment dictates most of the constraints below.

Source of truth: da-agent/src/mcp/client.ts, da-agent/src/mcp/tool-adapter.ts, da-agent/src/tool-assembly.ts, da-agent/src/mcp/token-allowlist.ts. If the code and this doc disagree, the code wins — please open a PR to update this doc.

1. Transport: Streamable HTTP (required)

The client speaks the Streamable HTTP transport (MCP protocol revision 2025-03-26). A legacy SSE (text/event-stream) response is accepted as a backwards‑compatible fallback, but plain Streamable HTTP is the recommended target.

What the client sends

Every JSON‑RPC call is a single POST to your server URL with:

  • sample (json)
POST <your-server-url>
Content-Type: application/json
Accept: application/json, text/event-stream
Mcp-Session-Id: <id>        # only after you return one (see below)

Your server may respond with either:

Either content type is fine. Pick whichever your framework produces.

Session handling

Required handshake

The client always performs this sequence. Implement all of it:

  • sample (json)
{ "protocolVersion": "2025-03-26", "capabilities": {}, "clientInfo": { "name": "da-agent", "version": "1.0.0" } }
  1. Initialize and respond with your result (capabilities, serverInfo, etc.).
  2. notifications/initialized — a notification (no id). Return HTTP 202 with no body. The client does not expect a JSON‑RPC response here.
  3. tools/list — return { "tools": [ ... ] }.
  4. tools/call{ "name": "...", "arguments": { ... } } → return a tool result (see §5).

If initialize or tools/list throws/times out, your server is silently dropped for that request and none of your tools appear. Make these two calls fast and reliable.

2. Timeouts

The client uses an AbortController per request and aborts on timeout, surfacing MCP request timed out after <n>ms.

Path Effective timeout (ms) Notes
Tool discovery + tool calls during a chat (connectAndRegisterMCPTools) 15,000 ms Applied in tool-assembly.ts. Covers initialize, tools/list, and each tools/call.
MCPClient standalone default 30,000 ms Library default; the assembly path overrides it to 15,000 ms.

Practical budget: every initialize, tools/list, and tools/call must complete within ~15,000 milliseconds. This is wall‑clock, including your upstream/backend latency.

Guidance:

3. Domain allow list & token forwarding

Experience Workspace forwards the signed‑in user's IMS bearer token (and the DA OAuth client id as x-api-key) to your server only if your URL's hostname matches the trusted domain allow list managed by our development team. Untrusted servers receive only the explicit headers the caller configured — never the user's token.

Implications for authors:

When trusted, expect:

  • header (json)
Authorization: Bearer <user-IMS-token>
x-api-key: <DA OAuth client id>

Please keep in mind that tokens in place may not imply authorization. Please implement strategic validation.

4. Authentication & custom headers

Two ways your server can be authenticated:

  1. Adobe IMS passthrough — automatic, trusted‑domain in allow-list, only (§3).

  2. Caller‑supplied headers — the chat request may include mcpServerHeaders keyed by server id. These are merged into every request to your URL. Use this for your own API keys / bearer tokens when you are not on a trusted domain.

  • sample-config (json)
{
  "mcpServers":       { "my-server": "https://mcp.example.com/mcp" },
  "mcpServerHeaders": { "my-server": [{ "name": "X-My-Api-Key", "value": "..." }] }
  // a { "Header-Name": "value" } object form is also accepted
}

Requirements & cautions:

5. Tool definitions & results

Tool naming

The client namespaces every discovered tool as:

  • namespace (json)
mcp__<serverId>__<yourToolName>

Keep your own tool names short, stable, and snake_case/kebab friendly. Renaming a tool breaks any saved references and the model's learned usage.

Input schemas

Your inputSchema (JSON Schema) is converted to Zod before being handed to the model provider (Bedrock via AI SDK v6). The converter (mcpSchemaToZod) is intentionally conservative:

To be maximally robust:

Tool results

Return the standard MCP tool result shape:

  • tool-result-shape (json)
{ "content": [ { "type": "text", "text": "..." } ], "isError": false }

6. Registration shape (what Experience Workspace stores)

A registered remote server resolves to:

  • sample-config-object (json)
// da-agent/src/mcp/types.ts
interface RemoteMCPServerConfig {
  type: 'http' | 'sse';          // 'http' = Streamable HTTP (recommended)
  url: string;                   // your HTTPS endpoint
  headers?: Record<string, string>;
}

7. Pre‑registration checklist

Use the checklist below for MCP registration compliance checkpoints

Item Check
Endpoint is reachable over HTTPS.
Implements Streamable HTTP (JSON or SSE response); handles a single POST per JSON‑RPC message.
initialize accepts protocolVersion: "2025-03-26" and returns promptly.
notifications/initialized returns 202 with no body.
tools/list returns within ~15,000 ms on a cold session.
Every tools/call returns within ~15,000 ms, or is redesigned to be async/pollable.
Mcp-Session-Id (if used) is honored on follow‑up requests and DELETE.
Tool input schemas are flat/scalar with descriptions.
Tool results use the { content: [{ type, text }], isError } shape.
If you need the user's Adobe identity: hosted on a trusted domain (*.adobe.io by default) or added to TRUSTED_MCP_DOMAINS.
No secrets/PII accepted via tool arguments or URL — credentials go in headers.
Server validates the bearer token / API key it receives; fails closed on error.