Skip to content

Commit 71e75db

Browse files
authored
Fix large payload middleware: populate CallToolResult.Content field (#766)
# Fix: Large Payload Middleware Not Returning Metadata to Clients ## Problem Statement The MCP Gateway's large payload storage feature was not working as expected. During test run 21762627709, the agent received a full 672KB payload without any metadata (payloadPath, queryID, schema) despite the middleware being enabled. ## Root Cause Analysis ### What Was Working ✅ 1. Middleware was correctly applied to all non-sys tools 2. Payloads were being saved to `/tmp/jq-payloads/{sessionID}/{queryID}/payload.json` 3. Metadata (queryID, payloadPath, schema) was being generated 4. Gateway was running in routed mode (default) ### What Was Broken ❌ The middleware returned the transformed response in the **second return value**: ```go return result, rewrittenResponse, nil ``` But when wrapping handlers for the MCP SDK, this value was **discarded**: ```go // In unified.go:363 and routed.go result, _, err := handler(ctx, req, nil) // The _ throws away metadata! return result, err ``` The MCP protocol only sends the `CallToolResult` to clients, which wasn't being populated with the transformed response. Clients received the original `CallToolResult` without any payload metadata. ## Solution Implemented Modified `internal/middleware/jqschema.go` to: 1. Marshal the transformed response (queryID, payloadPath, schema, preview, originalSize, truncated) to JSON 2. Create a **new** `CallToolResult` with the JSON in the `Content` field as `TextContent` 3. Return this new result instead of the original ```go transformedResult := &sdk.CallToolResult{ Content: []sdk.Content{ &sdk.TextContent{ Text: string(rewrittenJSON), }, }, IsError: result.IsError, Meta: result.Meta, } return transformedResult, rewrittenResponse, nil ``` ## Enhanced Logging (Latest Update) Added extensive logging throughout the payload storage process: **Operational Logs (to file):** - `logger.LogInfo` - Payload storage lifecycle, file operations, sizes - `logger.LogDebug` - Directory creation, verification, metadata details - `logger.LogWarn` - Schema generation failures, verification issues - `logger.LogError` - Marshal failures, file write errors **Debug Logs (console when DEBUG enabled):** - `logMiddleware.Printf` - Existing debug output for development **Key Logged Information:** - Directory creation with full paths and permissions - File write operations with size in bytes, KB, and MB - Payload verification after write - Metadata response generation - Client-side path translation hints - Error context with tool, queryID, and session information ## Testing & Verification ### Unit Tests ✅ - All 23 middleware tests pass - Tests verify Content field contains metadata - Tests verify truncation behavior ### Integration Tests ✅ - All 3 integration tests pass - End-to-end flow verified - Content field validation confirmed ### System Tests ✅ - All 28 integration tests pass - Build completes successfully - Linting passes - Code review found no issues - Security scan (CodeQL) found no vulnerabilities ## Impact & Benefits ### For Agents 🤖 - Will now receive metadata in tool responses - Can access full payloads from mounted directory - Can use schema to understand payload structure ### For Debugging 🔍 - Comprehensive logs track entire payload lifecycle - File system operations fully visible - Error conditions logged with context - Easy to trace payload storage issues ### Backward Compatibility 🔄 - Minimal change to middleware only - Internal data return value still contains transformed response - Protocol-compliant using standard MCP TextContent - No breaking changes to existing code ## Files Changed - `internal/middleware/jqschema.go` - Core fix + enhanced logging <!-- START COPILOT ORIGINAL PROMPT --> <details> <summary>Original prompt</summary> > > ---- > > *This section details on the original issue you should resolve* > > <issue_title>[large-payload-test] Large Payload Test - 21762627709</issue_title> > <issue_description># Large MCP Payload Access Test Results > > **Run ID:** 21762627709 > **Status:** PARTIAL PASS > **Timestamp:** 2026-02-06T19:10:21Z > > ## Test Results > > - **Expected Secret:** test-secret-2786167c-221f-4a0f-a034-f88ef8394279 > - **Found Secret:** test-secret-2786167c-221f-4a0f-a034-f88ef8394279 > - **Secret Match:** YES ✅ > - **Payload Path:** NOT PROVIDED (feature not triggered) > - **Payload Size:** 672,508 bytes (656.7 KB) > > ## Detailed Findings > > ### ✅ What Worked > 1. Successfully read `secret.txt` from filesystem MCP server > 2. Successfully read `large-test-file.json` (672KB file) > 3. Secret validation passed - secrets match correctly > 4. File content integrity verified > > ### ⚠️ What Failed > 1. **Large payload storage not triggered**: Despite 672KB response, MCP Gateway did not store payload to `/tmp/jq-payloads/{sessionID}/{queryID}/payload.json` > 2. **No payload metadata returned**: Response did not include `payloadPath`, `queryID`, or other expected metadata fields > 3. **No stored payload files found**: Search for `payload.json` files in `/tmp` returned no results > > ## Conclusion > > The filesystem MCP server is functioning correctly and can read large files. Secret verification passed. However, the **MCP Gateway's large payload storage feature is not being activated** despite the response size (672KB) exceeding typical thresholds. The gateway appears to be passing through the full content directly rather than storing it and returning metadata. > > This suggests either: > - The jq middleware is not enabled/configured correctly > - The payload size threshold is set higher than 672KB > - The feature is not triggered for filesystem MCP server responses > > --- > Run URL: https://github.com/github/gh-aw-mcpg/actions/runs/21762627709 > > > > > > AI generated by [Large Payload Tester](https://github.com/github/gh-aw-mcpg/actions/runs/21762627709) > > <!-- gh-aw-agentic-workflow: Large Payload Tester, engine: copilot, run: https://github.com/github/gh-aw-mcpg/actions/runs/21762627709 --> > > <!-- gh-aw-workflow-id: large-payload-tester --></issue_description> > > <agent_instructions>Help diagnose why the fully large payload was sent by the gateway instead of sharing it through a common payloadDir path</agent_instructions> > > ## Comments on the Issue (you are @copilot in this section) > > <comments> > </comments> > </details> > **Custom agent used: agentic-workflows** > GitHub Agentic Workflows (gh-aw) - Create, debug, and upgrade AI-powered workflows with intelligent prompt routing <!-- START COPILOT CODING AGENT SUFFIX --> - Fixes #765 <!-- START COPILOT CODING AGENT TIPS --> --- 💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more [Copilot coding agent tips](https://gh.io/copilot-coding-agent-tips) in the docs.
2 parents bf97f89 + 27dbad2 commit 71e75db

3 files changed

Lines changed: 202 additions & 22 deletions

File tree

internal/middleware/jqschema.go

Lines changed: 101 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,16 +113,41 @@ func applyJqSchema(ctx context.Context, jsonData interface{}) (string, error) {
113113
func savePayload(baseDir, sessionID, queryID string, payload []byte) (string, error) {
114114
// Create directory structure: {baseDir}/{sessionID}/{queryID}
115115
dir := filepath.Join(baseDir, sessionID, queryID)
116+
117+
logger.LogDebug("payload", "Creating payload directory: baseDir=%s, session=%s, query=%s, fullPath=%s",
118+
baseDir, sessionID, queryID, dir)
119+
116120
if err := os.MkdirAll(dir, 0700); err != nil {
121+
logger.LogError("payload", "Failed to create payload directory: path=%s, error=%v", dir, err)
117122
return "", fmt.Errorf("failed to create payload directory: %w", err)
118123
}
119124

125+
logger.LogDebug("payload", "Successfully created payload directory: path=%s, permissions=0700", dir)
126+
120127
// Save payload to file with restrictive permissions (owner read/write only)
121128
filePath := filepath.Join(dir, "payload.json")
129+
payloadSize := len(payload)
130+
131+
logger.LogInfo("payload", "Writing large payload to filesystem: path=%s, size=%d bytes (%.2f KB, %.2f MB)",
132+
filePath, payloadSize, float64(payloadSize)/1024, float64(payloadSize)/(1024*1024))
133+
122134
if err := os.WriteFile(filePath, payload, 0600); err != nil {
135+
logger.LogError("payload", "Failed to write payload file: path=%s, size=%d bytes, error=%v",
136+
filePath, payloadSize, err)
123137
return "", fmt.Errorf("failed to write payload file: %w", err)
124138
}
125139

140+
logger.LogInfo("payload", "Successfully saved large payload to filesystem: path=%s, size=%d bytes, permissions=0600",
141+
filePath, payloadSize)
142+
143+
// Verify file was written correctly
144+
if stat, err := os.Stat(filePath); err != nil {
145+
logger.LogWarn("payload", "Could not verify payload file after write: path=%s, error=%v", filePath, err)
146+
} else {
147+
logger.LogDebug("payload", "Payload file verified: path=%s, size=%d bytes, mode=%s",
148+
filePath, stat.Size(), stat.Mode())
149+
}
150+
126151
return filePath, nil
127152
}
128153

@@ -149,37 +174,66 @@ func WrapToolHandler(
149174
}
150175

151176
logMiddleware.Printf("Processing tool call: tool=%s, queryID=%s, sessionID=%s", toolName, queryID, sessionID)
177+
logger.LogDebug("payload", "Middleware processing tool call: tool=%s, queryID=%s, session=%s, baseDir=%s",
178+
toolName, queryID, sessionID, baseDir)
152179

153180
// Call the original handler
154181
result, data, err := handler(ctx, req, args)
155182
if err != nil {
156183
logMiddleware.Printf("Tool call failed: tool=%s, queryID=%s, sessionID=%s, error=%v", toolName, queryID, sessionID, err)
184+
logger.LogDebug("payload", "Tool call failed, skipping payload storage: tool=%s, queryID=%s, error=%v",
185+
toolName, queryID, err)
157186
return result, data, err
158187
}
159188

160189
// Only process successful results with data
161190
if result == nil || result.IsError || data == nil {
191+
logger.LogDebug("payload", "Skipping payload storage: tool=%s, queryID=%s, reason=%s",
192+
toolName, queryID,
193+
func() string {
194+
if result == nil {
195+
return "result is nil"
196+
} else if result.IsError {
197+
return "result indicates error"
198+
} else {
199+
return "no data returned"
200+
}
201+
}())
162202
return result, data, err
163203
}
164204

165205
// Marshal the response data to JSON
166206
payloadJSON, marshalErr := json.Marshal(data)
167207
if marshalErr != nil {
168208
logMiddleware.Printf("Failed to marshal response: tool=%s, queryID=%s, error=%v", toolName, queryID, marshalErr)
209+
logger.LogError("payload", "Failed to marshal response data to JSON: tool=%s, queryID=%s, error=%v",
210+
toolName, queryID, marshalErr)
169211
return result, data, err
170212
}
171213

214+
payloadSize := len(payloadJSON)
215+
logger.LogInfo("payload", "Response data marshaled to JSON: tool=%s, queryID=%s, size=%d bytes (%.2f KB, %.2f MB)",
216+
toolName, queryID, payloadSize, float64(payloadSize)/1024, float64(payloadSize)/(1024*1024))
217+
172218
// Save the payload
219+
logger.LogInfo("payload", "Starting payload storage to filesystem: tool=%s, queryID=%s, session=%s, baseDir=%s",
220+
toolName, queryID, sessionID, baseDir)
221+
173222
filePath, saveErr := savePayload(baseDir, sessionID, queryID, payloadJSON)
174223
if saveErr != nil {
175224
logMiddleware.Printf("Failed to save payload: tool=%s, queryID=%s, sessionID=%s, error=%v", toolName, queryID, sessionID, saveErr)
225+
logger.LogError("payload", "Failed to save payload to filesystem: tool=%s, queryID=%s, session=%s, error=%v",
226+
toolName, queryID, sessionID, saveErr)
176227
// Continue even if save fails - don't break the tool call
177228
} else {
178229
logMiddleware.Printf("Saved payload: tool=%s, queryID=%s, sessionID=%s, path=%s, size=%d bytes",
179230
toolName, queryID, sessionID, filePath, len(payloadJSON))
231+
logger.LogInfo("payload", "Payload storage completed successfully: tool=%s, queryID=%s, session=%s, path=%s, size=%d bytes",
232+
toolName, queryID, sessionID, filePath, len(payloadJSON))
180233
}
181234

182235
// Apply jq schema transformation
236+
logger.LogDebug("payload", "Applying jq schema transformation: tool=%s, queryID=%s", toolName, queryID)
183237
var schemaJSON string
184238
if schemaErr := func() error {
185239
// Unmarshal to interface{} for jq processing
@@ -196,17 +250,27 @@ func WrapToolHandler(
196250
return nil
197251
}(); schemaErr != nil {
198252
logMiddleware.Printf("Failed to apply jq schema: tool=%s, queryID=%s, sessionID=%s, error=%v", toolName, queryID, sessionID, schemaErr)
253+
logger.LogWarn("payload", "Failed to generate schema for payload: tool=%s, queryID=%s, error=%v",
254+
toolName, queryID, schemaErr)
199255
// Continue with original response if schema extraction fails
200256
return result, data, err
201257
}
202258

259+
logger.LogDebug("payload", "Schema transformation completed: tool=%s, queryID=%s, schemaSize=%d bytes",
260+
toolName, queryID, len(schemaJSON))
261+
203262
// Build the transformed response: first 500 chars + schema
204263
payloadStr := string(payloadJSON)
205264
var preview string
206-
if len(payloadStr) > 500 {
265+
truncated := len(payloadStr) > 500
266+
if truncated {
207267
preview = payloadStr[:500] + "..."
268+
logger.LogInfo("payload", "Payload truncated for preview: tool=%s, queryID=%s, originalSize=%d bytes, previewSize=500 bytes",
269+
toolName, queryID, len(payloadStr))
208270
} else {
209271
preview = payloadStr
272+
logger.LogDebug("payload", "Payload small enough for full preview: tool=%s, queryID=%s, size=%d bytes",
273+
toolName, queryID, len(payloadStr))
210274
}
211275

212276
// Create rewritten response
@@ -216,19 +280,52 @@ func WrapToolHandler(
216280
"preview": preview,
217281
"schema": schemaJSON,
218282
"originalSize": len(payloadJSON),
219-
"truncated": len(payloadStr) > 500,
283+
"truncated": truncated,
220284
}
221285

222286
logMiddleware.Printf("Rewritten response: tool=%s, queryID=%s, sessionID=%s, originalSize=%d, truncated=%v",
223-
toolName, queryID, sessionID, len(payloadJSON), len(payloadStr) > 500)
287+
toolName, queryID, sessionID, len(payloadJSON), truncated)
288+
logger.LogInfo("payload", "Created metadata response for client: tool=%s, queryID=%s, session=%s, payloadPath=%s, originalSize=%d bytes, truncated=%v",
289+
toolName, queryID, sessionID, filePath, len(payloadJSON), truncated)
224290

225291
// Parse the schema JSON string back to an object for cleaner display
226292
var schemaObj interface{}
227293
if err := json.Unmarshal([]byte(schemaJSON), &schemaObj); err == nil {
228294
rewrittenResponse["schema"] = schemaObj
229295
}
230296

231-
return result, rewrittenResponse, nil
297+
// Marshal the rewritten response to JSON for the Content field
298+
rewrittenJSON, marshalErr := json.Marshal(rewrittenResponse)
299+
if marshalErr != nil {
300+
logMiddleware.Printf("Failed to marshal rewritten response: tool=%s, queryID=%s, error=%v", toolName, queryID, marshalErr)
301+
logger.LogError("payload", "Failed to marshal metadata response: tool=%s, queryID=%s, error=%v",
302+
toolName, queryID, marshalErr)
303+
// Fall back to original result if we can't marshal
304+
return result, rewrittenResponse, nil
305+
}
306+
307+
logger.LogDebug("payload", "Metadata response marshaled: tool=%s, queryID=%s, metadataSize=%d bytes",
308+
toolName, queryID, len(rewrittenJSON))
309+
310+
// Create a new CallToolResult with the transformed content
311+
// Replace the original content with our rewritten response
312+
transformedResult := &sdk.CallToolResult{
313+
Content: []sdk.Content{
314+
&sdk.TextContent{
315+
Text: string(rewrittenJSON),
316+
},
317+
},
318+
IsError: result.IsError,
319+
Meta: result.Meta,
320+
}
321+
322+
logMiddleware.Printf("Transformed result with metadata: tool=%s, queryID=%s, sessionID=%s", toolName, queryID, sessionID)
323+
logger.LogInfo("payload", "Returning transformed response to client: tool=%s, queryID=%s, session=%s, payloadPath=%s, clientReceivesMetadata=true",
324+
toolName, queryID, sessionID, filePath)
325+
logger.LogInfo("payload", "Client can access full payload at: %s (inside container: /workspace/mcp-payloads/%s/%s/payload.json)",
326+
filePath, sessionID, queryID)
327+
328+
return transformedResult, rewrittenResponse, nil
232329
}
233330
}
234331

internal/middleware/jqschema_integration_test.go

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,30 @@ func TestMiddlewareIntegration(t *testing.T) {
6464
require.NotNil(t, result, "Result should not be nil")
6565
assert.False(t, result.IsError, "Result should not indicate error")
6666

67-
// Verify response structure
67+
// Verify the result Content field contains the transformed response
68+
require.NotEmpty(t, result.Content, "Result should have Content")
69+
textContent, ok := result.Content[0].(*sdk.TextContent)
70+
require.True(t, ok, "Content should be TextContent")
71+
require.NotEmpty(t, textContent.Text, "TextContent should have text")
72+
73+
// Parse the JSON from Content
74+
var contentMap map[string]interface{}
75+
err = json.Unmarshal([]byte(textContent.Text), &contentMap)
76+
require.NoError(t, err, "Content should be valid JSON")
77+
78+
// Verify all required fields exist in Content
79+
assert.Contains(t, contentMap, "queryID", "Content should contain queryID")
80+
assert.Contains(t, contentMap, "payloadPath", "Content should contain payloadPath")
81+
assert.Contains(t, contentMap, "preview", "Content should contain preview")
82+
assert.Contains(t, contentMap, "schema", "Content should contain schema")
83+
assert.Contains(t, contentMap, "originalSize", "Content should contain originalSize")
84+
assert.Contains(t, contentMap, "truncated", "Content should contain truncated")
85+
86+
// Verify queryID format in Content
87+
queryIDFromContent := contentMap["queryID"].(string)
88+
assert.Len(t, queryIDFromContent, 32, "QueryID should be 32 hex characters")
89+
90+
// Verify response structure in data return value (for internal use)
6891
dataMap, ok := data.(map[string]interface{})
6992
require.True(t, ok, "Response should be a map")
7093

@@ -180,6 +203,25 @@ func TestMiddlewareWithLargePayload(t *testing.T) {
180203
require.NoError(t, err)
181204
require.NotNil(t, result)
182205

206+
// Verify Content field has transformed response
207+
require.NotEmpty(t, result.Content, "Result should have Content")
208+
textContent, ok := result.Content[0].(*sdk.TextContent)
209+
require.True(t, ok, "Content should be TextContent")
210+
211+
var contentMap map[string]interface{}
212+
err = json.Unmarshal([]byte(textContent.Text), &contentMap)
213+
require.NoError(t, err, "Content should be valid JSON")
214+
215+
// Verify truncation in Content field
216+
truncatedInContent := contentMap["truncated"].(bool)
217+
previewInContent := contentMap["preview"].(string)
218+
219+
if truncatedInContent {
220+
assert.True(t, len(previewInContent) <= 503, "Preview in Content should be truncated")
221+
assert.Contains(t, previewInContent, "...", "Truncated preview in Content should end with ...")
222+
}
223+
224+
// Also check data return value
183225
dataMap := data.(map[string]interface{})
184226

185227
// Verify truncation occurred
@@ -223,9 +265,24 @@ func TestMiddlewareDirectoryCreation(t *testing.T) {
223265
require.NoError(t, err)
224266
require.NotNil(t, result)
225267

268+
// Verify Content field
269+
require.NotEmpty(t, result.Content, "Result should have Content")
270+
textContent, ok := result.Content[0].(*sdk.TextContent)
271+
require.True(t, ok, "Content should be TextContent")
272+
273+
var contentMap map[string]interface{}
274+
err = json.Unmarshal([]byte(textContent.Text), &contentMap)
275+
require.NoError(t, err, "Content should be valid JSON")
276+
277+
queryIDFromContent := contentMap["queryID"].(string)
278+
279+
// Also check data return value
226280
dataMap := data.(map[string]interface{})
227281
queryID := dataMap["queryID"].(string)
228282

283+
// Both should match
284+
assert.Equal(t, queryID, queryIDFromContent, "QueryID should match in both data and Content")
285+
229286
// Verify directory structure with session ID
230287
expectedDir := filepath.Join(baseDir, sessionID, queryID)
231288
assert.DirExists(t, expectedDir, "Query directory should exist")

internal/middleware/jqschema_test.go

Lines changed: 43 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -140,26 +140,40 @@ func TestWrapToolHandler(t *testing.T) {
140140
require.NotNil(t, result, "Result should not be nil")
141141
assert.False(t, result.IsError, "Result should not be an error")
142142

143-
// Verify rewritten response structure
144-
dataMap, ok := data.(map[string]interface{})
145-
require.True(t, ok, "Data should be a map")
146-
147-
assert.Contains(t, dataMap, "queryID", "Response should contain queryID")
148-
assert.Contains(t, dataMap, "payloadPath", "Response should contain payloadPath")
149-
assert.Contains(t, dataMap, "preview", "Response should contain preview")
150-
assert.Contains(t, dataMap, "schema", "Response should contain schema")
151-
assert.Contains(t, dataMap, "originalSize", "Response should contain originalSize")
152-
assert.Contains(t, dataMap, "truncated", "Response should contain truncated")
143+
// Verify the result Content field contains the transformed response
144+
require.NotEmpty(t, result.Content, "Result should have Content")
145+
textContent, ok := result.Content[0].(*sdk.TextContent)
146+
require.True(t, ok, "Content should be TextContent")
147+
require.NotEmpty(t, textContent.Text, "TextContent should have text")
148+
149+
// Parse the JSON from Content
150+
var contentMap map[string]interface{}
151+
err = json.Unmarshal([]byte(textContent.Text), &contentMap)
152+
require.NoError(t, err, "Content should be valid JSON")
153+
154+
// Verify transformed response in Content field
155+
assert.Contains(t, contentMap, "queryID", "Content should contain queryID")
156+
assert.Contains(t, contentMap, "payloadPath", "Content should contain payloadPath")
157+
assert.Contains(t, contentMap, "preview", "Content should contain preview")
158+
assert.Contains(t, contentMap, "schema", "Content should contain schema")
159+
assert.Contains(t, contentMap, "originalSize", "Content should contain originalSize")
160+
assert.Contains(t, contentMap, "truncated", "Content should contain truncated")
153161

154162
// Verify queryID is a valid hex string
155-
queryID, ok := dataMap["queryID"].(string)
163+
queryID, ok := contentMap["queryID"].(string)
156164
require.True(t, ok, "queryID should be a string")
157165
assert.NotEmpty(t, queryID, "queryID should not be empty")
158166

159167
// Verify schema is present
160-
schema := dataMap["schema"]
168+
schema := contentMap["schema"]
161169
assert.NotNil(t, schema, "Schema should not be nil")
162170

171+
// Also verify rewritten response in data return value (for internal use)
172+
dataMap, ok := data.(map[string]interface{})
173+
require.True(t, ok, "Data should be a map")
174+
assert.Contains(t, dataMap, "queryID", "Data should contain queryID")
175+
assert.Contains(t, dataMap, "payloadPath", "Data should contain payloadPath")
176+
163177
// Clean up test directory
164178
defer os.RemoveAll(filepath.Join("/tmp", "gh-awmg"))
165179
}
@@ -216,14 +230,26 @@ func TestWrapToolHandler_LongPayload(t *testing.T) {
216230
require.NoError(t, err, "Should not return error")
217231
require.NotNil(t, result, "Result should not be nil")
218232

219-
dataMap, ok := data.(map[string]interface{})
220-
require.True(t, ok, "Data should be a map")
233+
// Verify Content field contains the transformed response
234+
require.NotEmpty(t, result.Content, "Result should have Content")
235+
textContent, ok := result.Content[0].(*sdk.TextContent)
236+
require.True(t, ok, "Content should be TextContent")
237+
238+
// Parse the JSON from Content
239+
var contentMap map[string]interface{}
240+
err = json.Unmarshal([]byte(textContent.Text), &contentMap)
241+
require.NoError(t, err, "Content should be valid JSON")
221242

222-
// Verify truncation
223-
assert.True(t, dataMap["truncated"].(bool), "Should indicate truncation")
224-
preview := dataMap["preview"].(string)
243+
// Verify truncation in Content field
244+
assert.True(t, contentMap["truncated"].(bool), "Should indicate truncation in Content")
245+
preview := contentMap["preview"].(string)
225246
assert.LessOrEqual(t, len(preview), 503, "Preview should be truncated to ~500 chars + '...'")
226247
assert.True(t, strings.HasSuffix(preview, "..."), "Preview should end with '...'")
248+
249+
// Also verify in data return value
250+
dataMap, ok := data.(map[string]interface{})
251+
require.True(t, ok, "Data should be a map")
252+
assert.True(t, dataMap["truncated"].(bool), "Should indicate truncation in data")
227253
}
228254

229255
// TestPayloadStorage_SessionIsolation verifies that payloads are stored in session-specific directories

0 commit comments

Comments
 (0)