[TT-16566] mcp policy filter tools resource prompt lists #7868
probelabs / Visor: performance
failed
Apr 16, 2026 in 1m 15s
🚨 Check Failed
performance check failed because fail_if condition was met.
Details
📊 Summary
- Total Issues: 4
- Error Issues: 2
- Warning Issues: 2
🔍 Failure Condition Results
Failed Conditions
- global_fail_if: output.issues && output.issues.some(i => i.severity === 'critical' || i.severity === 'error')
- Severity: ❌ error
Issues by Category
Performance (3)
- ❌ internal/mcp/list_filter.go:83 - The
FilterItemsfunction exhibits an N+1 JSON parsing anti-pattern. It iterates over a slice ofjson.RawMessageand callsExtractStringFieldfor each item.ExtractStringFieldin turn callsjson.Unmarshalon each item's raw JSON just to extract a single field. For a list of N items, this results in at least N separate unmarshaling operations within a loop, which is highly inefficient and leads to excessive allocations and CPU usage, especially for large lists. ⚠️ internal/mcp/list_filter.go:205 - ThematchPatternfunction callsregexp.Compileon every invocation. This function is called within a loop inCheckAccessControlRulesfor each access control pattern against each item in the list. While thetyk/regexppackage provides caching, this still results in repeated cache lookup overhead (map access, mutex locks) in a hot path. For a list of 1000 items and 10 rules, this can be called 10,000 times per request.⚠️ gateway/res_handler_mcp_list_filter.go:65 - TheHandleResponsefunction reads the entire response body into memory viareadAndCloseBody(io.ReadAll) before filtering. For very large list responses, this can cause significant memory allocations and put pressure on the garbage collector. While the author's performance analysis notes this is acceptable for the current expected scale, it presents a potential scalability risk if list sizes grow significantly.
Logic (1)
- ❌ system:0 - Global failure condition met: output.issues && output.issues.some(i => i.severity === 'critical' || i.severity === 'error')
Powered by Visor from Probelabs
💡 TIP: You can chat with Visor using /visor ask <your question>
Annotations
Check failure on line 83 in internal/mcp/list_filter.go
probelabs / Visor: performance
performance Issue
The `FilterItems` function exhibits an N+1 JSON parsing anti-pattern. It iterates over a slice of `json.RawMessage` and calls `ExtractStringField` for each item. `ExtractStringField` in turn calls `json.Unmarshal` on each item's raw JSON just to extract a single field. For a list of N items, this results in at least N separate unmarshaling operations within a loop, which is highly inefficient and leads to excessive allocations and CPU usage, especially for large lists.
Raw output
Refactor the filtering logic to avoid unmarshaling each item individually inside the loop. Instead, unmarshal the entire list into a structured Go type (e.g., `[]map[string]any`) once. Then, iterate over this slice, perform the filtering by accessing the fields via map lookups, and finally, re-marshal the filtered slice back to JSON. This changes the complexity from O(N * M) where M is item parsing cost, to O(N) for filtering after a single initial parse.
Check warning on line 205 in internal/mcp/list_filter.go
probelabs / Visor: performance
performance Issue
The `matchPattern` function calls `regexp.Compile` on every invocation. This function is called within a loop in `CheckAccessControlRules` for each access control pattern against each item in the list. While the `tyk/regexp` package provides caching, this still results in repeated cache lookup overhead (map access, mutex locks) in a hot path. For a list of 1000 items and 10 rules, this can be called 10,000 times per request.
Raw output
Pre-compile all regex patterns from the `user.AccessControlRules` once per request, before starting the item filtering loop. The resulting `*regexp.Regexp` objects can be stored in a temporary struct or slice and reused for all items in that request. This avoids the overhead of repeated compilation or cache lookups inside the main filtering loop.
Check warning on line 65 in gateway/res_handler_mcp_list_filter.go
probelabs / Visor: performance
performance Issue
The `HandleResponse` function reads the entire response body into memory via `readAndCloseBody` (`io.ReadAll`) before filtering. For very large list responses, this can cause significant memory allocations and put pressure on the garbage collector. While the author's performance analysis notes this is acceptable for the current expected scale, it presents a potential scalability risk if list sizes grow significantly.
Raw output
For future optimization, consider replacing the full-body read with a streaming JSON parser. This would allow filtering the list without holding the entire response in memory, albeit at the cost of increased implementation complexity. No immediate action is required, but this should be monitored.
Loading