1+ import { z } from "zod"
12import type { ExtensionChatMessage } from "../../../types/messages.js"
23import type { ToolData , McpServerData , FollowUpData , ApiReqInfo , ImageData } from "./types.js"
34
@@ -21,39 +22,32 @@ export function parseToolData(message: ExtensionChatMessage): ToolData | null {
2122}
2223
2324/**
24- * Type guard to check if an object is valid McpServerData
25+ * Zod schema for MCP server data validation
2526 */
26- export function isMcpServerData ( obj : any ) : obj is McpServerData {
27- if ( ! obj || typeof obj !== "object" ) return false
28-
29- // Check required fields
30- if ( ! obj . type || typeof obj . serverName !== "string" ) return false
31-
32- // Validate type field
33- if ( obj . type !== "use_mcp_tool" && obj . type !== "access_mcp_resource" ) return false
34-
35- // Type-specific validation
36- if ( obj . type === "use_mcp_tool" ) {
37- // Tool use should have toolName
38- if ( obj . toolName !== undefined && typeof obj . toolName !== "string" ) return false
39- } else if ( obj . type === "access_mcp_resource" ) {
40- // Resource access should have uri
41- if ( obj . uri !== undefined && typeof obj . uri !== "string" ) return false
42- }
43-
44- // Validate optional fields if present
45- if ( obj . arguments !== undefined && typeof obj . arguments !== "string" ) return false
27+ const McpServerDataSchema = z . object ( {
28+ type : z . enum ( [ "use_mcp_tool" , "access_mcp_resource" ] ) ,
29+ serverName : z . string ( ) ,
30+ toolName : z . string ( ) . optional ( ) ,
31+ arguments : z . string ( ) . optional ( ) ,
32+ uri : z . string ( ) . optional ( ) ,
33+ response : z . any ( ) . optional ( ) ,
34+ } )
4635
47- return true
36+ /**
37+ * Type guard to check if an object is valid McpServerData
38+ * Uses Zod for validation
39+ */
40+ export function isMcpServerData ( obj : unknown ) : obj is McpServerData {
41+ return McpServerDataSchema . safeParse ( obj ) . success
4842}
4943
5044/**
51- * Parse MCP server data from message with validation
45+ * Parse MCP server data from message with Zod validation
5246 */
5347export function parseMcpServerData ( message : ExtensionChatMessage ) : McpServerData | null {
54- const parsed = parseMessageJson < McpServerData > ( message . text )
55- if ( ! parsed || ! isMcpServerData ( parsed ) ) return null
56- return parsed
48+ const parsed = parseMessageJson ( message . text )
49+ const result = McpServerDataSchema . safeParse ( parsed )
50+ return result . success ? ( result . data as McpServerData ) : null
5751}
5852
5953/**
@@ -253,31 +247,14 @@ export function formatJson(jsonString: string, indent: number = 2): string | nul
253247}
254248
255249/**
256- * Calculate byte size of string without creating full byte array for large strings
257- * Uses chunked approach for strings over 10KB to avoid memory spike
250+ * Approximate byte size of string for display purposes
251+ * Uses 3x multiplier as conservative estimate (handles ASCII, UTF-8, emoji)
258252 *
259253 * @param str - The string to measure
260- * @returns The byte size in UTF-8 encoding
254+ * @returns Approximate byte size
261255 */
262- function calculateByteSize ( str : string ) : number {
263- const CHUNK_THRESHOLD = 10000
264- const CHUNK_SIZE = 10000
265-
266- // For small strings, use TextEncoder directly (faster)
267- if ( str . length < CHUNK_THRESHOLD ) {
268- return new TextEncoder ( ) . encode ( str ) . length
269- }
270-
271- // For large strings, chunk it to avoid memory spike
272- const encoder = new TextEncoder ( )
273- let totalBytes = 0
274-
275- for ( let i = 0 ; i < str . length ; i += CHUNK_SIZE ) {
276- const chunk = str . slice ( i , Math . min ( i + CHUNK_SIZE , str . length ) )
277- totalBytes += encoder . encode ( chunk ) . length
278- }
279-
280- return totalBytes
256+ function approximateByteSize ( str : string ) : number {
257+ return str . length * 3
281258}
282259
283260/**
@@ -323,7 +300,7 @@ export function formatContentWithMetadata(
323300 const lines = content . split ( "\n" )
324301 const lineCount = lines . length
325302 const charCount = content . length
326- const byteSize = calculateByteSize ( content )
303+ const byteSize = approximateByteSize ( content )
327304
328305 // Determine if preview is needed
329306 const isPreview = lineCount > maxLines
@@ -348,11 +325,12 @@ export function formatContentWithMetadata(
348325
349326/**
350327 * Format byte size for display
328+ * Adds ~ prefix for approximations (KB/MB)
351329 */
352330export function formatByteSize ( bytes : number ) : string {
353331 if ( bytes < 1024 ) return `${ bytes } B`
354- if ( bytes < 1024 * 1024 ) return `${ ( bytes / 1024 ) . toFixed ( 1 ) } KB`
355- return `${ ( bytes / ( 1024 * 1024 ) ) . toFixed ( 1 ) } MB`
332+ if ( bytes < 1024 * 1024 ) return `~ ${ ( bytes / 1024 ) . toFixed ( 1 ) } KB`
333+ return `~ ${ ( bytes / ( 1024 * 1024 ) ) . toFixed ( 1 ) } MB`
356334}
357335
358336/**
0 commit comments