@@ -2,6 +2,8 @@ import { createServer as createNodeServer } from 'node:http';
22import { randomUUID } from 'node:crypto' ;
33
44import { createApp , createRouter , defineEventHandler , getQuery , readRawBody , setResponseStatus , toNodeListener } from 'h3' ;
5+ import { writeFileSync } from 'node:fs' ;
6+ import { join } from 'node:path' ;
57import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js' ;
68import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js' ;
79import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js' ;
@@ -278,6 +280,12 @@ export async function startHttpServer(
278280 console . log ( `MCP (Streamable HTTP) listening → http://${ opts . host } :${ opts . port } ${ opts . endpoint } ` ) ;
279281 // eslint-disable-next-line no-console
280282 console . log ( `Log ingest endpoint → http://${ opts . host } :${ opts . port } ${ opts . logsRoute } ` ) ;
283+ // Expose ingest discovery for other MCP instances in the same process tree
284+ try {
285+ process . env . BROWSER_ECHO_INGEST_BASE = `http://${ opts . host } :${ opts . port } ` ;
286+ process . env . BROWSER_ECHO_LOGS_ROUTE = String ( opts . logsRoute ) ;
287+ process . env . BROWSER_ECHO_INGEST_OWNER = '1' ;
288+ } catch { }
281289}
282290
283291/** Start a minimal HTTP server exposing ONLY:
@@ -365,6 +373,12 @@ export async function startIngestOnlyServer(
365373 if ( res && res . ok ) {
366374 // eslint-disable-next-line no-console
367375 console . error ( `Ingest server already running at ${ base } ${ opts . logsRoute } . Reusing existing instance.` ) ;
376+ // Expose discovery for non-owner instances
377+ try {
378+ process . env . BROWSER_ECHO_INGEST_BASE = base ;
379+ process . env . BROWSER_ECHO_LOGS_ROUTE = String ( opts . logsRoute ) ;
380+ process . env . BROWSER_ECHO_INGEST_OWNER = '0' ;
381+ } catch { }
368382 return ; // Treat as success
369383 }
370384 } catch { }
@@ -376,6 +390,18 @@ export async function startIngestOnlyServer(
376390
377391 // eslint-disable-next-line no-console
378392 console . error ( `Log ingest endpoint → http://${ opts . host } :${ actualPort } ${ opts . logsRoute } ` ) ;
393+ // Write project-local discovery file for frameworks/tools to find ingest
394+ try {
395+ const discPath = join ( process . cwd ( ) , '.browser-echo-mcp.json' ) ;
396+ const payload = { url : `http://${ opts . host } :${ actualPort } ` , route : String ( opts . logsRoute ) , timestamp : Date . now ( ) } ;
397+ writeFileSync ( discPath , JSON . stringify ( payload ) ) ;
398+ } catch { }
399+ // Expose discovery for owner instance
400+ try {
401+ process . env . BROWSER_ECHO_INGEST_BASE = `http://${ opts . host } :${ actualPort } ` ;
402+ process . env . BROWSER_ECHO_LOGS_ROUTE = String ( opts . logsRoute ) ;
403+ process . env . BROWSER_ECHO_INGEST_OWNER = '1' ;
404+ } catch { }
379405}
380406
381407// Removed project JSON discovery in single-server mode
@@ -409,6 +435,13 @@ function createLogIngestRoutes(store: LogStore, logsRoute: `/${string}`) {
409435 const hdrs = event . node . req . headers ;
410436 const projectHeader = ( hdrs [ 'x-browser-echo-project-name' ] || hdrs [ 'x-project-name' ] || hdrs [ 'x-project' ] || '' ) as string | string [ ] | undefined ;
411437 const projectName = Array . isArray ( projectHeader ) ? String ( projectHeader [ 0 ] || '' ) : String ( projectHeader || '' ) ;
438+ // Special command: remote clear request
439+ const isClear = payload . entries . length === 1 && String ( payload . entries [ 0 ] ?. text || '' ) === '__BROWSER_ECHO_CLEAR__' ;
440+ if ( isClear ) {
441+ store . clear ( { session : sid ? String ( sid ) . slice ( 0 , 8 ) : undefined , scope : 'hard' , project : projectName || undefined } ) ;
442+ setResponseStatus ( event , 204 ) ;
443+ return '' ;
444+ }
412445 for ( const entry of payload . entries as Array < { level : BrowserLogLevel | string ; text : string ; time ?: number ; stack ?: string ; source ?: string ; } > ) {
413446 const level = normalizeLevel ( entry . level ) ;
414447 store . append ( {
0 commit comments