modelcontextprotocol / modelcontextprotocol/typescript-sdk
[v2] feedback: Client sends legacy `initialize` to non-conforming "MCP" endpoint — no dual-era probe
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 13.4k
- Forks
- 2.2k
- Avg merge
- 3d 15h
- Merged PRs (30d)
- 4
Description
What happened?
We used the v2 beta client (@modelcontextprotocol/client@2.0.0-beta.1) to connect to Shopify's Global Catalog endpoint, which advertises transport: "mcp" in its UCP service card at https://catalog.shopify.com/.well-known/ucp.
On client.connect(transport), the SDK unconditionally sends an initialize request with protocolVersion: "2025-11-25". Shopify's server doesn't implement the MCP protocol lifecycle at any version — it only accepts raw tools/call JSON-RPC POSTs. It rejects initialize with a domain-specific error:
SdkHttpError: Error POSTing to endpoint: {"jsonrpc":"2.0","id":0,"error":{"code":-32001,"message":"UCP discovery failed","data":{"code":"invalid_profile_url","content":"Unable to fetch agent profile: Missing profile uri"}}}
The SDK retries this 3 times (treating it as a transport failure) and never reaches callTool.
Wire capture of the first request the SDK sends:
{
"url": "https://catalog.shopify.com/api/ucp/mcp",
"method": "POST",
"headers": {
"accept": "application/json, text/event-stream",
"content-type": "application/json"
},
"body": {
"method": "initialize",
"params": {
"protocolVersion": "2025-11-25",
"capabilities": {},
"clientInfo": { "name": "progettai-shopify-ucp", "version": "1.0.0" }
},
"jsonrpc": "2.0",
"id": 0
}
}
Key observations:
LATEST_PROTOCOL_VERSIONin the compiled bundle is"2025-11-25", not"2026-07-28"- No
MCP-Protocol-Versionheader (required by the 2026-07-28 spec) - No
_metainline metadata — the client operates in legacy mode only - The server returns a well-formed JSON-RPC error (code
-32001) but the SDK treats it as retryable rather than surfacing it as a clear failure
Shopify's non-conformance: Their endpoint doesn't implement initialize, doesn't use MCP-Protocol-Version headers, doesn't use _meta envelopes — it accepts only bare tools/call POSTs with a proprietary arguments.meta.ucp-agent.profile payload. It's JSON-RPC with MCP framing but no MCP lifecycle.
What did you expect?
Per the 2026-07-28 versioning spec, a dual-era client should:
- Attempt a modern stateless request first (with
_metainline andMCP-Protocol-Versionheader) - Stay modern if the server responds with success or a recognized modern error
- Fall back to
initializeonly if the server returns a 4xx without a recognized modern JSON-RPC error
We expected the v2 SDK (which claims 2026-07-28 support in its release notes) to implement this probe-first flow on the client side. With that, a modern-only server (or even a non-conforming one like Shopify) would receive a tools/call as the first request rather than initialize.
Additionally: When the SDK receives a well-formed JSON-RPC error in response to initialize (even with a non-standard code like -32001), we expected it to surface a clear error rather than retrying — the server is telling the client "I don't understand this request," not "transient failure."
Our workaround: We bypassed the SDK and send raw fetch POSTs with JSON-RPC tools/call directly, matching Shopify's format.
Code to reproduce
import { Client, StreamableHTTPClientTransport } from '@modelcontextprotocol/client';
const client = new Client({ name: 'my-client', version: '1.0.0' });
const transport = new StreamableHTTPClientTransport(
new URL('https://catalog.shopify.com/api/ucp/mcp'),
{
requestInit: {
headers: { 'UCP-Agent': 'profile="https://example.com/.well-known/ucp"' }
}
}
);
// Sends "initialize" — Shopify rejects it. callTool is never reached.
await client.connect(transport);
await client.callTool({
name: 'search_catalog',
arguments: {
meta: { 'ucp-agent': { profile: 'https://example.com/.well-known/ucp' } },
catalog: { query: 'test' }
}
});
SDK version
@modelcontextprotocol/client@2.0.0-beta.1
Area
Client
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with Client.connect and StreamableHTTPClientTransport, then run the supplied Shopify reproduction to capture the initial request and retry behavior. The work is done when the client follows the requested modern-versus-legacy probing behavior and surfaces the well-formed initialize error without treating it as a retryable transport failure.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- api
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100