diff --git a/packages/sunpeak/src/mcp/production-server.ts b/packages/sunpeak/src/mcp/production-server.ts index c4f3298..63b93f4 100644 --- a/packages/sunpeak/src/mcp/production-server.ts +++ b/packages/sunpeak/src/mcp/production-server.ts @@ -143,6 +143,17 @@ export interface ProductionServerConfig { * where holding open SSE connections is unreliable. Defaults to `true`. */ enableJsonResponse?: boolean; + /** + * Override the client name used for domain resolution (e.g. `'chatgpt'`). + * + * Normally, client name is detected from the MCP initialization handshake. + * In stateless/serverless environments where each request creates a fresh + * server instance, the client name won't be available for non-initialize + * requests. Set this to ensure proper domain resolution for resources. + * + * Common values: `'chatgpt'`, `'openai-mcp'`, `'claude'` + */ + clientName?: string; } /** @@ -194,6 +205,7 @@ export function createProductionMcpServer(config: ProductionServerConfig): McpSe tools, resources, serverUrl, + clientName: configClientName, } = config; const mcpServer = new McpServer( @@ -218,9 +230,11 @@ export function createProductionMcpServer(config: ProductionServerConfig): McpSe // Read callbacks close over this variable to resolve host-specific domain maps. // Also update listing-level _meta on all resources so that `resources/list` // returns the resolved domain (hosts like ChatGPT check domain from the listing). - let clientName: string | undefined; + // In stateless/serverless mode, use configClientName as the default since + // oninitialized won't fire for non-initialize requests. + let clientName: string | undefined = configClientName; mcpServer.server.oninitialized = () => { - clientName = mcpServer.server.getClientVersion()?.name; + clientName = mcpServer.server.getClientVersion()?.name ?? configClientName; for (const handle of resourceHandles) { const currentMeta = handle.metadata?._meta as Record | undefined; @@ -269,6 +283,7 @@ export function createProductionMcpServer(config: ProductionServerConfig): McpSe if (typeof result === 'string') { return { content: [{ type: 'text' as const, text: result }] }; } + return result; }; };