Skip to main content
Glama
thunderbird89

anthilll-inside

README.md
# anthilll-inside

PoC MCP server for reproducing [Anthropic hosted MCP client SSE response-size hangs](anthropic-mcp-sse-bug-report.md).

The server uses `@modelcontextprotocol/sdk@1.29.0` with **stateless** `StreamableHTTPServerTransport` in default **SSE** mode (`enableJsonResponse: false`), matching the conditions described in the bug report. A single tool — `generate_lipsum` — returns a configurable number of pseudo-Latin sentences so you can bracket the suspected ~10–333 KiB threshold.

## Quick start (local)

```bash
cp .env.example .env
npm install
npm run dev
```

The MCP endpoint is `http://127.0.0.1:3000/mcp`.

### Tool: `generate_lipsum`

| `sentence_count` | Approx. SSE response |
| --- | --- |
| 50 | ~8 KiB |
| 500 | ~55 KiB |
| 2000 | ~222 KiB |
| 3000 | ~333 KiB |

### Test with curl (SSE)

```bash
# Initialize + list tools (small SSE response)
curl -sS -X POST http://127.0.0.1:3000/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"curl","version":"1.0"}}}'

# Call generate_lipsum with 3000 sentences (~333 KiB SSE — matches bug report)
curl -sS -X POST http://127.0.0.1:3000/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"generate_lipsum","arguments":{"sentence_count":3000}}}'
```

### Workaround mode (JSON)

Set `ENABLE_JSON_RESPONSE=true` to serve `Content-Type: application/json` instead of SSE — the known workaround from the bug report.

## Logging

This PoC logs **everything** useful for layer-by-layer debugging:

**Server-side (Pino structured logs to stdout):**

- `request_received` / `request_completed` — per-request access log with `reqId`, `responseBytes`, `durationMs`, `writableEnded`
- `jsonrpc_request_body` — inbound JSON-RPC body (Bearer tokens redacted)
- `mcp_request_dispatch` — method, `Accept` header, transport mode
- `tool_invoked` / `lipsum_generated` / `tool_response_ready` — tool lifecycle with byte counts

**Client-visible (MCP `notifications/message` via logging capability):**

- `tool_start`, `lipsum_generated`, `tool_response_ready` events with `payloadBytes` and `toolReqId`

Note: client-visible log notifications are only delivered in **SSE** mode; they are dropped when `ENABLE_JSON_RESPONSE=true` (SDK behavior).

## Docker

```bash
docker build -t mcp-sse-repro .
docker run --rm -p 3000:3000 mcp-sse-repro
```

## Kubernetes (Helm)

```bash
docker build -t your-registry/mcp-sse-repro:latest .
docker push your-registry/mcp-sse-repro:latest

helm upgrade --install mcp-sse-repro ./helm/mcp-sse-repro \
  --set image.repository=your-registry/mcp-sse-repro \
  --set image.tag=latest \
  --set ingress.enabled=true \
  --set ingress.hosts[0].host=mcp.example.com \
  --set env.ALLOWED_HOSTS=mcp.example.com
```

When deploying behind an ingress, set `ALLOWED_HOSTS` to your public hostname for DNS rebinding protection.

## Configuration

| Variable | Default | Description |
| --- | --- | --- |
| `PORT` | `3000` | HTTP listen port |
| `HOST` | `127.0.0.1` | Bind address (`0.0.0.0` in container/K8s) |
| `ALLOWED_HOSTS` | — | Comma-separated allowed `Host` headers |
| `ENABLE_JSON_RESPONSE` | `false` | `true` = JSON workaround; `false` = SSE repro |
| `LOG_LEVEL` | `debug` | Pino log level |
| `LOG_REQUEST_BODY` | `true` | Log inbound JSON-RPC bodies |
| `LOG_RESPONSE_BODY_PREVIEW_BYTES` | `2048` | Max bytes of request body to log |
| `MAX_SENTENCE_COUNT` | `10000` | Upper bound for `sentence_count` |

## Bug report

See [anthropic-mcp-sse-bug-report.md](anthropic-mcp-sse-bug-report.md) for the full write-up with layer-by-layer evidence. File issues at [anthropics/claude-ai-mcp](https://github.com/anthropics/claude-ai-mcp/issues).