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.
| Framework | Connect with x-api-key over Streamable HTTP |
|---|---|
| OpenAI Agents SDK | MCPServerStreamableHttp(params={"url": "…", "headers": {"x-api-key": "…"}}) — docs |
| LangChain | MultiServerMCPClient with "transport": "streamable_http" and a "headers" dict — docs |
| LlamaIndex | BasicMCPClient(url, headers={"x-api-key": "…"}) + McpToolSpec — docs |
| Pydantic AI | MCPServerStreamableHTTP(url, headers={"x-api-key": "…"}) — docs |
| Vercel AI SDK | createMCPClient with an http transport that takes a headers map — docs |
| Mastra | new 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.
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 passheaders=(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.