amplitude / amplitude/mcp-server-guide

MCP server returns truncated JSON-RPC error body (82 bytes, invalid JSON) + HTTP/2 stream never terminated

Abierto
#3 0 comentarios 0 reacciones 0 asignados Ver en GitHub
Lenguaje dominante
Sin datos de lenguaje
Estrellas
45
Forks
5
Métricas de merge de PR
Sin PR fusionados en 30 d

Descripción

# Bug: MCP server returns truncated JSON-RPC error body (82 bytes, invalid JSON)

## Summary

Every request to `https://mcp.amplitude.com/mcp` with a valid OAuth token returns HTTP 500 with a truncated, invalid JSON body. The server sends exactly 82 bytes regardless of request type, cutting the JSON off mid-string.

## Steps to Reproduce

### Step 1 — Obtain a valid access token via OAuth (curl + browser)

**1a. Register a client dynamically:**

```bash
curl -s -X POST https://mcp.amplitude.com/register \
-H "Content-Type: application/json" \
-d '{
"client_name": "debug-client",
"redirect_uris": ["http://localhost:9999/callback"],
"grant_types": ["authorization_code", "refresh_token"],
"response_types": ["code"],
"token_endpoint_auth_method": "none"
}'
```

Save the returned `client_id`.

**1b. Generate a PKCE pair and capture into shell variables:**

```bash
eval $(node -e "
const crypto = require('crypto');
const v = crypto.randomBytes(32).toString('base64url');
const c = crypto.createHash('sha256').update(v).digest('base64url');
console.log('CODE_VERIFIER=' + v);
console.log('CODE_CHALLENGE=' + c);
")
```

**1c. Start a temporary listener on port 9999** to capture the auth code from the redirect (the browser redirects to `localhost:9999` after authorization — without a listener the code may not be visible in the address bar, especially on Safari/Windows):

```bash
# macOS / Linux (run in a separate terminal)
nc -l 9999

# Windows (PowerShell, run in a separate terminal)
$l = [System.Net.Sockets.TcpListener]::new(9999); $l.Start(); $c = $l.AcceptTcpClient(); [System.IO.StreamReader]::new($c.GetStream()).ReadLine(); $l.Stop()
```

**1d. Open this URL in a browser** (replace ``):

```
https://mcp.amplitude.com/authorize?client_id=&response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A9999%2Fcallback&scope=mcp%3Aread+mcp%3Awrite+offline_access&state=test&code_challenge=$CODE_CHALLENGE&code_challenge_method=S256
```

After authorizing, the `nc` listener prints the raw HTTP request — copy `` from the `GET /callback?code=&state=test` line.

**1e. Exchange the code for a token** (replace `` and ``):

```bash
TOKEN=$(curl -s -X POST https://mcp.amplitude.com/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code&code=&redirect_uri=http%3A%2F%2Flocalhost%3A9999%2Fcallback&client_id=&code_verifier=$CODE_VERIFIER" \
| node -e "const d=require('fs').readFileSync('/dev/stdin','utf8'); console.log(JSON.parse(d).access_token)")
```

### Step 2 — Reproduce the bug

**Symptom A — HTTP/2 stream never terminated (connection hangs):**

```bash
curl -v --max-time 10 -X POST https://mcp.amplitude.com/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "Authorization: Bearer $TOKEN" \
-d '{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}},"id":1}'
```

Observe: response headers arrive instantly (`HTTP/2 500`, `content-length: 82`) but curl hangs until `--max-time` kills it — no body is printed. This is because the server writes 82 bytes but never sends the HTTP/2 `END_STREAM` frame to close the response, so curl waits indefinitely.

**Symptom B — Truncated, invalid JSON body:**

The MCP TypeScript SDK reads exactly `Content-Length` bytes and closes, exposing the actual body:

```bash
npm install @modelcontextprotocol/sdk
```

```js
// debug.mjs
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";

const transport = new StreamableHTTPClientTransport(
new URL("https://mcp.amplitude.com/mcp"),
{ requestInit: { headers: { Authorization: "Bearer " } } }
);
const client = new Client({ name: "test", version: "1.0" }, { capabilities: {} });
await client.connect(transport); // throws with the body below
```

```bash
node debug.mjs
```

## Actual Response

**HTTP status:** `500`
**Content-Length:** `82`
**Body (all 82 bytes, confirmed via `body.length` in Node — truncated mid-JSON):**

```
{"jsonrpc":"2.0","error":{"code":-32603,"message":"Internal server error [requestI
```

The JSON is invalid — the string value, object, and array are all unclosed. The body is cut off at exactly 82 bytes, 31 bytes into the error message.

## Expected Response

A complete, valid JSON-RPC error response, e.g.:

```json
{"jsonrpc":"2.0","error":{"code":-32603,"message":"Internal server error [requestId: req_xxx]"},"id":1}
```

## Analysis

The 82-byte body breaks down as:
- 51 bytes: fixed envelope `{"jsonrpc":"2.0","error":{"code":-32603,"message":"`
- 31 bytes: start of message `Internal server error [requestI`

The body is cut off mid-string with the JSON never closed. The server appears to abort or crash mid-response after writing this partial content. Both the truncated body and the missing HTTP/2 `END_STREAM` frame are symptoms of the same failure.

Verified: an invalid token returns a fast HTTP 401 in ~0.7s. Only valid tokens trigger the 500 + partial body, confirming the failure occurs after authentication succeeds.

## Environment

- Endpoint: `https://mcp.amplitude.com/mcp`
- MCP SDK: `@modelcontextprotocol/sdk` latest
- Transport: Streamable HTTP
- Protocol version: `2024-11-05`
- Affected requests: `initialize` confirmed; GET `/mcp` also returns the same 500 + partial body

Guía de contribución

No hay ninguna guía de contribución indexada para este repositorio

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.