Skip to content

Commit 5b3fd63

Browse files
committed
Use extractRelevantLogInfo() to extract relevant log information from worker log event in order to avoid issues with overwhelming models with too much context
1 parent 7d6a3e4 commit 5b3fd63

File tree

4 files changed

+123
-8
lines changed

4 files changed

+123
-8
lines changed

apps/workers-observability/src/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export type Props = {
2424

2525
export type State = { activeAccountId: string | null }
2626

27-
export class MyMCP extends McpAgent<Env, State, Props> {
27+
export class ObservabilityMCP extends McpAgent<Env, State, Props> {
2828
server = new McpServer({
2929
name: 'Remote MCP Server with Workers Observability',
3030
version: '1.0.0',
@@ -69,7 +69,7 @@ export class MyMCP extends McpAgent<Env, State, Props> {
6969
export default new OAuthProvider({
7070
apiRoute: '/sse',
7171
// @ts-ignore
72-
apiHandler: MyMCP.mount('/sse'),
72+
apiHandler: ObservabilityMCP.mount('/sse'),
7373
// @ts-ignore
7474
defaultHandler: CloudflareAuthHandler,
7575
authorizeEndpoint: '/oauth/authorize',

apps/workers-observability/src/tools/logs.ts

Lines changed: 87 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,90 @@ import { z } from 'zod'
22

33
import { 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
891
const 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,

packages/mcp-common/src/api/workers-logs.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { fetchCloudflareApi } from '../cloudflare-api'
2-
import { zKeysResponse, zReturnedQueryRunResult } from '../schemas/workers-logs-schemas'
2+
import { zKeysResponse, zReturnedQueryRunResult } from '../types/workers-logs-schemas'
33
import { V4Schema } from '../v4-api'
44

55
/**

packages/mcp-common/src/schemas/workers-logs-schemas.ts renamed to packages/mcp-common/src/types/workers-logs-schemas.ts

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,27 @@ export const zStatistics = z.object({
115115
bytes_read: z.number(),
116116
})
117117

118+
const zCloudflareMiniEventDetailsRequest = z.object({
119+
url: z.string().optional(),
120+
method: z.string().optional(),
121+
path: z.string().optional(),
122+
search: z.record(z.string()).optional(),
123+
})
124+
125+
const zCloudflareMiniEventDetailsResponse = z.object({
126+
status: z.number().optional(),
127+
})
128+
129+
const zCloudflareMiniEventDetails = z.object({
130+
request: zCloudflareMiniEventDetailsRequest.optional(),
131+
response: zCloudflareMiniEventDetailsResponse.optional(),
132+
rpcMethod: z.string().optional(),
133+
rayId: z.string().optional(),
134+
executionModel: z.string().optional(),
135+
})
136+
118137
export const zCloudflareMiniEvent = z.object({
119-
event: z.record(z.string(), z.unknown()).optional(),
138+
event: zCloudflareMiniEventDetails,
120139
scriptName: z.string(),
121140
outcome: z.string(),
122141
eventType: z.enum([
@@ -161,10 +180,21 @@ export const zCloudflareEvent = zCloudflareMiniEvent.extend({
161180
cpuTimeMs: z.number(),
162181
})
163182

183+
const zSourceSchema = z.object({
184+
exception: z
185+
.object({
186+
stack: z.string().optional(),
187+
name: z.string().optional(),
188+
message: z.string().optional(),
189+
timestamp: z.number().optional(),
190+
})
191+
.optional(),
192+
})
193+
164194
export const zReturnedTelemetryEvent = z.object({
165195
dataset: z.string(),
166196
timestamp: z.number().int().positive(),
167-
source: z.union([z.string(), z.object({})]),
197+
source: z.union([z.string(), zSourceSchema]),
168198
$workers: z.union([zCloudflareMiniEvent, zCloudflareEvent]).optional(),
169199
$metadata: z.object({
170200
id: z.string(),
@@ -198,6 +228,7 @@ export const zReturnedTelemetryEvent = z.object({
198228
}),
199229
})
200230

231+
export type zReturnedQueryRunEvents = z.infer<typeof zReturnedQueryRunEvents>
201232
export const zReturnedQueryRunEvents = z.object({
202233
events: z.array(zReturnedTelemetryEvent).optional(),
203234
fields: z

0 commit comments

Comments
 (0)