Skip to content

feat: make HTTP streamable stateless mode configurable via FASTMCP_STATELESS env#212

Open
SpongeRobert wants to merge 1 commit intofirecrawl:mainfrom
SpongeRobert:configurable-stateless-mode
Open

feat: make HTTP streamable stateless mode configurable via FASTMCP_STATELESS env#212
SpongeRobert wants to merge 1 commit intofirecrawl:mainfrom
SpongeRobert:configurable-stateless-mode

Conversation

@SpongeRobert
Copy link
Copy Markdown

Summary

Makes the HTTP streamable stateless option configurable via FASTMCP_STATELESS env var, matching the convention already used by the underlying firecrawl-fastmcp / punkpeye/fastmcp framework. Default behavior is preserved (stateless: true).

Motivation

MCP clients built on the official modelcontextprotocol/go-sdk (StreamableClientTransport) expect stateful session semantics. When connecting to a firecrawl-mcp instance running in HTTP_STREAMABLE_SERVER mode, the client receives a Mcp-Session-Id header in the initialize response, then passes it back on subsequent requests — but because the server is stateless, it replies session not found and the connection fails.

This prevents self-hosted Firecrawl integrations with Go-based MCP clients (e.g. picoclaw-based agents) from working out of the box. Downstream users are currently forced to patch the compiled JS to work around it.

Change

src/index.ts:1259 — single-line change:

-      stateless: true,
+      stateless: process.env.FASTMCP_STATELESS !== 'false',

Plus a short README note explaining when to set FASTMCP_STATELESS=false.

Backward compatible — default remains true (stateless). Stateful mode is opt-in via FASTMCP_STATELESS=false.

Testing

  • Built locally (pnpm install --frozen-lockfile && pnpm run build); compiled JS contains the expected FASTMCP_STATELESS !== 'false' expression.
  • Runtime verification: with FASTMCP_STATELESS=false, the server log changes from Starting server in stateless mode to server is running on HTTP Stream (FastMCP's stateful path).
  • Integration with modelcontextprotocol/go-sdk v1.4.1 StreamableClientTransport: client successfully connects, lists all tools (12), and calls firecrawl_scrape / firecrawl_crawl / firecrawl_search / firecrawl_map end-to-end after applying this change.

Alternatives considered

  • CLI flag: rejected — no CLI arg parser in current firecrawl-mcp.
  • Flip default to stateful: rejected — potential breaking change for existing HTTP streamable users (serverless / LB'd deployments).
  • FASTMCP_STATELESS env: chosen — matches the documented FastMCP convention (see punkpeye/fastmcp README § Stateless Mode), backward compatible, no breaking changes.

…LESS

Follows the env var convention already used by `firecrawl-fastmcp` /
`punkpeye/fastmcp`. Default behavior is preserved (`stateless: true`).

## Motivation

MCP clients built on the official `modelcontextprotocol/go-sdk`
(`StreamableClientTransport`) expect stateful session semantics. When
connecting to a firecrawl-mcp instance running in `HTTP_STREAMABLE_SERVER`
mode, the client receives a `Mcp-Session-Id` header in the initialize
response, passes it back on subsequent requests — but because the server
is stateless, it replies "session not found" and the connection fails.

This prevents self-hosted Firecrawl integrations with Go-based MCP clients
(e.g. picoclaw / internal AI agents) from working out of the box.
Downstream users are forced to patch the compiled JS.

## Change

`src/index.ts:1259`: `stateless: true` → `stateless: process.env.FASTMCP_STATELESS !== 'false'`

Plus a short README note explaining when to set `FASTMCP_STATELESS=false`.

Backward compatible — default remains stateless. Stateful mode is opt-in.

## Testing

- Built locally (`pnpm run build`) — compiled JS contains the expected
  `FASTMCP_STATELESS !== 'false'` expression.
- Runtime with `FASTMCP_STATELESS=false`: server log changes from
  "Starting server in stateless mode" to "server is running on HTTP Stream"
  (FastMCP's stateful path).
- Integration with `modelcontextprotocol/go-sdk` v1.4.1 `StreamableClientTransport`:
  client successfully connects, lists tools (12), and calls scrape/crawl
  tools end-to-end after this change.
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