> ## Documentation Index
> Fetch the complete documentation index at: https://docs.getmcpulse.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Serving over HTTP

> Streamable HTTP builds a fresh McpServer per request. Wrap inside the factory, not around a long-lived instance.

A streamable-HTTP server builds a **new `McpServer` for every request**, so there is no long-lived instance to wrap. Wrap it inside the factory instead, beside the tool registration:

```ts theme={null}
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { watch } from "@mcpulse/sdk";

function createServer() {
  const server = new McpServer({ name: "my-server", version: "1.0.0" });

  // … your registerTool calls …

  return watch(server, { key: process.env.MCPULSE_KEY });
}
```

`watch` returns the same server, so `return watch(…)` is the whole change.

## Calling watch per request is expected and cheap

This is the part that looks wrong and is not. The session and its buffer are held **per process**, shared by every `watch()` in it that reports to the same place — so calling `watch()` a thousand times does not open a thousand sessions.

Without that sharing, each request would open a session of its own, no two calls would ever land in the same session, and **no retry could ever be detected**. First-call success would read 100% forever, which is worse than reading nothing.

<Warning>
  **Session state must be process-scoped.** This is the gotcha everyone hits: assuming the `watch()` wrapper instance persists between requests. It does not — a streamable-HTTP server builds a fresh `McpServer` per request, so the wrapper is new every time and only the *stream behind it* is shared.

  If session state is not process-scoped, every request becomes its own session. Retries can never be detected, because a retry is the same tool twice inside one session and there is only ever one call in each. First-call success then reads **100% forever**, however badly the server is doing — the one number this product exists to tell the truth about.

  Do not generate your own session id per request and pass it in. There is no option to do so, deliberately.
</Warning>

## Wrapping the outer HTTP app does nothing

MCPulse instruments the **MCP server**, not your HTTP framework. It hooks `initialize` and `tools/call` on the low-level protocol server, which is where the tool name, the outcome and the result size actually are. There is nothing useful to read at the Express or Hono layer.

## Related

* [Serving over stdio](/sdk/stdio)
* [Sessions](/metrics/sessions) — why the session boundary matters
* [First-call success](/metrics/first-call-success) — what breaks if sessions fragment


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.