Skip to main content

Connect a custom client

If you're building your own agent rather than using Claude Desktop or Claude Code, connect directly with the official MCP SDK. Both TypeScript and Python SDKs ship with Streamable-HTTP transport built in.

TypeScript

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

const client = new Client(
{ name: "my-agent", version: "1.0.0" },
{ capabilities: {} },
);

// No trailing slash on the URL — /mcp/ gets redirected and the handshake fails
await client.connect(
new StreamableHTTPClientTransport(new URL("https://mcp.edited.com/mcp"), {
requestInit: { headers: { "x-api-key": "YOUR_API_KEY" } },
}),
);

// Discover what's available
const { tools } = await client.listTools();

// Call a tool
const result = await client.callTool({
name: "mdmcp-search_brands",
arguments: { query: "Nike", search_limit: 5 },
});

// Tool results come back as TextContent blocks; the text is JSON
const brands = JSON.parse(result.content[0].text);
console.log(brands);
// → [{ name: "Nike", slug: "nike" }, ...]

await client.close();

Python

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

async def main():
headers = {"x-api-key": "YOUR_API_KEY"}
# No trailing slash on the URL — /mcp/ gets redirected and the handshake fails
async with streamablehttp_client("https://mcp.edited.com/mcp", headers=headers) as (read, write, _):
async with ClientSession(read, write) as session:
await session.initialize()
tools = await session.list_tools()
result = await session.call_tool(
"mdmcp-search_brands",
{"query": "Nike", "search_limit": 5},
)
# result.content is a list of TextContent objects
print(result.content[0].text)

asyncio.run(main())

Using your agent's framework

Most agent frameworks speak MCP natively — point them at the same endpoint and pass your key as the x-api-key header. That header detail matters: EDITED MCP authenticates with x-api-key, not a bearer token, so the framework has to let you set an arbitrary request header.

FrameworkConnect with x-api-key over Streamable HTTP
OpenAI Agents SDKMCPServerStreamableHttp(params={"url": "…", "headers": {"x-api-key": "…"}})docs
LangChainMultiServerMCPClient with "transport": "streamable_http" and a "headers" dict — docs
LlamaIndexBasicMCPClient(url, headers={"x-api-key": "…"}) + McpToolSpecdocs
Pydantic AIMCPServerStreamableHTTP(url, headers={"x-api-key": "…"})docs
Vercel AI SDKcreateMCPClient with an http transport that takes a headers map — docs
Mastranew MCPClient({ servers: { edited: { url, requestInit: { headers: { "x-api-key": "…" } } } } })docs

In each case the URL is the same MCP endpoint and the framework handles the JSON-RPC handshake; you supply the x-api-key header.

Claude via the Anthropic API

Anthropic's hosted MCP connector (the mcp_servers parameter on the Messages API) only forwards a bearer token via authorization_token, so it can't send the x-api-key EDITED MCP requires. To call EDITED MCP from Claude, connect with the official MCP SDK client yourself (the TypeScript / Python examples above) and hand the resulting tools to the model with Anthropic's client-side MCP helpers — or use the ready-made app integrations in Connect Claude Code and Connect Claude Desktop.

What to do with the result

The identifier-resolution tools return objects with stable identifiers (slug for retailers/brands, id for everything else). Feed those identifiers into the market-data query tools to pull the actual data — see Recipes → Example workflow for an end-to-end demo.

Troubleshooting

401 Unauthorized on every call

  • Missing or wrong x-api-key, or a key that isn't enabled for MCP. Set the header on the transport (TS) or pass headers= (Python), as shown above — see Authentication & access for how to get a key.

Connection refused / timeout

  • Wrong URL, or the endpoint isn't reachable from your network. Confirm with curl -I https://mcp.edited.com/mcp — an HTTP status (e.g. 401) means it's reachable; a connection error means it isn't.

Handshake fails with a 307 or the connection dies after the first call

  • The URL has a trailing slash. Use https://mcp.edited.com/mcp — the /mcp/ form is redirected in a way most HTTP clients can't follow (see Concepts → Transports).

MCP error -32600 on every call

  • Missing the protocol-version header. The SDK adds it automatically; if you're hand-rolling fetch, include MCP-Protocol-Version: 2025-06-18.

Empty result on a real query

  • Vector search is misspelling-tolerant but not magic. Try a more specific query or batch several phrasings — see Concepts → Tools.

For the full error model — HTTP statuses, JSON-RPC codes, and retry guidance — see Operations → Errors.