@@ -2,7 +2,90 @@ import { z } from 'zod'
22
33import { handleWorkerLogs , handleWorkerLogsKeys } from '@repo/mcp-common/src/api/workers-logs'
44
5- import type { MyMCP } from '../index'
5+ import type { zReturnedQueryRunEvents } from '@repo/mcp-common/src/types/workers-logs-schemas'
6+ import type { ObservabilityMCP } from '../index'
7+
8+ type RelevantLogInfo = z . infer < typeof RelevantLogInfoSchema >
9+ const RelevantLogInfoSchema = z . object ( {
10+ timestamp : z . string ( ) ,
11+ path : z . string ( ) . nullable ( ) ,
12+ method : z . string ( ) . nullable ( ) ,
13+ status : z . number ( ) . nullable ( ) ,
14+ outcome : z . string ( ) ,
15+ eventType : z . string ( ) ,
16+ duration : z . number ( ) . nullable ( ) ,
17+ error : z . string ( ) . nullable ( ) ,
18+ message : z . string ( ) . nullable ( ) ,
19+ requestId : z . string ( ) ,
20+ rayId : z . string ( ) . nullable ( ) ,
21+ exceptionStack : z . string ( ) . nullable ( ) ,
22+ } )
23+
24+ /**
25+ * Extracts only the most relevant information from a worker log event ( this is to avoid crashing Claude when returning too much data )
26+ * @param event z.array(zReturnedTelemetryEvent).optional()
27+ * @returns Relevant information extracted from the log
28+ */
29+ function extractRelevantLogInfo ( events : zReturnedQueryRunEvents [ 'events' ] = [ ] ) : RelevantLogInfo [ ] {
30+ return events . map ( ( event ) => {
31+ const workers = event . $workers
32+ const metadata = event . $metadata
33+ const source = event . source
34+
35+ let path = null
36+ let method = null
37+ let status = null
38+ if ( workers ?. event ?. request ) {
39+ path = workers . event . request . path ?? null
40+ method = workers . event . request . method ?? null
41+ }
42+
43+ if ( workers ?. event ?. response ) {
44+ status = workers . event . response . status ?? null
45+ }
46+
47+ let error = null
48+ if ( metadata . error ) {
49+ error = metadata . error
50+ }
51+
52+ let message = metadata ?. message ?? null
53+ if ( ! message ) {
54+ if ( workers ?. event ?. rpcMethod ) {
55+ message = `RPC: ${ workers . event . rpcMethod } `
56+ } else if ( path && method ) {
57+ message = `${ method } ${ path } `
58+ }
59+ }
60+
61+ // Calculate duration
62+ const duration = ( workers ?. wallTimeMs || 0 ) + ( workers ?. cpuTimeMs || 0 )
63+
64+ // Extract rayId if available
65+ const rayId = workers ?. event ?. rayId ?? null
66+
67+ let exceptionStack = null
68+ // Extract exception stack if available
69+ if ( typeof source !== 'string' ) {
70+ exceptionStack = source ?. exception ?. stack ?? null
71+ }
72+
73+ return {
74+ timestamp : new Date ( event . timestamp ) . toISOString ( ) ,
75+ path,
76+ method,
77+ status,
78+ outcome : workers ?. outcome || 'unknown' ,
79+ eventType : workers ?. eventType || 'unknown' ,
80+ duration : duration || null ,
81+ error,
82+ message,
83+ requestId : workers ?. requestId || metadata ?. id || 'unknown' ,
84+ rayId,
85+ exceptionStack,
86+ }
87+ } )
88+ }
689
790// Worker logs parameter schema
891const workerNameParam = z . string ( ) . describe ( 'The name of the worker to analyze logs for' )
@@ -27,7 +110,7 @@ const rayIdParam = z.string().optional().describe('Filter logs by specific Cloud
27110 * @param accountId Cloudflare account ID
28111 * @param apiToken Cloudflare API token
29112 */
30- export function registerLogsTools ( agent : MyMCP ) {
113+ export function registerLogsTools ( agent : ObservabilityMCP ) {
31114 // Register the worker logs analysis tool by worker name
32115 agent . server . tool (
33116 'worker_logs_by_worker_name' ,
@@ -125,12 +208,13 @@ export function registerLogsTools(agent: MyMCP) {
125208 shouldFilterErrors,
126209 rayId,
127210 } )
211+ const events = logs ?. events ?. events ?? [ ]
128212 return {
129213 content : [
130214 {
131215 type : 'text' ,
132216 text : JSON . stringify ( {
133- logs ,
217+ events : extractRelevantLogInfo ( events ) ,
134218 stats : {
135219 timeRange : {
136220 from,
0 commit comments