Skip to content

fix: resolve Cursor connection failures and structuredContent spec violation - #38

Merged
huacnlee merged 2 commits into
mainfrom
fix-sse
May 12, 2026
Merged

fix: resolve Cursor connection failures and structuredContent spec violation#38
huacnlee merged 2 commits into
mainfrom
fix-sse

Conversation

@huacnlee

@huacnlee huacnlee commented May 12, 2026

Copy link
Copy Markdown
Member

Summary

  • Switch MCP server to stateless mode to fix session-not-found errors on multi-instance deployments
  • Fix structuredContent type violation that caused Cursor to reject array tool responses

Fix 1 — Stateless mode (src/auth/mod.rs)

Root cause: The server is designed for stateless multi-instance deployment, but was using stateful_mode: true (the rmcp default) with an in-memory LocalSessionManager. Under a load balancer, sessions created on Instance A are unknown to Instance B, triggering a failure chain in Cursor:

  1. POST with cached Mcp-Session-Id404 Session not found (session lives on another instance)
  2. Cursor falls back to legacy SSE V1 protocol (GET without session ID) → 400 Session ID is required

Claude Code and Codex are unaffected because they re-initialize on every connection rather than caching session IDs.

Fix: Replace LocalSessionManager with NeverSessionManager and set stateful_mode(false). The Mcp-Session-Id header is completely ignored in stateless mode; each POST is handled independently with no session state.

Fix 2 — structuredContent type (src/tools/mod.rs)

Root cause: tool_result unconditionally assigned the parsed JSON value to structured_content. Tools returning Vec<T> (e.g. quote, static_info, trades, candlesticks, participants) serialize to a JSON array. The MCP spec requires structuredContent to be a JSON object (record); Cursor validates this strictly and rejects array values with:

Invalid input: expected record, received array

Fix: Added .filter(serde_json::Value::is_object) so structured_content is only set for JSON object responses. Array responses leave it as None, which is valid per spec. The fix is centralized in tool_result and covers all tools.

🤖 Generated with Claude Code

huacnlee and others added 2 commits May 12, 2026 14:06
…nce deployments

Use NeverSessionManager + stateful_mode(false) so every POST request is
handled independently. Previously the default LocalSessionManager stored
sessions in memory per process, causing Cursor (and any load-balanced
client) to receive 404 "Session not found" when a request landed on a
different instance than the one that created the session. Clients that
cache the Mcp-Session-Id then fell back to legacy SSE V1, which this
server does not implement, producing a 400 error.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
MCP spec requires structuredContent to be a record (JSON object).
Tools that return Vec<T> serialize to a JSON array, which Cursor
validates strictly and rejects with "expected record, received array".

Filter structured_content to None for non-object values so array
responses (static_info, quote, trades, candlesticks, etc.) no longer
violate the spec. The fix is centralized in tool_result so all tools
are covered without per-tool changes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@huacnlee huacnlee changed the title fix: stateless mode to fix Cursor session-not-found errors fix: resolve Cursor connection failures and structuredContent spec violation May 12, 2026
@huacnlee
huacnlee merged commit 987e3d1 into main May 12, 2026
1 check passed
@huacnlee
huacnlee deleted the fix-sse branch May 12, 2026 06:19
hogan-yuan pushed a commit that referenced this pull request Jul 6, 2026
…olation (#38)

## Summary

- Switch MCP server to stateless mode to fix session-not-found errors on
multi-instance deployments
- Fix `structuredContent` type violation that caused Cursor to reject
array tool responses

## Fix 1 — Stateless mode (`src/auth/mod.rs`)

**Root cause**: The server is designed for stateless multi-instance
deployment, but was using `stateful_mode: true` (the rmcp default) with
an in-memory `LocalSessionManager`. Under a load balancer, sessions
created on Instance A are unknown to Instance B, triggering a failure
chain in Cursor:

1. POST with cached `Mcp-Session-Id` → **404 Session not found**
(session lives on another instance)
2. Cursor falls back to legacy SSE V1 protocol (GET without session ID)
→ **400 Session ID is required**

Claude Code and Codex are unaffected because they re-initialize on every
connection rather than caching session IDs.

**Fix**: Replace `LocalSessionManager` with `NeverSessionManager` and
set `stateful_mode(false)`. The `Mcp-Session-Id` header is completely
ignored in stateless mode; each POST is handled independently with no
session state.

## Fix 2 — `structuredContent` type (`src/tools/mod.rs`)

**Root cause**: `tool_result` unconditionally assigned the parsed JSON
value to `structured_content`. Tools returning `Vec<T>` (e.g. `quote`,
`static_info`, `trades`, `candlesticks`, `participants`) serialize to a
JSON array. The MCP spec requires `structuredContent` to be a JSON
**object** (record); Cursor validates this strictly and rejects array
values with:

```
Invalid input: expected record, received array
```

**Fix**: Added `.filter(serde_json::Value::is_object)` so
`structured_content` is only set for JSON object responses. Array
responses leave it as `None`, which is valid per spec. The fix is
centralized in `tool_result` and covers all tools.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant