@agimon-ai/log-sink-mcp 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,70 @@
1
+ import{existsSync as e}from"node:fs";import t,{dirname as n,join as r}from"node:path";import{fileURLToPath as i}from"node:url";import{PortRegistryService as a}from"@agimon-ai/foundation-port-registry";import{Container as o,ContainerModule as s,inject as c,injectable as l,unmanaged as u}from"inversify";import"reflect-metadata";import{spawn as d}from"node:child_process";import f,{mkdir as p}from"node:fs/promises";import{and as m,count as h,desc as g,eq as _,gte as v,lte as y,sql as b}from"drizzle-orm";import{index as x,integer as S,sqliteTable as ee,text as C}from"drizzle-orm/sqlite-core";import{ulid as te}from"ulidx";import{tmpdir as w}from"node:os";import T from"better-sqlite3";import{drizzle as E}from"drizzle-orm/better-sqlite3";import{Server as D}from"@modelcontextprotocol/sdk/server/index.js";import{CallToolRequestSchema as O,ListToolsRequestSchema as k}from"@modelcontextprotocol/sdk/types.js";import{StdioServerTransport as A}from"@modelcontextprotocol/sdk/server/stdio.js";function j(e,t,n,r){var i=arguments.length,a=i<3?t:r===null?r=Object.getOwnPropertyDescriptor(t,n):r,o;if(typeof Reflect==`object`&&typeof Reflect.decorate==`function`)a=Reflect.decorate(e,t,n,r);else for(var s=e.length-1;s>=0;s--)(o=e[s])&&(a=(i<3?o(a):i>3?o(t,n,a):o(t,n))||a);return i>3&&a&&Object.defineProperty(t,n,a),a}let M=class{defaultTimeout=5e3;async check(e,t=this.defaultTimeout){try{let n=`http://localhost:${e}/health`,r=new AbortController,i=setTimeout(()=>r.abort(),t);try{let t=await fetch(n,{signal:r.signal,headers:{"Content-Type":`application/json`}});if(clearTimeout(i),!t.ok)return{healthy:!1,error:`HTTP error ${t.status}: ${t.statusText}`};let a=await t.json();return a.status===`healthy`?{healthy:!0,port:e,serviceName:a.serviceName??a.service}:{healthy:!1,error:`Unexpected health status: ${a.status||`unknown`}`}}catch(e){return clearTimeout(i),e instanceof Error?e.name===`AbortError`?{healthy:!1,error:`Health check timed out after ${t}ms`}:`code`in e&&e.code===`ECONNREFUSED`?{healthy:!1,error:`Connection refused - server not running`}:{healthy:!1,error:`Network error: ${e.message}`}:{healthy:!1,error:`Unknown error: ${String(e)}`}}}catch(e){return{healthy:!1,error:e instanceof Error?e.message:String(e)}}}};M=j([l()],M);const N={LogStorageService:Symbol.for(`LogStorageService`),LogQueryService:Symbol.for(`LogQueryService`),LogSearchService:Symbol.for(`LogSearchService`),LogRetentionService:Symbol.for(`LogRetentionService`),HttpServerHealthCheck:Symbol.for(`HttpServerHealthCheck`),HttpServerManager:Symbol.for(`HttpServerManager`),Database:Symbol.for(`Database`),Logger:Symbol.for(`Logger`),HttpServer:Symbol.for(`HttpServer`),McpServer:Symbol.for(`McpServer`)};function P(e,t){if(typeof Reflect==`object`&&typeof Reflect.metadata==`function`)return Reflect.metadata(e,t)}function F(e,t){return function(n,r){t(n,r,e)}}const I=i(import.meta.url),L=t.dirname(I),R=[`pnpm-workspace.yaml`,`nx.json`,`.git`];function z(n=process.cwd()){let r=t.resolve(n);for(;;){for(let n of R)if(e(t.join(r,n)))return r;let n=t.dirname(r);if(n===r)return process.cwd();r=n}}let B=class{repositoryPath;environment;serviceName=`log-sink-mcp-http`;serviceType=`tool`;host=`127.0.0.1`;registryPath;portRegistry;constructor(e,n,r){this.healthCheck=e,this.repositoryPath=z(n?t.resolve(n):process.cwd()),this.environment=process.env.NODE_ENV||`development`,this.registryPath=r||process.env.PORT_REGISTRY_PATH,this.portRegistry=new a(this.registryPath)}async getRegistration(){let e=await this.portRegistry.getPort({repositoryPath:this.repositoryPath,serviceName:this.serviceName,serviceType:this.serviceType,environment:this.environment});return!e.success||!e.record?null:{port:e.record.port,pid:e.record.pid,environment:e.record.environment??this.environment,host:e.record.host,metadata:e.record.metadata}}async releaseService(e){let t=await this.portRegistry.releasePort({repositoryPath:this.repositoryPath,serviceName:this.serviceName,serviceType:this.serviceType,environment:this.environment,...e?{pid:e}:{}});if(!t.success&&!t.error?.includes(`No matching registry entry`))throw Error(t.error||`Failed to release registry entry`)}async findAvailablePort(e){let t=await this.portRegistry.findAvailablePort({repositoryPath:this.repositoryPath,serviceName:this.serviceName,serviceType:this.serviceType,environment:this.environment,host:this.host,preferredPort:e});if(!t.success||!t.port)throw Error(`No available port found from ${e}: ${t.error}`);return t.port}async fileExists(e){try{return await f.access(e),!0}catch{return!1}}async startHttpServer(e,n){let r=[t.resolve(L,`cli.mjs`),t.resolve(L,`..`,`dist`,`cli.mjs`),t.resolve(L,`..`,`..`,`dist`,`cli.mjs`)],i=[t.resolve(L,`..`,`cli.ts`),t.resolve(L,`..`,`..`,`src`,`cli.ts`)],a;for(let e of r)if(await this.fileExists(e)){a=e;break}let o;if(!a){for(let e of i)if(await this.fileExists(e)){o=e;break}}let s,c;if(a)s=process.execPath,c=[a,`http-serve`,`--port`,e.toString()];else if(o)s=process.execPath,c=[`--import`,`tsx`,o,`http-serve`,`--port`,e.toString()];else throw Error(`Cannot find log-sink MCP CLI in any expected location`);let l=t.resolve(n);await f.mkdir(t.dirname(l),{recursive:!0});let u=d(s,[...c,`--db-path`,l],{detached:!0,stdio:`ignore`,env:{...process.env,...this.registryPath?{PORT_REGISTRY_PATH:this.registryPath}:{},NODE_ENV:this.environment}});u.unref();let p=u.pid;if(!p)throw Error(`Failed to spawn HTTP server - no PID returned`);return p}async waitForRegisteredService(e,t,n=10,r=250){for(let i=0;i<n;i+=1){let n=await this.getRegistration();if(n?.pid===t&&n.port===e)return n;await new Promise(e=>setTimeout(e,r))}return null}async waitForServerStartup(e=4e3){await new Promise(t=>setTimeout(t,e))}async killProcess(e){try{process.kill(e,0),process.kill(e,`SIGTERM`),await new Promise(e=>setTimeout(e,1e3));try{process.kill(e,0),process.kill(e,`SIGKILL`)}catch{}}catch{}}async ensureRunning(e=3100,t=`./logs/session.db`){try{let n=await this.getRegistration();if(n){let e=await this.healthCheck.check(n.port);if(e.healthy&&e.serviceName===this.serviceName)return{running:!0,port:n.port,pid:n.pid};n.pid&&await this.killProcess(n.pid),await this.releaseService(n.pid)}let r=e;for(let e=0;e<3;e+=1){let e=await this.findAvailablePort(r),n=await this.startHttpServer(e,t);await this.waitForServerStartup();let i=await this.healthCheck.check(e);if(!i.healthy||i.serviceName!==this.serviceName){await this.killProcess(n),await this.releaseService(n),r=e+1;continue}if(!await this.waitForRegisteredService(e,n)){await this.killProcess(n),await this.releaseService(n),r=e+1;continue}return{running:!0,port:e,pid:n}}return{running:!1,error:`Failed to start log-sink-mcp HTTP server after retries`}}catch(e){return{running:!1,error:e instanceof Error?e.message:String(e)}}}async stop(){try{let e=await this.getRegistration();if(!e)return!1;try{if(e.pid&&process.kill(e.pid,`SIGTERM`),await new Promise(e=>setTimeout(e,1e3)),e.pid)try{process.kill(e.pid,0),process.kill(e.pid,`SIGKILL`)}catch{}}catch{}return await this.releaseService(e.pid),!0}catch(e){throw Error(`Failed to stop HTTP server: ${e instanceof Error?e.message:String(e)}`)}}async getStatus(){try{let e=await this.getRegistration();if(!e)return{running:!1,error:`No HTTP server registered`};let t=await this.healthCheck.check(e.port);return t.healthy&&t.serviceName===this.serviceName?{running:!0,port:e.port,pid:e.pid}:{running:!1,port:e.port,pid:e.pid,error:t.error}}catch(e){return{running:!1,error:e instanceof Error?e.message:String(e)}}}};B=j([l(),F(0,c(N.HttpServerHealthCheck)),F(1,u()),F(2,u()),P(`design:paramtypes`,[Object,String,String])],B);const V=ee(`logs`,{id:C(`id`).primaryKey().$defaultFn(()=>te()),timestamp:S(`timestamp`,{mode:`timestamp`}).$defaultFn(()=>new Date).notNull(),level:C(`level`).notNull(),message:C(`message`).notNull(),traceId:C(`trace_id`),spanId:C(`span_id`),parentSpanId:C(`parent_span_id`),service:C(`service`).notNull(),hostname:C(`hostname`),pid:S(`pid`),metadata:C(`metadata`,{mode:`json`}),errorType:C(`error_type`),errorMessage:C(`error_message`),errorStack:C(`error_stack`),createdAt:S(`created_at`,{mode:`timestamp`}).$defaultFn(()=>new Date).notNull()},e=>({timestampIdx:x(`logs_timestamp_idx`).on(e.timestamp),levelIdx:x(`logs_level_idx`).on(e.level),traceIdIdx:x(`logs_trace_id_idx`).on(e.traceId),spanIdIdx:x(`logs_span_id_idx`).on(e.spanId),parentSpanIdIdx:x(`logs_parent_span_id_idx`).on(e.parentSpanId),serviceIdx:x(`logs_service_idx`).on(e.service),errorTypeIdx:x(`logs_error_type_idx`).on(e.errorType)}));let H=class{constructor(e){this.storage=e}async getDb(){return await this.storage.ensureInitialized(),this.storage.getDatabaseClient()}async filterByLevel(e,t=100){try{let n=await this.getDb(),r=Array.isArray(e)?e:[e];return r.length===1?await n.select().from(V).where(_(V.level,r[0])).orderBy(g(V.timestamp)).limit(t):await n.select().from(V).where(b`${V.level} IN (${b.join(r.map(e=>b`${e}`),b`, `)})`).orderBy(g(V.timestamp)).limit(t)}catch(e){throw Error(`Failed to filter by level: ${e instanceof Error?e.message:String(e)}`)}}async filterByTrace(e){try{return await(await this.getDb()).select().from(V).where(_(V.traceId,e)).orderBy(V.timestamp)}catch(e){throw Error(`Failed to filter by trace: ${e instanceof Error?e.message:String(e)}`)}}async filterByTimeRange(e,t){try{return await(await this.getDb()).select().from(V).where(m(v(V.timestamp,e),y(V.timestamp,t))).orderBy(V.timestamp)}catch(e){throw Error(`Failed to filter by time range: ${e instanceof Error?e.message:String(e)}`)}}async filterByService(e){try{let t=await this.getDb(),n=Array.isArray(e)?e:[e];return n.length===1?await t.select().from(V).where(_(V.service,n[0])).orderBy(V.timestamp):await t.select().from(V).where(b`${V.service} IN (${b.join(n.map(e=>b`${e}`),b`, `)})`).orderBy(V.timestamp)}catch(e){throw Error(`Failed to filter by service: ${e instanceof Error?e.message:String(e)}`)}}async getTraceTimeline(e){try{let t=await this.filterByTrace(e),n=[];for(let e=0;e<t.length;e++){let r=t[e],i=t[e+1];n.push({timestamp:r.timestamp,service:r.service,spanId:r.spanId??null,parentSpanId:r.parentSpanId??null,level:r.level,message:r.message,duration:i?i.timestamp.getTime()-r.timestamp.getTime():void 0})}return n}catch(e){throw Error(`Failed to get trace timeline: ${e instanceof Error?e.message:String(e)}`)}}async getStatistics(e,t=`both`){try{let n=(await this.getDb()).select({service:V.service,level:V.level,count:h()}).from(V);return e&&(n=n.where(m(v(V.timestamp,e.start),y(V.timestamp,e.end)))),n=t===`level`?n.groupBy(V.level):t===`service`?n.groupBy(V.service):n.groupBy(V.service,V.level),(await n).map(e=>({service:e.service,level:e.level,count:Number(e.count)}))}catch(e){throw Error(`Failed to get statistics: ${e instanceof Error?e.message:String(e)}`)}}async getActiveServices(){try{return(await(await this.getDb()).select({service:V.service}).from(V).groupBy(V.service)).map(e=>e.service)}catch(e){throw Error(`Failed to get active services: ${e instanceof Error?e.message:String(e)}`)}}async countByLevel(e){try{let t=await this.getDb(),n=Array.isArray(e)?e:[e],r;return r=n.length===1?await t.select({count:h()}).from(V).where(_(V.level,n[0])):await t.select({count:h()}).from(V).where(b`${V.level} IN (${b.join(n.map(e=>b`${e}`),b`, `)})`),Number(r[0]?.count??0)}catch(e){throw Error(`Failed to count by level: ${e instanceof Error?e.message:String(e)}`)}}async countByService(e){try{let t=await this.getDb(),n=Array.isArray(e)?e:[e],r;return r=n.length===1?await t.select({count:h()}).from(V).where(_(V.service,n[0])):await t.select({count:h()}).from(V).where(b`${V.service} IN (${b.join(n.map(e=>b`${e}`),b`, `)})`),Number(r[0]?.count??0)}catch(e){throw Error(`Failed to count by service: ${e instanceof Error?e.message:String(e)}`)}}async countByTrace(e){try{let t=await(await this.getDb()).select({count:h()}).from(V).where(_(V.traceId,e));return Number(t[0]?.count??0)}catch(e){throw Error(`Failed to count by trace: ${e instanceof Error?e.message:String(e)}`)}}async queryLogs(e){try{let t=await this.getDb(),{level:n,service:r,traceId:i,limit:a=25,offset:o=0}=e,s=[];if(n){let e=Array.isArray(n)?n:[n];e.length===1?s.push(_(V.level,e[0])):s.push(b`${V.level} IN (${b.join(e.map(e=>b`${e}`),b`, `)})`)}r&&s.push(_(V.service,r)),i&&s.push(_(V.traceId,i));let c=s.length>0?m(...s):void 0,l=c?await t.select({count:h()}).from(V).where(c):await t.select({count:h()}).from(V),u=Number(l[0]?.count??0),d=t.select().from(V);return c&&(d=d.where(c)),{logs:await d.orderBy(g(V.timestamp)).limit(a).offset(o),total:u}}catch(e){throw Error(`Failed to query logs: ${e instanceof Error?e.message:String(e)}`)}}};H=j([l(),F(0,c(N.LogStorageService)),P(`design:paramtypes`,[Object])],H);let U=class{constructor(e){this.storage=e}async getDb(){return await this.storage.ensureInitialized(),this.storage.getDb()}async cleanupOldLogs(e){try{let t=await this.getDb(),n=new Date;return n.setDate(n.getDate()-e),(await t.delete(V).where(y(V.timestamp,n))).changes??0}catch(e){throw Error(`Failed to cleanup old logs: ${e instanceof Error?e.message:String(e)}`)}}async clearAllLogs(){try{return await this.storage.deleteAllLogs()}catch(e){throw Error(`Failed to clear all logs: ${e instanceof Error?e.message:String(e)}`)}}};U=j([l(),F(0,c(N.LogStorageService)),P(`design:paramtypes`,[Object])],U);let W=class{constructor(e){this.storage=e}async getSqlite(){return await this.storage.ensureInitialized(),this.storage.getSqliteClient()}buildSearchSql(e,t={}){let n=e.replace(/[^\w\s\-"*]/g,``).trim();if(!n)throw Error(`Search query is empty after sanitization`);let r=`WHERE logs_search MATCH ?`,i=[n];if(t.service){let e=Array.isArray(t.service)?t.service:[t.service];r+=` AND logs.service IN (${e.map(()=>`?`).join(`,`)})`,i.push(...e)}if(t.level){let e=Array.isArray(t.level)?t.level:[t.level];r+=` AND logs.level IN (${e.map(()=>`?`).join(`,`)})`,i.push(...e)}return t.startTime&&(r+=` AND logs.timestamp >= ?`,i.push(Math.floor(t.startTime.getTime()/1e3))),t.endTime&&(r+=` AND logs.timestamp <= ?`,i.push(Math.floor(t.endTime.getTime()/1e3))),{whereSql:r,params:i}}mapSearchRow(e){return{id:e.id,timestamp:new Date(e.timestamp*1e3),level:e.level,message:e.message,traceId:e.traceId,spanId:e.spanId,parentSpanId:e.parentSpanId,service:e.service,hostname:e.hostname,pid:e.pid,metadata:e.metadata?JSON.parse(e.metadata):null,errorType:e.errorType,errorMessage:e.errorMessage,errorStack:e.errorStack,createdAt:new Date(e.createdAt*1e3),rank:e.rank}}async searchLogs(e,t={},n=100){let{results:r}=await this.searchLogsPaginated(e,{...t,offset:0},n);return r}async searchLogsPaginated(e,t={},n=100){try{let r=await this.getSqlite(),{whereSql:i,params:a}=this.buildSearchSql(e,t),o=t.offset??0,s=`
2
+ SELECT COUNT(*) as total
3
+ FROM logs_search
4
+ JOIN logs ON logs.rowid = logs_search.rowid
5
+ ${i}
6
+ `,c=`
7
+ SELECT
8
+ logs.id as id,
9
+ logs.timestamp as timestamp,
10
+ logs.level as level,
11
+ logs.message as message,
12
+ logs.trace_id as traceId,
13
+ logs.span_id as spanId,
14
+ logs.parent_span_id as parentSpanId,
15
+ logs.service as service,
16
+ logs.hostname as hostname,
17
+ logs.pid as pid,
18
+ logs.metadata as metadata,
19
+ logs.error_type as errorType,
20
+ logs.error_message as errorMessage,
21
+ logs.error_stack as errorStack,
22
+ logs.created_at as createdAt,
23
+ logs_search.rank as rank
24
+ FROM logs_search
25
+ JOIN logs ON logs.rowid = logs_search.rowid
26
+ ${i}
27
+ ORDER BY rank
28
+ LIMIT ?
29
+ OFFSET ?
30
+ `,l=r.prepare(s).get(...a);return{results:r.prepare(c).all(...a,n,o).map(e=>this.mapSearchRow(e)),total:Number(l.total??0)}}catch(e){throw Error(`Failed to search logs: ${e instanceof Error?e.message:String(e)}`)}}rankResults(e){return e.sort((e,t)=>e.rank-t.rank)}};W=j([l(),F(0,c(N.LogStorageService)),P(`design:paramtypes`,[Object])],W);let G=class{db=null;sqlite=null;async initializeDatabase(e,t=!1){try{let r=t?`:memory:`:e;t||await p(n(r),{recursive:!0}),this.sqlite=new T(r),this.sqlite.pragma(`journal_mode = WAL`),this.sqlite.pragma(`foreign_keys = ON`),this.db=E(this.sqlite,{schema:{logs:V}}),this.sqlite.exec(`
31
+ CREATE TABLE IF NOT EXISTS logs (
32
+ id text PRIMARY KEY NOT NULL,
33
+ timestamp integer NOT NULL,
34
+ level text NOT NULL,
35
+ message text NOT NULL,
36
+ trace_id text,
37
+ span_id text,
38
+ parent_span_id text,
39
+ service text NOT NULL,
40
+ hostname text,
41
+ pid integer,
42
+ metadata text,
43
+ error_type text,
44
+ error_message text,
45
+ error_stack text,
46
+ created_at integer NOT NULL
47
+ )
48
+ `),this.sqlite.exec(`CREATE INDEX IF NOT EXISTS logs_timestamp_idx ON logs (timestamp)`),this.sqlite.exec(`CREATE INDEX IF NOT EXISTS logs_level_idx ON logs (level)`),this.sqlite.exec(`CREATE INDEX IF NOT EXISTS logs_trace_id_idx ON logs (trace_id)`),this.sqlite.exec(`CREATE INDEX IF NOT EXISTS logs_span_id_idx ON logs (span_id)`),this.sqlite.exec(`CREATE INDEX IF NOT EXISTS logs_parent_span_id_idx ON logs (parent_span_id)`),this.sqlite.exec(`CREATE INDEX IF NOT EXISTS logs_service_idx ON logs (service)`),this.sqlite.exec(`CREATE INDEX IF NOT EXISTS logs_error_type_idx ON logs (error_type)`),this.sqlite.exec(`
49
+ CREATE VIRTUAL TABLE IF NOT EXISTS logs_search USING fts5(
50
+ id UNINDEXED,
51
+ message,
52
+ error_message,
53
+ content='logs',
54
+ content_rowid='rowid'
55
+ )
56
+ `),this.sqlite.exec(`
57
+ CREATE TRIGGER IF NOT EXISTS logs_ai AFTER INSERT ON logs BEGIN
58
+ INSERT INTO logs_search(rowid, id, message, error_message)
59
+ VALUES (new.rowid, new.id, new.message, new.error_message);
60
+ END;
61
+ `),this.sqlite.exec(`
62
+ CREATE TRIGGER IF NOT EXISTS logs_ad AFTER DELETE ON logs BEGIN
63
+ DELETE FROM logs_search WHERE rowid = old.rowid;
64
+ END;
65
+ `),this.sqlite.exec(`
66
+ CREATE TRIGGER IF NOT EXISTS logs_au AFTER UPDATE ON logs BEGIN
67
+ UPDATE logs_search SET message = new.message, error_message = new.error_message
68
+ WHERE rowid = new.rowid;
69
+ END;
70
+ `)}catch(e){throw Error(`Failed to initialize database: ${e instanceof Error?e.message:String(e)}`)}}getDefaultDbPath(){return r(w(),`log-sink-mcp`,`session.db`)}isInitialized(){return this.db!==null}async ensureInitialized(){this.db||await this.initializeDatabase(this.getDefaultDbPath())}getDb(){if(!this.db)throw Error(`Database not initialized. Call initializeDatabase() first.`);return this.db}getDatabaseClient(){return this.getDb()}getSqliteClient(){if(!this.sqlite)throw Error(`Database not initialized. Call initializeDatabase() first.`);return this.sqlite}async insertLog(e){try{let t=await this.getDb().insert(V).values(e).returning();if(t.length===0)throw Error(`Failed to insert log - no record returned`);return t[0]}catch(e){throw Error(`Failed to insert log: ${e instanceof Error?e.message:String(e)}`)}}async insertBatch(e){try{return e.length===0?[]:await this.getDb().insert(V).values(e).returning()}catch(e){throw Error(`Failed to insert batch: ${e instanceof Error?e.message:String(e)}`)}}async queryLogs(e={}){try{let t=this.getDb(),n=[];if(e.level){let t=Array.isArray(e.level)?e.level:[e.level];t.length===1?n.push(_(V.level,t[0])):n.push(b`${V.level} IN (${b.join(t.map(e=>b`${e}`),b`, `)})`)}if(e.service){let t=Array.isArray(e.service)?e.service:[e.service];t.length===1?n.push(_(V.service,t[0])):n.push(b`${V.service} IN (${b.join(t.map(e=>b`${e}`),b`, `)})`)}e.traceId&&n.push(_(V.traceId,e.traceId)),e.spanId&&n.push(_(V.spanId,e.spanId)),e.errorType&&n.push(_(V.errorType,e.errorType)),e.startTime&&n.push(v(V.timestamp,e.startTime)),e.endTime&&n.push(y(V.timestamp,e.endTime));let r=n.length>0?m(...n):void 0,i=e.limit??100,a=e.offset??0,o=t.select().from(V);return r&&(o=o.where(r)),await o.orderBy(g(V.timestamp)).limit(i).offset(a)}catch(e){throw Error(`Failed to query logs: ${e instanceof Error?e.message:String(e)}`)}}async deleteAllLogs(){try{if(!this.sqlite)throw Error(`Database not initialized`);return this.sqlite.prepare(`DELETE FROM logs`).run().changes}catch(e){throw Error(`Failed to delete all logs: ${e instanceof Error?e.message:String(e)}`)}}async disconnect(){this.sqlite&&(this.sqlite.close(),this.sqlite=null,this.db=null)}};G=j([l()],G);const ne=new s(e=>{e.bind(N.LogStorageService).to(G).inSingletonScope(),e.bind(N.LogQueryService).to(H).inSingletonScope(),e.bind(N.LogSearchService).to(W).inSingletonScope(),e.bind(N.LogRetentionService).to(U).inSingletonScope(),e.bind(N.HttpServerHealthCheck).to(M).inSingletonScope(),e.bind(N.HttpServerManager).to(B).inSingletonScope()});function K(){let e=new o;return e.load(ne),e}var q=class e{static TOOL_NAME=`analyze_errors`;queryService;constructor(e){this.queryService=e.get(N.LogQueryService)}getDefinition(){return{name:e.TOOL_NAME,description:`Analyze error patterns and group similar errors by type and message. Calculates error frequencies, suggests root causes, and provides debugging insights.`,inputSchema:{type:`object`,properties:{timeRange:{type:`object`,properties:{start:{type:`string`,format:`date-time`,description:`Start time for error analysis (ISO 8601 format)`},end:{type:`string`,format:`date-time`,description:`End time for error analysis (ISO 8601 format)`}},description:`Optional time range to filter errors`},traceId:{type:`string`,description:`Optional trace ID to analyze errors within a specific trace`},limit:{type:`number`,minimum:1,maximum:1e3,default:100,description:`Maximum number of error entries to analyze`}},additionalProperties:!1}}}async execute(e){try{let t=e.limit??100,n;if(e.traceId)n=(await this.queryService.filterByTrace(e.traceId)).filter(e=>e.level===`error`||e.level===`fatal`).slice(0,t);else if(e.timeRange){let r=new Date(e.timeRange.start),i=new Date(e.timeRange.end);n=(await this.queryService.filterByTimeRange(r,i)).filter(e=>e.level===`error`||e.level===`fatal`).slice(0,t)}else n=await this.queryService.filterByLevel([`error`,`fatal`],t);let r=new Map;for(let e of n){let t=`${e.errorType}::${e.errorMessage}`;r.has(t)||r.set(t,{errorType:e.errorType,errorMessage:e.errorMessage,frequency:0,services:new Set,examples:[]});let n=r.get(t);n.frequency+=1,n.services.add(e.service),n.examples.length<3&&n.examples.push({id:e.id,timestamp:e.timestamp.toISOString(),service:e.service})}let i=Array.from(r.values()).sort((e,t)=>t.frequency-e.frequency).map(e=>({errorType:e.errorType,errorMessage:e.errorMessage,frequency:e.frequency,affectedServices:Array.from(e.services),examples:e.examples}));return{content:[{type:`text`,text:JSON.stringify({totalErrorsAnalyzed:n.length,uniqueErrorPatterns:i.length,analysis:i},null,2)}]}}catch(e){return{content:[{type:`text`,text:`Error analyzing logs: ${e instanceof Error?e.message:`Unknown error`}`}],isError:!0}}}},J=class e{static TOOL_NAME=`clear_logs`;retentionService;constructor(e){this.retentionService=e.get(N.LogRetentionService)}getDefinition(){return{name:e.TOOL_NAME,description:`Clear all logs from the database. Returns confirmation with count of deleted entries. Use with caution - this operation cannot be undone.`,inputSchema:{type:`object`,properties:{},additionalProperties:!1}}}async execute(e){try{let e=await this.retentionService.clearAllLogs();return{content:[{type:`text`,text:JSON.stringify({success:!0,message:`All logs have been cleared`,deletedEntries:e},null,2)}]}}catch(e){return{content:[{type:`text`,text:`Error clearing logs: ${e instanceof Error?e.message:`Unknown error`}`}],isError:!0}}}},Y=class e{static TOOL_NAME=`get_log_stats`;queryService;constructor(e){this.queryService=e.get(N.LogQueryService)}getDefinition(){return{name:e.TOOL_NAME,description:`Get aggregated log statistics grouped by level, service, or time period. Returns counts, distributions, and trends for log analysis.`,inputSchema:{type:`object`,properties:{startTime:{type:`string`,format:`date-time`,description:`Start time for statistics (ISO 8601 format)`},endTime:{type:`string`,format:`date-time`,description:`End time for statistics (ISO 8601 format)`},groupBy:{type:`string`,enum:[`level`,`service`,`both`],default:`both`,description:`Group statistics by 'level', 'service', or 'both'`}},additionalProperties:!1}}}async execute(e){try{let t;(e.startTime||e.endTime)&&(t={start:e.startTime?new Date(e.startTime):new Date(0),end:e.endTime?new Date(e.endTime):new Date});let n=e.groupBy??`both`,r=await this.queryService.getStatistics(t,n);return{content:[{type:`text`,text:JSON.stringify({timeRange:t,groupBy:n,totalEntries:r.reduce((e,t)=>e+t.count,0),statistics:r.map(e=>({service:e.service,level:e.level,count:e.count}))},null,2)}]}}catch(e){return{content:[{type:`text`,text:`Error getting statistics: ${e instanceof Error?e.message:`Unknown error`}`}],isError:!0}}}},X=class e{static TOOL_NAME=`get_services`;queryService;constructor(e){this.queryService=e.get(N.LogQueryService)}getDefinition(){return{name:e.TOOL_NAME,description:`Get list of all unique services that have logged entries. Useful for discovering available services in the system.`,inputSchema:{type:`object`,properties:{},additionalProperties:!1}}}async execute(e){try{let e=await this.queryService.getActiveServices();return{content:[{type:`text`,text:JSON.stringify({count:e.length,services:e.sort()},null,2)}]}}catch(e){return{content:[{type:`text`,text:`Error getting services: ${e instanceof Error?e.message:`Unknown error`}`}],isError:!0}}}},Z=class e{static TOOL_NAME=`get_trace_timeline`;queryService;constructor(e){this.queryService=e.get(N.LogQueryService)}getDefinition(){return{name:e.TOOL_NAME,description:`Get complete trace timeline for a given trace ID, showing all log entries ordered by timestamp with span hierarchy visualization. Returns chronological sequence of events across services.`,inputSchema:{type:`object`,properties:{traceId:{type:`string`,description:`OTEL trace ID (32 hex characters)`}},required:[`traceId`],additionalProperties:!1}}}async execute(e){try{let t=await this.queryService.getTraceTimeline(e.traceId);return{content:[{type:`text`,text:JSON.stringify({traceId:e.traceId,count:t.length,timeline:t.map(e=>({timestamp:e.timestamp.toISOString(),service:e.service,spanId:e.spanId,parentSpanId:e.parentSpanId,level:e.level,message:e.message,duration:e.duration}))},null,2)}]}}catch(e){return{content:[{type:`text`,text:`Error getting trace timeline: ${e instanceof Error?e.message:`Unknown error`}`}],isError:!0}}}},Q=class e{static TOOL_NAME=`query_logs`;queryService;constructor(e){this.queryService=e.get(N.LogQueryService)}getDefinition(){return{name:e.TOOL_NAME,description:`Query and filter log entries by level, trace ID, time range, service, or other criteria. Returns filtered log entries with full metadata.`,inputSchema:{type:`object`,properties:{level:{oneOf:[{type:`string`,enum:[`trace`,`debug`,`info`,`warn`,`error`,`fatal`]},{type:`array`,items:{type:`string`,enum:[`trace`,`debug`,`info`,`warn`,`error`,`fatal`]}}],description:`Log level(s) to filter by`},traceId:{type:`string`,description:`Trace ID to filter by (OTEL format: 32 hex characters)`},service:{oneOf:[{type:`string`},{type:`array`,items:{type:`string`}}],description:`Service name(s) to filter by`},startTime:{type:`string`,format:`date-time`,description:`Start time for log range (ISO 8601 format)`},endTime:{type:`string`,format:`date-time`,description:`End time for log range (ISO 8601 format)`},limit:{type:`number`,minimum:1,maximum:1e3,default:100,description:`Maximum number of log entries to return`}},additionalProperties:!1}}}async execute(e){try{let t=e.limit??100,n;if(e.traceId)n=await this.queryService.filterByTrace(e.traceId),n=n.slice(0,t);else if(e.level)n=await this.queryService.filterByLevel(e.level,t);else if(e.service)n=await this.queryService.filterByService(e.service),n=n.slice(0,t);else if(e.startTime||e.endTime){let r=e.startTime?new Date(e.startTime):new Date(0),i=e.endTime?new Date(e.endTime):new Date;n=await this.queryService.filterByTimeRange(r,i),n=n.slice(0,t)}else n=await this.queryService.filterByLevel([`info`,`warn`,`error`,`fatal`],t);return{content:[{type:`text`,text:JSON.stringify({count:n.length,logs:n.map(e=>({id:e.id,timestamp:e.timestamp.toISOString(),level:e.level,message:e.message,service:e.service,traceId:e.traceId,spanId:e.spanId,parentSpanId:e.parentSpanId,hostname:e.hostname,pid:e.pid,metadata:e.metadata,errorType:e.errorType,errorMessage:e.errorMessage,errorStack:e.errorStack}))},null,2)}]}}catch(e){return{content:[{type:`text`,text:`Error querying logs: ${e instanceof Error?e.message:`Unknown error`}`}],isError:!0}}}},$=class e{static TOOL_NAME=`search_logs`;searchService;constructor(e){this.searchService=e.get(N.LogSearchService)}getDefinition(){return{name:e.TOOL_NAME,description:`Search log entries using full-text search on message and error message fields. Returns ranked search results from FTS5 with relevance scoring.`,inputSchema:{type:`object`,properties:{searchQuery:{type:`string`,description:`FTS5 search query (supports phrase queries with quotes, prefix matching with *, boolean operators AND/OR/NOT)`},service:{oneOf:[{type:`string`},{type:`array`,items:{type:`string`}}],description:`Service name(s) to filter by`},level:{oneOf:[{type:`string`,enum:[`trace`,`debug`,`info`,`warn`,`error`,`fatal`]},{type:`array`,items:{type:`string`,enum:[`trace`,`debug`,`info`,`warn`,`error`,`fatal`]}}],description:`Log level(s) to filter by`},startTime:{type:`string`,format:`date-time`,description:`Start time for log range (ISO 8601 format)`},endTime:{type:`string`,format:`date-time`,description:`End time for log range (ISO 8601 format)`},limit:{type:`number`,minimum:1,maximum:1e3,default:100,description:`Maximum number of search results to return`}},required:[`searchQuery`],additionalProperties:!1}}}async execute(e){try{let t={};e.service&&(t.service=e.service),e.level&&(t.level=e.level),e.startTime&&(t.startTime=new Date(e.startTime)),e.endTime&&(t.endTime=new Date(e.endTime));let n=e.limit??100,r=await this.searchService.searchLogs(e.searchQuery,t,n);return{content:[{type:`text`,text:JSON.stringify({count:r.length,query:e.searchQuery,results:r.map(e=>({rank:e.rank,id:e.id,timestamp:e.timestamp.toISOString(),level:e.level,message:e.message,service:e.service,traceId:e.traceId,spanId:e.spanId,parentSpanId:e.parentSpanId,hostname:e.hostname,pid:e.pid,metadata:e.metadata,errorType:e.errorType,errorMessage:e.errorMessage}))},null,2)}]}}catch(e){return{content:[{type:`text`,text:`Error searching logs: ${e instanceof Error?e.message:`Unknown error`}`}],isError:!0}}}};function re(e){let t=new D({name:`log-sink-mcp`,version:`0.1.0`},{capabilities:{tools:{}}}),n=e??K(),r=new Q(n),i=new $(n),a=new Z(n),o=new q(n),s=new Y(n),c=new X(n),l=new J(n);return t.setRequestHandler(k,async()=>({tools:[r.getDefinition(),i.getDefinition(),a.getDefinition(),o.getDefinition(),s.getDefinition(),c.getDefinition(),l.getDefinition()]})),t.setRequestHandler(O,async e=>{let{name:t,arguments:n}=e.params;try{switch(t){case`query_logs`:return await r.execute(n);case`search_logs`:return await i.execute(n);case`get_trace_timeline`:return await a.execute(n);case`analyze_errors`:return await o.execute(n);case`get_log_stats`:return await s.execute(n);case`get_services`:return await c.execute(n);case`clear_logs`:return await l.execute(n);default:throw Error(`Unknown tool: ${t}`)}}catch(e){return{content:[{type:`text`,text:`Error executing tool ${t}: ${e instanceof Error?e.message:String(e)}`}],isError:!0}}}),t}var ie=class{server;transport=null;constructor(e){this.server=e}async start(){this.transport=new A,await this.server.connect(this.transport)}async stop(){this.transport&&=(await this.transport.close(),null)}};export{Z as a,J as c,N as d,Q as i,q as l,re as n,X as o,$ as r,Y as s,ie as t,K as u};
@@ -0,0 +1,70 @@
1
+ var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require(`node:fs`),l=require(`node:path`);l=s(l);let u=require(`node:url`),d=require(`@agimon-ai/foundation-port-registry`),f=require(`inversify`);require(`reflect-metadata`);let p=require(`node:child_process`),m=require(`node:fs/promises`);m=s(m);let h=require(`drizzle-orm`),g=require(`drizzle-orm/sqlite-core`),_=require(`ulidx`),v=require(`node:os`),y=require(`better-sqlite3`);y=s(y);let b=require(`drizzle-orm/better-sqlite3`),x=require(`@modelcontextprotocol/sdk/server/index.js`),S=require(`@modelcontextprotocol/sdk/types.js`),C=require(`@modelcontextprotocol/sdk/server/stdio.js`);function w(e,t,n,r){var i=arguments.length,a=i<3?t:r===null?r=Object.getOwnPropertyDescriptor(t,n):r,o;if(typeof Reflect==`object`&&typeof Reflect.decorate==`function`)a=Reflect.decorate(e,t,n,r);else for(var s=e.length-1;s>=0;s--)(o=e[s])&&(a=(i<3?o(a):i>3?o(t,n,a):o(t,n))||a);return i>3&&a&&Object.defineProperty(t,n,a),a}let T=class{defaultTimeout=5e3;async check(e,t=this.defaultTimeout){try{let n=`http://localhost:${e}/health`,r=new AbortController,i=setTimeout(()=>r.abort(),t);try{let t=await fetch(n,{signal:r.signal,headers:{"Content-Type":`application/json`}});if(clearTimeout(i),!t.ok)return{healthy:!1,error:`HTTP error ${t.status}: ${t.statusText}`};let a=await t.json();return a.status===`healthy`?{healthy:!0,port:e,serviceName:a.serviceName??a.service}:{healthy:!1,error:`Unexpected health status: ${a.status||`unknown`}`}}catch(e){return clearTimeout(i),e instanceof Error?e.name===`AbortError`?{healthy:!1,error:`Health check timed out after ${t}ms`}:`code`in e&&e.code===`ECONNREFUSED`?{healthy:!1,error:`Connection refused - server not running`}:{healthy:!1,error:`Network error: ${e.message}`}:{healthy:!1,error:`Unknown error: ${String(e)}`}}}catch(e){return{healthy:!1,error:e instanceof Error?e.message:String(e)}}}};T=w([(0,f.injectable)()],T);const E={LogStorageService:Symbol.for(`LogStorageService`),LogQueryService:Symbol.for(`LogQueryService`),LogSearchService:Symbol.for(`LogSearchService`),LogRetentionService:Symbol.for(`LogRetentionService`),HttpServerHealthCheck:Symbol.for(`HttpServerHealthCheck`),HttpServerManager:Symbol.for(`HttpServerManager`),Database:Symbol.for(`Database`),Logger:Symbol.for(`Logger`),HttpServer:Symbol.for(`HttpServer`),McpServer:Symbol.for(`McpServer`)};function D(e,t){if(typeof Reflect==`object`&&typeof Reflect.metadata==`function`)return Reflect.metadata(e,t)}function O(e,t){return function(n,r){t(n,r,e)}}const k=(0,u.fileURLToPath)(require(`url`).pathToFileURL(__filename).href),A=l.default.dirname(k),j=[`pnpm-workspace.yaml`,`nx.json`,`.git`];function M(e=process.cwd()){let t=l.default.resolve(e);for(;;){for(let e of j)if((0,c.existsSync)(l.default.join(t,e)))return t;let e=l.default.dirname(t);if(e===t)return process.cwd();t=e}}let N=class{repositoryPath;environment;serviceName=`log-sink-mcp-http`;serviceType=`tool`;host=`127.0.0.1`;registryPath;portRegistry;constructor(e,t,n){this.healthCheck=e,this.repositoryPath=M(t?l.default.resolve(t):process.cwd()),this.environment=process.env.NODE_ENV||`development`,this.registryPath=n||process.env.PORT_REGISTRY_PATH,this.portRegistry=new d.PortRegistryService(this.registryPath)}async getRegistration(){let e=await this.portRegistry.getPort({repositoryPath:this.repositoryPath,serviceName:this.serviceName,serviceType:this.serviceType,environment:this.environment});return!e.success||!e.record?null:{port:e.record.port,pid:e.record.pid,environment:e.record.environment??this.environment,host:e.record.host,metadata:e.record.metadata}}async releaseService(e){let t=await this.portRegistry.releasePort({repositoryPath:this.repositoryPath,serviceName:this.serviceName,serviceType:this.serviceType,environment:this.environment,...e?{pid:e}:{}});if(!t.success&&!t.error?.includes(`No matching registry entry`))throw Error(t.error||`Failed to release registry entry`)}async findAvailablePort(e){let t=await this.portRegistry.findAvailablePort({repositoryPath:this.repositoryPath,serviceName:this.serviceName,serviceType:this.serviceType,environment:this.environment,host:this.host,preferredPort:e});if(!t.success||!t.port)throw Error(`No available port found from ${e}: ${t.error}`);return t.port}async fileExists(e){try{return await m.default.access(e),!0}catch{return!1}}async startHttpServer(e,t){let n=[l.default.resolve(A,`cli.mjs`),l.default.resolve(A,`..`,`dist`,`cli.mjs`),l.default.resolve(A,`..`,`..`,`dist`,`cli.mjs`)],r=[l.default.resolve(A,`..`,`cli.ts`),l.default.resolve(A,`..`,`..`,`src`,`cli.ts`)],i;for(let e of n)if(await this.fileExists(e)){i=e;break}let a;if(!i){for(let e of r)if(await this.fileExists(e)){a=e;break}}let o,s;if(i)o=process.execPath,s=[i,`http-serve`,`--port`,e.toString()];else if(a)o=process.execPath,s=[`--import`,`tsx`,a,`http-serve`,`--port`,e.toString()];else throw Error(`Cannot find log-sink MCP CLI in any expected location`);let c=l.default.resolve(t);await m.default.mkdir(l.default.dirname(c),{recursive:!0});let u=(0,p.spawn)(o,[...s,`--db-path`,c],{detached:!0,stdio:`ignore`,env:{...process.env,...this.registryPath?{PORT_REGISTRY_PATH:this.registryPath}:{},NODE_ENV:this.environment}});u.unref();let d=u.pid;if(!d)throw Error(`Failed to spawn HTTP server - no PID returned`);return d}async waitForRegisteredService(e,t,n=10,r=250){for(let i=0;i<n;i+=1){let n=await this.getRegistration();if(n?.pid===t&&n.port===e)return n;await new Promise(e=>setTimeout(e,r))}return null}async waitForServerStartup(e=4e3){await new Promise(t=>setTimeout(t,e))}async killProcess(e){try{process.kill(e,0),process.kill(e,`SIGTERM`),await new Promise(e=>setTimeout(e,1e3));try{process.kill(e,0),process.kill(e,`SIGKILL`)}catch{}}catch{}}async ensureRunning(e=3100,t=`./logs/session.db`){try{let n=await this.getRegistration();if(n){let e=await this.healthCheck.check(n.port);if(e.healthy&&e.serviceName===this.serviceName)return{running:!0,port:n.port,pid:n.pid};n.pid&&await this.killProcess(n.pid),await this.releaseService(n.pid)}let r=e;for(let e=0;e<3;e+=1){let e=await this.findAvailablePort(r),n=await this.startHttpServer(e,t);await this.waitForServerStartup();let i=await this.healthCheck.check(e);if(!i.healthy||i.serviceName!==this.serviceName){await this.killProcess(n),await this.releaseService(n),r=e+1;continue}if(!await this.waitForRegisteredService(e,n)){await this.killProcess(n),await this.releaseService(n),r=e+1;continue}return{running:!0,port:e,pid:n}}return{running:!1,error:`Failed to start log-sink-mcp HTTP server after retries`}}catch(e){return{running:!1,error:e instanceof Error?e.message:String(e)}}}async stop(){try{let e=await this.getRegistration();if(!e)return!1;try{if(e.pid&&process.kill(e.pid,`SIGTERM`),await new Promise(e=>setTimeout(e,1e3)),e.pid)try{process.kill(e.pid,0),process.kill(e.pid,`SIGKILL`)}catch{}}catch{}return await this.releaseService(e.pid),!0}catch(e){throw Error(`Failed to stop HTTP server: ${e instanceof Error?e.message:String(e)}`)}}async getStatus(){try{let e=await this.getRegistration();if(!e)return{running:!1,error:`No HTTP server registered`};let t=await this.healthCheck.check(e.port);return t.healthy&&t.serviceName===this.serviceName?{running:!0,port:e.port,pid:e.pid}:{running:!1,port:e.port,pid:e.pid,error:t.error}}catch(e){return{running:!1,error:e instanceof Error?e.message:String(e)}}}};N=w([(0,f.injectable)(),O(0,(0,f.inject)(E.HttpServerHealthCheck)),O(1,(0,f.unmanaged)()),O(2,(0,f.unmanaged)()),D(`design:paramtypes`,[Object,String,String])],N);const P=(0,g.sqliteTable)(`logs`,{id:(0,g.text)(`id`).primaryKey().$defaultFn(()=>(0,_.ulid)()),timestamp:(0,g.integer)(`timestamp`,{mode:`timestamp`}).$defaultFn(()=>new Date).notNull(),level:(0,g.text)(`level`).notNull(),message:(0,g.text)(`message`).notNull(),traceId:(0,g.text)(`trace_id`),spanId:(0,g.text)(`span_id`),parentSpanId:(0,g.text)(`parent_span_id`),service:(0,g.text)(`service`).notNull(),hostname:(0,g.text)(`hostname`),pid:(0,g.integer)(`pid`),metadata:(0,g.text)(`metadata`,{mode:`json`}),errorType:(0,g.text)(`error_type`),errorMessage:(0,g.text)(`error_message`),errorStack:(0,g.text)(`error_stack`),createdAt:(0,g.integer)(`created_at`,{mode:`timestamp`}).$defaultFn(()=>new Date).notNull()},e=>({timestampIdx:(0,g.index)(`logs_timestamp_idx`).on(e.timestamp),levelIdx:(0,g.index)(`logs_level_idx`).on(e.level),traceIdIdx:(0,g.index)(`logs_trace_id_idx`).on(e.traceId),spanIdIdx:(0,g.index)(`logs_span_id_idx`).on(e.spanId),parentSpanIdIdx:(0,g.index)(`logs_parent_span_id_idx`).on(e.parentSpanId),serviceIdx:(0,g.index)(`logs_service_idx`).on(e.service),errorTypeIdx:(0,g.index)(`logs_error_type_idx`).on(e.errorType)}));let F=class{constructor(e){this.storage=e}async getDb(){return await this.storage.ensureInitialized(),this.storage.getDatabaseClient()}async filterByLevel(e,t=100){try{let n=await this.getDb(),r=Array.isArray(e)?e:[e];return r.length===1?await n.select().from(P).where((0,h.eq)(P.level,r[0])).orderBy((0,h.desc)(P.timestamp)).limit(t):await n.select().from(P).where(h.sql`${P.level} IN (${h.sql.join(r.map(e=>h.sql`${e}`),h.sql`, `)})`).orderBy((0,h.desc)(P.timestamp)).limit(t)}catch(e){throw Error(`Failed to filter by level: ${e instanceof Error?e.message:String(e)}`)}}async filterByTrace(e){try{return await(await this.getDb()).select().from(P).where((0,h.eq)(P.traceId,e)).orderBy(P.timestamp)}catch(e){throw Error(`Failed to filter by trace: ${e instanceof Error?e.message:String(e)}`)}}async filterByTimeRange(e,t){try{return await(await this.getDb()).select().from(P).where((0,h.and)((0,h.gte)(P.timestamp,e),(0,h.lte)(P.timestamp,t))).orderBy(P.timestamp)}catch(e){throw Error(`Failed to filter by time range: ${e instanceof Error?e.message:String(e)}`)}}async filterByService(e){try{let t=await this.getDb(),n=Array.isArray(e)?e:[e];return n.length===1?await t.select().from(P).where((0,h.eq)(P.service,n[0])).orderBy(P.timestamp):await t.select().from(P).where(h.sql`${P.service} IN (${h.sql.join(n.map(e=>h.sql`${e}`),h.sql`, `)})`).orderBy(P.timestamp)}catch(e){throw Error(`Failed to filter by service: ${e instanceof Error?e.message:String(e)}`)}}async getTraceTimeline(e){try{let t=await this.filterByTrace(e),n=[];for(let e=0;e<t.length;e++){let r=t[e],i=t[e+1];n.push({timestamp:r.timestamp,service:r.service,spanId:r.spanId??null,parentSpanId:r.parentSpanId??null,level:r.level,message:r.message,duration:i?i.timestamp.getTime()-r.timestamp.getTime():void 0})}return n}catch(e){throw Error(`Failed to get trace timeline: ${e instanceof Error?e.message:String(e)}`)}}async getStatistics(e,t=`both`){try{let n=(await this.getDb()).select({service:P.service,level:P.level,count:(0,h.count)()}).from(P);return e&&(n=n.where((0,h.and)((0,h.gte)(P.timestamp,e.start),(0,h.lte)(P.timestamp,e.end)))),n=t===`level`?n.groupBy(P.level):t===`service`?n.groupBy(P.service):n.groupBy(P.service,P.level),(await n).map(e=>({service:e.service,level:e.level,count:Number(e.count)}))}catch(e){throw Error(`Failed to get statistics: ${e instanceof Error?e.message:String(e)}`)}}async getActiveServices(){try{return(await(await this.getDb()).select({service:P.service}).from(P).groupBy(P.service)).map(e=>e.service)}catch(e){throw Error(`Failed to get active services: ${e instanceof Error?e.message:String(e)}`)}}async countByLevel(e){try{let t=await this.getDb(),n=Array.isArray(e)?e:[e],r;return r=n.length===1?await t.select({count:(0,h.count)()}).from(P).where((0,h.eq)(P.level,n[0])):await t.select({count:(0,h.count)()}).from(P).where(h.sql`${P.level} IN (${h.sql.join(n.map(e=>h.sql`${e}`),h.sql`, `)})`),Number(r[0]?.count??0)}catch(e){throw Error(`Failed to count by level: ${e instanceof Error?e.message:String(e)}`)}}async countByService(e){try{let t=await this.getDb(),n=Array.isArray(e)?e:[e],r;return r=n.length===1?await t.select({count:(0,h.count)()}).from(P).where((0,h.eq)(P.service,n[0])):await t.select({count:(0,h.count)()}).from(P).where(h.sql`${P.service} IN (${h.sql.join(n.map(e=>h.sql`${e}`),h.sql`, `)})`),Number(r[0]?.count??0)}catch(e){throw Error(`Failed to count by service: ${e instanceof Error?e.message:String(e)}`)}}async countByTrace(e){try{let t=await(await this.getDb()).select({count:(0,h.count)()}).from(P).where((0,h.eq)(P.traceId,e));return Number(t[0]?.count??0)}catch(e){throw Error(`Failed to count by trace: ${e instanceof Error?e.message:String(e)}`)}}async queryLogs(e){try{let t=await this.getDb(),{level:n,service:r,traceId:i,limit:a=25,offset:o=0}=e,s=[];if(n){let e=Array.isArray(n)?n:[n];e.length===1?s.push((0,h.eq)(P.level,e[0])):s.push(h.sql`${P.level} IN (${h.sql.join(e.map(e=>h.sql`${e}`),h.sql`, `)})`)}r&&s.push((0,h.eq)(P.service,r)),i&&s.push((0,h.eq)(P.traceId,i));let c=s.length>0?(0,h.and)(...s):void 0,l=c?await t.select({count:(0,h.count)()}).from(P).where(c):await t.select({count:(0,h.count)()}).from(P),u=Number(l[0]?.count??0),d=t.select().from(P);return c&&(d=d.where(c)),{logs:await d.orderBy((0,h.desc)(P.timestamp)).limit(a).offset(o),total:u}}catch(e){throw Error(`Failed to query logs: ${e instanceof Error?e.message:String(e)}`)}}};F=w([(0,f.injectable)(),O(0,(0,f.inject)(E.LogStorageService)),D(`design:paramtypes`,[Object])],F);let I=class{constructor(e){this.storage=e}async getDb(){return await this.storage.ensureInitialized(),this.storage.getDb()}async cleanupOldLogs(e){try{let t=await this.getDb(),n=new Date;return n.setDate(n.getDate()-e),(await t.delete(P).where((0,h.lte)(P.timestamp,n))).changes??0}catch(e){throw Error(`Failed to cleanup old logs: ${e instanceof Error?e.message:String(e)}`)}}async clearAllLogs(){try{return await this.storage.deleteAllLogs()}catch(e){throw Error(`Failed to clear all logs: ${e instanceof Error?e.message:String(e)}`)}}};I=w([(0,f.injectable)(),O(0,(0,f.inject)(E.LogStorageService)),D(`design:paramtypes`,[Object])],I);let L=class{constructor(e){this.storage=e}async getSqlite(){return await this.storage.ensureInitialized(),this.storage.getSqliteClient()}buildSearchSql(e,t={}){let n=e.replace(/[^\w\s\-"*]/g,``).trim();if(!n)throw Error(`Search query is empty after sanitization`);let r=`WHERE logs_search MATCH ?`,i=[n];if(t.service){let e=Array.isArray(t.service)?t.service:[t.service];r+=` AND logs.service IN (${e.map(()=>`?`).join(`,`)})`,i.push(...e)}if(t.level){let e=Array.isArray(t.level)?t.level:[t.level];r+=` AND logs.level IN (${e.map(()=>`?`).join(`,`)})`,i.push(...e)}return t.startTime&&(r+=` AND logs.timestamp >= ?`,i.push(Math.floor(t.startTime.getTime()/1e3))),t.endTime&&(r+=` AND logs.timestamp <= ?`,i.push(Math.floor(t.endTime.getTime()/1e3))),{whereSql:r,params:i}}mapSearchRow(e){return{id:e.id,timestamp:new Date(e.timestamp*1e3),level:e.level,message:e.message,traceId:e.traceId,spanId:e.spanId,parentSpanId:e.parentSpanId,service:e.service,hostname:e.hostname,pid:e.pid,metadata:e.metadata?JSON.parse(e.metadata):null,errorType:e.errorType,errorMessage:e.errorMessage,errorStack:e.errorStack,createdAt:new Date(e.createdAt*1e3),rank:e.rank}}async searchLogs(e,t={},n=100){let{results:r}=await this.searchLogsPaginated(e,{...t,offset:0},n);return r}async searchLogsPaginated(e,t={},n=100){try{let r=await this.getSqlite(),{whereSql:i,params:a}=this.buildSearchSql(e,t),o=t.offset??0,s=`
2
+ SELECT COUNT(*) as total
3
+ FROM logs_search
4
+ JOIN logs ON logs.rowid = logs_search.rowid
5
+ ${i}
6
+ `,c=`
7
+ SELECT
8
+ logs.id as id,
9
+ logs.timestamp as timestamp,
10
+ logs.level as level,
11
+ logs.message as message,
12
+ logs.trace_id as traceId,
13
+ logs.span_id as spanId,
14
+ logs.parent_span_id as parentSpanId,
15
+ logs.service as service,
16
+ logs.hostname as hostname,
17
+ logs.pid as pid,
18
+ logs.metadata as metadata,
19
+ logs.error_type as errorType,
20
+ logs.error_message as errorMessage,
21
+ logs.error_stack as errorStack,
22
+ logs.created_at as createdAt,
23
+ logs_search.rank as rank
24
+ FROM logs_search
25
+ JOIN logs ON logs.rowid = logs_search.rowid
26
+ ${i}
27
+ ORDER BY rank
28
+ LIMIT ?
29
+ OFFSET ?
30
+ `,l=r.prepare(s).get(...a);return{results:r.prepare(c).all(...a,n,o).map(e=>this.mapSearchRow(e)),total:Number(l.total??0)}}catch(e){throw Error(`Failed to search logs: ${e instanceof Error?e.message:String(e)}`)}}rankResults(e){return e.sort((e,t)=>e.rank-t.rank)}};L=w([(0,f.injectable)(),O(0,(0,f.inject)(E.LogStorageService)),D(`design:paramtypes`,[Object])],L);let R=class{db=null;sqlite=null;async initializeDatabase(e,t=!1){try{let n=t?`:memory:`:e;t||await(0,m.mkdir)((0,l.dirname)(n),{recursive:!0}),this.sqlite=new y.default(n),this.sqlite.pragma(`journal_mode = WAL`),this.sqlite.pragma(`foreign_keys = ON`),this.db=(0,b.drizzle)(this.sqlite,{schema:{logs:P}}),this.sqlite.exec(`
31
+ CREATE TABLE IF NOT EXISTS logs (
32
+ id text PRIMARY KEY NOT NULL,
33
+ timestamp integer NOT NULL,
34
+ level text NOT NULL,
35
+ message text NOT NULL,
36
+ trace_id text,
37
+ span_id text,
38
+ parent_span_id text,
39
+ service text NOT NULL,
40
+ hostname text,
41
+ pid integer,
42
+ metadata text,
43
+ error_type text,
44
+ error_message text,
45
+ error_stack text,
46
+ created_at integer NOT NULL
47
+ )
48
+ `),this.sqlite.exec(`CREATE INDEX IF NOT EXISTS logs_timestamp_idx ON logs (timestamp)`),this.sqlite.exec(`CREATE INDEX IF NOT EXISTS logs_level_idx ON logs (level)`),this.sqlite.exec(`CREATE INDEX IF NOT EXISTS logs_trace_id_idx ON logs (trace_id)`),this.sqlite.exec(`CREATE INDEX IF NOT EXISTS logs_span_id_idx ON logs (span_id)`),this.sqlite.exec(`CREATE INDEX IF NOT EXISTS logs_parent_span_id_idx ON logs (parent_span_id)`),this.sqlite.exec(`CREATE INDEX IF NOT EXISTS logs_service_idx ON logs (service)`),this.sqlite.exec(`CREATE INDEX IF NOT EXISTS logs_error_type_idx ON logs (error_type)`),this.sqlite.exec(`
49
+ CREATE VIRTUAL TABLE IF NOT EXISTS logs_search USING fts5(
50
+ id UNINDEXED,
51
+ message,
52
+ error_message,
53
+ content='logs',
54
+ content_rowid='rowid'
55
+ )
56
+ `),this.sqlite.exec(`
57
+ CREATE TRIGGER IF NOT EXISTS logs_ai AFTER INSERT ON logs BEGIN
58
+ INSERT INTO logs_search(rowid, id, message, error_message)
59
+ VALUES (new.rowid, new.id, new.message, new.error_message);
60
+ END;
61
+ `),this.sqlite.exec(`
62
+ CREATE TRIGGER IF NOT EXISTS logs_ad AFTER DELETE ON logs BEGIN
63
+ DELETE FROM logs_search WHERE rowid = old.rowid;
64
+ END;
65
+ `),this.sqlite.exec(`
66
+ CREATE TRIGGER IF NOT EXISTS logs_au AFTER UPDATE ON logs BEGIN
67
+ UPDATE logs_search SET message = new.message, error_message = new.error_message
68
+ WHERE rowid = new.rowid;
69
+ END;
70
+ `)}catch(e){throw Error(`Failed to initialize database: ${e instanceof Error?e.message:String(e)}`)}}getDefaultDbPath(){return(0,l.join)((0,v.tmpdir)(),`log-sink-mcp`,`session.db`)}isInitialized(){return this.db!==null}async ensureInitialized(){this.db||await this.initializeDatabase(this.getDefaultDbPath())}getDb(){if(!this.db)throw Error(`Database not initialized. Call initializeDatabase() first.`);return this.db}getDatabaseClient(){return this.getDb()}getSqliteClient(){if(!this.sqlite)throw Error(`Database not initialized. Call initializeDatabase() first.`);return this.sqlite}async insertLog(e){try{let t=await this.getDb().insert(P).values(e).returning();if(t.length===0)throw Error(`Failed to insert log - no record returned`);return t[0]}catch(e){throw Error(`Failed to insert log: ${e instanceof Error?e.message:String(e)}`)}}async insertBatch(e){try{return e.length===0?[]:await this.getDb().insert(P).values(e).returning()}catch(e){throw Error(`Failed to insert batch: ${e instanceof Error?e.message:String(e)}`)}}async queryLogs(e={}){try{let t=this.getDb(),n=[];if(e.level){let t=Array.isArray(e.level)?e.level:[e.level];t.length===1?n.push((0,h.eq)(P.level,t[0])):n.push(h.sql`${P.level} IN (${h.sql.join(t.map(e=>h.sql`${e}`),h.sql`, `)})`)}if(e.service){let t=Array.isArray(e.service)?e.service:[e.service];t.length===1?n.push((0,h.eq)(P.service,t[0])):n.push(h.sql`${P.service} IN (${h.sql.join(t.map(e=>h.sql`${e}`),h.sql`, `)})`)}e.traceId&&n.push((0,h.eq)(P.traceId,e.traceId)),e.spanId&&n.push((0,h.eq)(P.spanId,e.spanId)),e.errorType&&n.push((0,h.eq)(P.errorType,e.errorType)),e.startTime&&n.push((0,h.gte)(P.timestamp,e.startTime)),e.endTime&&n.push((0,h.lte)(P.timestamp,e.endTime));let r=n.length>0?(0,h.and)(...n):void 0,i=e.limit??100,a=e.offset??0,o=t.select().from(P);return r&&(o=o.where(r)),await o.orderBy((0,h.desc)(P.timestamp)).limit(i).offset(a)}catch(e){throw Error(`Failed to query logs: ${e instanceof Error?e.message:String(e)}`)}}async deleteAllLogs(){try{if(!this.sqlite)throw Error(`Database not initialized`);return this.sqlite.prepare(`DELETE FROM logs`).run().changes}catch(e){throw Error(`Failed to delete all logs: ${e instanceof Error?e.message:String(e)}`)}}async disconnect(){this.sqlite&&(this.sqlite.close(),this.sqlite=null,this.db=null)}};R=w([(0,f.injectable)()],R);const z=new f.ContainerModule(e=>{e.bind(E.LogStorageService).to(R).inSingletonScope(),e.bind(E.LogQueryService).to(F).inSingletonScope(),e.bind(E.LogSearchService).to(L).inSingletonScope(),e.bind(E.LogRetentionService).to(I).inSingletonScope(),e.bind(E.HttpServerHealthCheck).to(T).inSingletonScope(),e.bind(E.HttpServerManager).to(N).inSingletonScope()});function B(){let e=new f.Container;return e.load(z),e}var V=class e{static TOOL_NAME=`analyze_errors`;queryService;constructor(e){this.queryService=e.get(E.LogQueryService)}getDefinition(){return{name:e.TOOL_NAME,description:`Analyze error patterns and group similar errors by type and message. Calculates error frequencies, suggests root causes, and provides debugging insights.`,inputSchema:{type:`object`,properties:{timeRange:{type:`object`,properties:{start:{type:`string`,format:`date-time`,description:`Start time for error analysis (ISO 8601 format)`},end:{type:`string`,format:`date-time`,description:`End time for error analysis (ISO 8601 format)`}},description:`Optional time range to filter errors`},traceId:{type:`string`,description:`Optional trace ID to analyze errors within a specific trace`},limit:{type:`number`,minimum:1,maximum:1e3,default:100,description:`Maximum number of error entries to analyze`}},additionalProperties:!1}}}async execute(e){try{let t=e.limit??100,n;if(e.traceId)n=(await this.queryService.filterByTrace(e.traceId)).filter(e=>e.level===`error`||e.level===`fatal`).slice(0,t);else if(e.timeRange){let r=new Date(e.timeRange.start),i=new Date(e.timeRange.end);n=(await this.queryService.filterByTimeRange(r,i)).filter(e=>e.level===`error`||e.level===`fatal`).slice(0,t)}else n=await this.queryService.filterByLevel([`error`,`fatal`],t);let r=new Map;for(let e of n){let t=`${e.errorType}::${e.errorMessage}`;r.has(t)||r.set(t,{errorType:e.errorType,errorMessage:e.errorMessage,frequency:0,services:new Set,examples:[]});let n=r.get(t);n.frequency+=1,n.services.add(e.service),n.examples.length<3&&n.examples.push({id:e.id,timestamp:e.timestamp.toISOString(),service:e.service})}let i=Array.from(r.values()).sort((e,t)=>t.frequency-e.frequency).map(e=>({errorType:e.errorType,errorMessage:e.errorMessage,frequency:e.frequency,affectedServices:Array.from(e.services),examples:e.examples}));return{content:[{type:`text`,text:JSON.stringify({totalErrorsAnalyzed:n.length,uniqueErrorPatterns:i.length,analysis:i},null,2)}]}}catch(e){return{content:[{type:`text`,text:`Error analyzing logs: ${e instanceof Error?e.message:`Unknown error`}`}],isError:!0}}}},H=class e{static TOOL_NAME=`clear_logs`;retentionService;constructor(e){this.retentionService=e.get(E.LogRetentionService)}getDefinition(){return{name:e.TOOL_NAME,description:`Clear all logs from the database. Returns confirmation with count of deleted entries. Use with caution - this operation cannot be undone.`,inputSchema:{type:`object`,properties:{},additionalProperties:!1}}}async execute(e){try{let e=await this.retentionService.clearAllLogs();return{content:[{type:`text`,text:JSON.stringify({success:!0,message:`All logs have been cleared`,deletedEntries:e},null,2)}]}}catch(e){return{content:[{type:`text`,text:`Error clearing logs: ${e instanceof Error?e.message:`Unknown error`}`}],isError:!0}}}},U=class e{static TOOL_NAME=`get_log_stats`;queryService;constructor(e){this.queryService=e.get(E.LogQueryService)}getDefinition(){return{name:e.TOOL_NAME,description:`Get aggregated log statistics grouped by level, service, or time period. Returns counts, distributions, and trends for log analysis.`,inputSchema:{type:`object`,properties:{startTime:{type:`string`,format:`date-time`,description:`Start time for statistics (ISO 8601 format)`},endTime:{type:`string`,format:`date-time`,description:`End time for statistics (ISO 8601 format)`},groupBy:{type:`string`,enum:[`level`,`service`,`both`],default:`both`,description:`Group statistics by 'level', 'service', or 'both'`}},additionalProperties:!1}}}async execute(e){try{let t;(e.startTime||e.endTime)&&(t={start:e.startTime?new Date(e.startTime):new Date(0),end:e.endTime?new Date(e.endTime):new Date});let n=e.groupBy??`both`,r=await this.queryService.getStatistics(t,n);return{content:[{type:`text`,text:JSON.stringify({timeRange:t,groupBy:n,totalEntries:r.reduce((e,t)=>e+t.count,0),statistics:r.map(e=>({service:e.service,level:e.level,count:e.count}))},null,2)}]}}catch(e){return{content:[{type:`text`,text:`Error getting statistics: ${e instanceof Error?e.message:`Unknown error`}`}],isError:!0}}}},W=class e{static TOOL_NAME=`get_services`;queryService;constructor(e){this.queryService=e.get(E.LogQueryService)}getDefinition(){return{name:e.TOOL_NAME,description:`Get list of all unique services that have logged entries. Useful for discovering available services in the system.`,inputSchema:{type:`object`,properties:{},additionalProperties:!1}}}async execute(e){try{let e=await this.queryService.getActiveServices();return{content:[{type:`text`,text:JSON.stringify({count:e.length,services:e.sort()},null,2)}]}}catch(e){return{content:[{type:`text`,text:`Error getting services: ${e instanceof Error?e.message:`Unknown error`}`}],isError:!0}}}},G=class e{static TOOL_NAME=`get_trace_timeline`;queryService;constructor(e){this.queryService=e.get(E.LogQueryService)}getDefinition(){return{name:e.TOOL_NAME,description:`Get complete trace timeline for a given trace ID, showing all log entries ordered by timestamp with span hierarchy visualization. Returns chronological sequence of events across services.`,inputSchema:{type:`object`,properties:{traceId:{type:`string`,description:`OTEL trace ID (32 hex characters)`}},required:[`traceId`],additionalProperties:!1}}}async execute(e){try{let t=await this.queryService.getTraceTimeline(e.traceId);return{content:[{type:`text`,text:JSON.stringify({traceId:e.traceId,count:t.length,timeline:t.map(e=>({timestamp:e.timestamp.toISOString(),service:e.service,spanId:e.spanId,parentSpanId:e.parentSpanId,level:e.level,message:e.message,duration:e.duration}))},null,2)}]}}catch(e){return{content:[{type:`text`,text:`Error getting trace timeline: ${e instanceof Error?e.message:`Unknown error`}`}],isError:!0}}}},K=class e{static TOOL_NAME=`query_logs`;queryService;constructor(e){this.queryService=e.get(E.LogQueryService)}getDefinition(){return{name:e.TOOL_NAME,description:`Query and filter log entries by level, trace ID, time range, service, or other criteria. Returns filtered log entries with full metadata.`,inputSchema:{type:`object`,properties:{level:{oneOf:[{type:`string`,enum:[`trace`,`debug`,`info`,`warn`,`error`,`fatal`]},{type:`array`,items:{type:`string`,enum:[`trace`,`debug`,`info`,`warn`,`error`,`fatal`]}}],description:`Log level(s) to filter by`},traceId:{type:`string`,description:`Trace ID to filter by (OTEL format: 32 hex characters)`},service:{oneOf:[{type:`string`},{type:`array`,items:{type:`string`}}],description:`Service name(s) to filter by`},startTime:{type:`string`,format:`date-time`,description:`Start time for log range (ISO 8601 format)`},endTime:{type:`string`,format:`date-time`,description:`End time for log range (ISO 8601 format)`},limit:{type:`number`,minimum:1,maximum:1e3,default:100,description:`Maximum number of log entries to return`}},additionalProperties:!1}}}async execute(e){try{let t=e.limit??100,n;if(e.traceId)n=await this.queryService.filterByTrace(e.traceId),n=n.slice(0,t);else if(e.level)n=await this.queryService.filterByLevel(e.level,t);else if(e.service)n=await this.queryService.filterByService(e.service),n=n.slice(0,t);else if(e.startTime||e.endTime){let r=e.startTime?new Date(e.startTime):new Date(0),i=e.endTime?new Date(e.endTime):new Date;n=await this.queryService.filterByTimeRange(r,i),n=n.slice(0,t)}else n=await this.queryService.filterByLevel([`info`,`warn`,`error`,`fatal`],t);return{content:[{type:`text`,text:JSON.stringify({count:n.length,logs:n.map(e=>({id:e.id,timestamp:e.timestamp.toISOString(),level:e.level,message:e.message,service:e.service,traceId:e.traceId,spanId:e.spanId,parentSpanId:e.parentSpanId,hostname:e.hostname,pid:e.pid,metadata:e.metadata,errorType:e.errorType,errorMessage:e.errorMessage,errorStack:e.errorStack}))},null,2)}]}}catch(e){return{content:[{type:`text`,text:`Error querying logs: ${e instanceof Error?e.message:`Unknown error`}`}],isError:!0}}}},q=class e{static TOOL_NAME=`search_logs`;searchService;constructor(e){this.searchService=e.get(E.LogSearchService)}getDefinition(){return{name:e.TOOL_NAME,description:`Search log entries using full-text search on message and error message fields. Returns ranked search results from FTS5 with relevance scoring.`,inputSchema:{type:`object`,properties:{searchQuery:{type:`string`,description:`FTS5 search query (supports phrase queries with quotes, prefix matching with *, boolean operators AND/OR/NOT)`},service:{oneOf:[{type:`string`},{type:`array`,items:{type:`string`}}],description:`Service name(s) to filter by`},level:{oneOf:[{type:`string`,enum:[`trace`,`debug`,`info`,`warn`,`error`,`fatal`]},{type:`array`,items:{type:`string`,enum:[`trace`,`debug`,`info`,`warn`,`error`,`fatal`]}}],description:`Log level(s) to filter by`},startTime:{type:`string`,format:`date-time`,description:`Start time for log range (ISO 8601 format)`},endTime:{type:`string`,format:`date-time`,description:`End time for log range (ISO 8601 format)`},limit:{type:`number`,minimum:1,maximum:1e3,default:100,description:`Maximum number of search results to return`}},required:[`searchQuery`],additionalProperties:!1}}}async execute(e){try{let t={};e.service&&(t.service=e.service),e.level&&(t.level=e.level),e.startTime&&(t.startTime=new Date(e.startTime)),e.endTime&&(t.endTime=new Date(e.endTime));let n=e.limit??100,r=await this.searchService.searchLogs(e.searchQuery,t,n);return{content:[{type:`text`,text:JSON.stringify({count:r.length,query:e.searchQuery,results:r.map(e=>({rank:e.rank,id:e.id,timestamp:e.timestamp.toISOString(),level:e.level,message:e.message,service:e.service,traceId:e.traceId,spanId:e.spanId,parentSpanId:e.parentSpanId,hostname:e.hostname,pid:e.pid,metadata:e.metadata,errorType:e.errorType,errorMessage:e.errorMessage}))},null,2)}]}}catch(e){return{content:[{type:`text`,text:`Error searching logs: ${e instanceof Error?e.message:`Unknown error`}`}],isError:!0}}}};function J(e){let t=new x.Server({name:`log-sink-mcp`,version:`0.1.0`},{capabilities:{tools:{}}}),n=e??B(),r=new K(n),i=new q(n),a=new G(n),o=new V(n),s=new U(n),c=new W(n),l=new H(n);return t.setRequestHandler(S.ListToolsRequestSchema,async()=>({tools:[r.getDefinition(),i.getDefinition(),a.getDefinition(),o.getDefinition(),s.getDefinition(),c.getDefinition(),l.getDefinition()]})),t.setRequestHandler(S.CallToolRequestSchema,async e=>{let{name:t,arguments:n}=e.params;try{switch(t){case`query_logs`:return await r.execute(n);case`search_logs`:return await i.execute(n);case`get_trace_timeline`:return await a.execute(n);case`analyze_errors`:return await o.execute(n);case`get_log_stats`:return await s.execute(n);case`get_services`:return await c.execute(n);case`clear_logs`:return await l.execute(n);default:throw Error(`Unknown tool: ${t}`)}}catch(e){return{content:[{type:`text`,text:`Error executing tool ${t}: ${e instanceof Error?e.message:String(e)}`}],isError:!0}}}),t}var Y=class{server;transport=null;constructor(e){this.server=e}async start(){this.transport=new C.StdioServerTransport,await this.server.connect(this.transport)}async stop(){this.transport&&=(await this.transport.close(),null)}};Object.defineProperty(exports,`a`,{enumerable:!0,get:function(){return G}}),Object.defineProperty(exports,`c`,{enumerable:!0,get:function(){return H}}),Object.defineProperty(exports,`d`,{enumerable:!0,get:function(){return E}}),Object.defineProperty(exports,`f`,{enumerable:!0,get:function(){return s}}),Object.defineProperty(exports,`i`,{enumerable:!0,get:function(){return K}}),Object.defineProperty(exports,`l`,{enumerable:!0,get:function(){return V}}),Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return J}}),Object.defineProperty(exports,`o`,{enumerable:!0,get:function(){return W}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return q}}),Object.defineProperty(exports,`s`,{enumerable:!0,get:function(){return U}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return Y}}),Object.defineProperty(exports,`u`,{enumerable:!0,get:function(){return B}});
package/package.json ADDED
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "@agimon-ai/log-sink-mcp",
3
+ "description": "Log sink MCP server with HTTP ingestion and AI analysis",
4
+ "version": "0.2.0",
5
+ "license": "BUSL-1.1",
6
+ "keywords": [
7
+ "mcp",
8
+ "model-context-protocol",
9
+ "typescript"
10
+ ],
11
+ "bin": {
12
+ "log-sink-mcp": "./dist/cli.cjs"
13
+ },
14
+ "main": "./dist/index.cjs",
15
+ "types": "./dist/index.d.cts",
16
+ "module": "./dist/index.mjs",
17
+ "files": [
18
+ "dist",
19
+ "README.md"
20
+ ],
21
+ "dependencies": {
22
+ "@hono/node-server": "1.19.9",
23
+ "@hono/zod-validator": "^0.7.6",
24
+ "@modelcontextprotocol/sdk": "1.19.1",
25
+ "@opentelemetry/otlp-transformer": "0.210.0",
26
+ "better-sqlite3": "12.6.2",
27
+ "chalk": "5.6.2",
28
+ "commander": "14.0.1",
29
+ "drizzle-orm": "0.45.1",
30
+ "hono": "4.11.4",
31
+ "hono-rate-limiter": "0.5.2",
32
+ "inversify": "7.11.0",
33
+ "reflect-metadata": "0.2.2",
34
+ "ulidx": "2.4.1",
35
+ "zod": "4.3.6",
36
+ "@agimon-ai/foundation-port-registry": "0.2.0"
37
+ },
38
+ "devDependencies": {
39
+ "@types/better-sqlite3": "^7.6.13",
40
+ "@types/bun": "^1.3.6",
41
+ "@types/express": "^5.0.0",
42
+ "@types/node": "^22.0.0",
43
+ "tsdown": "0.16.1",
44
+ "typescript": "5.9.3",
45
+ "vitest": "4.0.18"
46
+ },
47
+ "type": "module",
48
+ "exports": {
49
+ ".": {
50
+ "import": "./dist/index.mjs",
51
+ "require": "./dist/index.cjs"
52
+ },
53
+ "./cli": {
54
+ "import": "./dist/cli.mjs",
55
+ "require": "./dist/cli.cjs"
56
+ },
57
+ "./package.json": "./package.json"
58
+ },
59
+ "publishConfig": {
60
+ "registry": "https://registry.npmjs.org/",
61
+ "access": "public"
62
+ },
63
+ "scripts": {
64
+ "dev": "node --loader ts-node/esm src/cli.ts mcp-serve",
65
+ "build": "tsdown",
66
+ "test": "vitest --run",
67
+ "typecheck": "tsc --noEmit"
68
+ }
69
+ }