McpServer for every request, so there is no long-lived instance to wrap. Wrap it inside the factory instead, beside the tool registration:
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 everywatch() 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.
Wrapping the outer HTTP app does nothing
MCPulse instruments the MCP server, not your HTTP framework. It hooksinitialize 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
- Sessions — why the session boundary matters
- First-call success — what breaks if sessions fragment