Skip to content

Commit a447cde

Browse files
author
Augment Agent
committed
Fix HTTP session inefficiency by sharing MultiIndexRunner across sessions
- Add optional 'runner' parameter to MCPServerConfig interface - Update createMCPServer() to accept and use optional runner parameter - Create shared MultiIndexRunner in HTTP server initialization - Pass shared runner to each per-session createMCPServer() call - Add comments explaining User-Agent update behavior with shared runner - Maintain backward compatibility (runner parameter is optional) - All existing tests pass This avoids redundant store.list() + store.loadSearch() calls for every HTTP session, which is especially wasteful for S3-backed stores. Related: https://slack.com/archives/C0ACX5SLML7/p1770850068455299
1 parent f7d6472 commit a447cde

File tree

2 files changed

+46
-11
lines changed

2 files changed

+46
-11
lines changed

src/clients/mcp-http-server.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
2828
import { createServer, IncomingMessage, ServerResponse } from "node:http";
2929
import { randomUUID, timingSafeEqual } from "node:crypto";
3030
import { createMCPServer, MCPServerConfig } from "./mcp-server.js";
31+
import { MultiIndexRunner } from "./multi-index-runner.js";
32+
import { buildClientUserAgent } from "../core/utils.js";
3133

3234
/**
3335
* HTTP error with status code for proper client error responses.
@@ -165,9 +167,24 @@ export async function createMCPHttpServer(
165167
// Store transports by session ID
166168
const transports: Map<string, StreamableHTTPServerTransport> = new Map();
167169

170+
// Create a shared MultiIndexRunner instance for all HTTP sessions
171+
// This avoids redundant store.list() + store.loadSearch() calls for every session
172+
const clientUserAgent = buildClientUserAgent("mcp");
173+
const sharedRunner = await MultiIndexRunner.create({
174+
store: config.store,
175+
indexNames: config.indexNames,
176+
searchOnly: config.searchOnly,
177+
clientUserAgent,
178+
});
179+
168180
// Create the underlying MCP server factory (creates new instance per session)
181+
// Each session gets its own MCP Server instance (required by MCP protocol),
182+
// but they all share the same MultiIndexRunner to avoid redundant store operations
169183
const createServerInstance = async (): Promise<Server> => {
170-
return createMCPServer(config);
184+
return createMCPServer({
185+
...config,
186+
runner: sharedRunner,
187+
});
171188
};
172189

173190
/**

src/clients/mcp-server.ts

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,14 @@ export interface MCPServerConfig {
7171
* @default "0.1.0"
7272
*/
7373
version?: string;
74+
/**
75+
* Optional shared MultiIndexRunner instance.
76+
* When provided, this runner is used instead of creating a new one.
77+
* This is useful for sharing a single runner across multiple MCP server instances
78+
* (e.g., in HTTP server with multiple sessions) to avoid redundant store operations.
79+
* @default undefined (creates a new runner)
80+
*/
81+
runner?: MultiIndexRunner;
7482
}
7583
/**
7684
* Create an MCP server instance.
@@ -94,16 +102,23 @@ export interface MCPServerConfig {
94102
export async function createMCPServer(
95103
config: MCPServerConfig
96104
): Promise<Server> {
97-
// Create shared runner for multi-index operations
98-
// Build User-Agent for analytics tracking
99-
const clientUserAgent = buildClientUserAgent("mcp");
100-
101-
const runner = await MultiIndexRunner.create({
102-
store: config.store,
103-
indexNames: config.indexNames,
104-
searchOnly: config.searchOnly,
105-
clientUserAgent,
106-
});
105+
// Use provided runner or create a new one
106+
let runner: MultiIndexRunner;
107+
if (config.runner) {
108+
// Use the shared runner provided by the caller
109+
runner = config.runner;
110+
} else {
111+
// Create a new runner for this server instance
112+
// Build User-Agent for analytics tracking
113+
const clientUserAgent = buildClientUserAgent("mcp");
114+
115+
runner = await MultiIndexRunner.create({
116+
store: config.store,
117+
indexNames: config.indexNames,
118+
searchOnly: config.searchOnly,
119+
clientUserAgent,
120+
});
121+
}
107122
const { indexNames, indexes } = runner;
108123
const searchOnly = !runner.hasFileOperations();
109124
// Format index list for tool descriptions
@@ -122,6 +137,9 @@ export async function createMCPServer(
122137
);
123138
// Use the SDK's oninitialized callback to capture MCP client info
124139
// This preserves the SDK's protocol version negotiation
140+
// Note: When using a shared runner (e.g., in HTTP server), this will update
141+
// the User-Agent for all sessions (last writer wins). This is acceptable
142+
// because the User-Agent is primarily for analytics tracking.
125143
server.oninitialized = () => {
126144
const clientInfo = server.getClientVersion();
127145
if (clientInfo) {

0 commit comments

Comments
 (0)