| name | MCP Response Optimization |
| description | Sophisticated MCP tool management to prevent massive responses from WordPress, Playwright, and other MCPs that can kill conversations on first call. Finely tuned for token efficiency. |
| when_to_use | ALWAYS before ANY MCP tool call, especially: (1) WordPress MCP operations, (2) Playwright browser automation, (3) Notion API queries, (4) Any search/list operations, (5) Large data retrievals. This skill is MANDATORY for all MCP usage. |
| version | 1.0.0 |
| dependencies | none |
MCP Response Optimization - Advanced Token Management
⚠️ THE CRITICAL PROBLEM
Single MCP calls can kill entire conversations:
- WordPress query returns 50 full pages → 100,000+ tokens
- Playwright DOM query returns entire page HTML → 80,000+ tokens
- Notion search returns 100 full pages → 120,000+ tokens
- Result: Conversation dead on first message
🎯 CORE PRINCIPLE
REQUEST MINIMAL, DISPLAY NOTHING, SAVE EVERYTHING
Every MCP call must be:
- Filtered - Request only needed fields
- Limited - Cap result counts aggressively
- Silent - Process without displaying
- Extracted - Pull only essential data
- Saved - Store full results in files if needed
🔴 WORDPRESS MCP - THE WORST OFFENDER
The Problem
WordPress returns EVERYTHING by default:
- Full post content (can be 50KB per post)
- All metadata fields
- Revision history
- Featured images (base64)
- Author details
- Comments
- Categories, tags, custom fields
- One query = conversation killer
The Solution: Aggressive Filtering
Rule 1: Always Use Field Filters
// ❌ NEVER DO THIS:
wordpress.list_posts({ per_page: 100 })
// Returns: 5MB of data, 1,250,000 tokens
// ✅ ALWAYS DO THIS:
wordpress.list_posts({
per_page: 10, // Start small
fields: 'id,title,date,status', // ONLY what you need
context: 'view' // Minimal context
})
// Returns: 5KB of data, 1,250 tokens
Rule 2: Limit Results Aggressively
// Default limits for WordPress MCP:
per_page: 10 // NEVER use 100
page: 1 // Paginate if needed, don't bulk load
Rule 3: Request Specific Fields Only
For Posts:
// Minimal post fields (when listing):
fields: 'id,title,status,date'
// Add fields only when needed:
fields: 'id,title,status,date,excerpt' // If need preview
fields: 'id,title,content' // Only when editing specific post
For Pages:
// Minimal page fields:
fields: 'id,title,status,parent'
For Media:
// NEVER load full images in queries
fields: 'id,title,source_url' // URL only, not base64
Rule 4: Use Specific Queries, Not Broad Searches
// ❌ BAD - Returns everything:
wordpress.list_posts({ search: 'conference' })
// ✅ GOOD - Targeted with filters:
wordpress.list_posts({
search: 'conference',
per_page: 5,
fields: 'id,title,date',
status: 'publish',
orderby: 'date',
order: 'desc'
})
Rule 5: Never Display Full WordPress Responses
// After WordPress MCP call:
// ❌ DON'T:
console.log(response) // Displays everything, huge token waste
// ✅ DO:
const ids = response.map(post => ({ id: post.id, title: post.title }))
console.log(`Found ${ids.length} posts`)
// Then work with IDs only
WordPress MCP Response Patterns
Pattern: List Posts
// Query with minimal data:
const posts = wordpress.list_posts({
per_page: 10,
fields: 'id,title,status,date',
status: 'publish'
})
// Process silently:
const post_ids = posts.map(p => p.id)
// Display only summary:
"Found ${posts.length} posts. Working with: ${post_ids.join(', ')}"
Pattern: Get Single Post
// Even for single posts, limit fields:
const post = wordpress.get_post({
id: 123,
fields: 'id,title,content,status' // Only needed fields
})
// Don't display content in conversation:
"Retrieved post: ${post.title} (${post.id})"
// Save full content to file if needed for editing
Pattern: Search and Filter
// Multi-stage filtering:
const results = wordpress.list_posts({
search: 'keyword',
per_page: 5, // Tiny initial batch
fields: 'id,title,date'
})
// Client-side filtering if needed:
const filtered = results.filter(/* your criteria */)
// Display IDs only:
"Found ${filtered.length} matches: [${filtered.map(p => p.id).join(', ')}]"
🔴 PLAYWRIGHT MCP - THE OTHER KILLER
The Problem
Playwright returns massive DOM data:
- Full page HTML (can be 200KB)
- Complete DOM tree with all attributes
- Inline styles and scripts
- Base64 screenshots (500KB-2MB)
- One screenshot = conversation over
The Solution: Selective Queries
Rule 1: Use CSS Selectors, Not Full DOM
// ❌ NEVER DO THIS:
playwright.evaluate("document.documentElement.outerHTML")
// Returns: Entire page HTML, 50,000+ tokens
// ✅ ALWAYS DO THIS:
playwright.locator('.product-title').textContent()
// Returns: Just the text you need, 10 tokens
Rule 2: Target Specific Elements
// Extract minimal data:
const data = {
title: await page.locator('h1').textContent(),
price: await page.locator('.price').textContent(),
available: await page.locator('.stock-status').textContent()
}
// DON'T extract full page content
Rule 3: Screenshots - Handle Carefully
// ❌ NEVER include screenshot in response:
const screenshot = await page.screenshot()
// This is 500KB-2MB of base64 data
// ✅ SAVE screenshot, don't display:
await page.screenshot({ path: '/outputs/screenshot.png' })
"Screenshot saved: /outputs/screenshot.png"
// 20 tokens vs 125,000 tokens
Rule 4: Use Specific Selectors
// Target exactly what you need:
const products = await page.locator('.product-card').all()
// Extract minimal fields:
const product_data = []
for (const product of products.slice(0, 10)) { // Limit to 10
product_data.push({
name: await product.locator('.name').textContent(),
price: await product.locator('.price').textContent()
})
}
// Save to file, don't display all:
fs.writeFileSync('/outputs/products.json', JSON.stringify(product_data))
"Extracted ${product_data.length} products → /outputs/products.json"
Playwright MCP Response Patterns
Pattern: Page Scraping
// Navigate:
await page.goto(url)
// Extract target data only:
const data = {
title: await page.title(),
h1: await page.locator('h1').first().textContent(),
meta_description: await page.locator('meta[name="description"]').getAttribute('content')
}
// Save, don't display:
fs.writeFileSync('/outputs/page_data.json', JSON.stringify(data))
"Page data saved: /outputs/page_data.json"
Pattern: Element Analysis
// Count elements without loading all:
const count = await page.locator('.product-item').count()
"Found ${count} products"
// Work with first few only:
const sample = await page.locator('.product-item').first().textContent()
"Sample product: ${sample}"
Pattern: Screenshots for User
// Take screenshot, save to outputs:
await page.screenshot({
path: '/mnt/user-data/outputs/page.png',
fullPage: false // Viewport only, smaller file
})
// Return link only:
"[View screenshot](computer:///mnt/user-data/outputs/page.png)"
🔴 NOTION MCP - ALSO PROBLEMATIC
The Problem
Notion returns full page objects with all blocks, which can be massive.
The Solution
// ❌ DON'T:
notion.search({ query: 'project' }) // Returns 100 full pages
// ✅ DO:
notion.search({
query: 'project',
page_size: 10, // Limit results
filter: { property: 'Status', select: { equals: 'Active' } }
})
// Then extract IDs only:
const page_ids = results.map(r => r.id)
"Found ${page_ids.length} pages: ${page_ids}"
🔴 GOOGLE DRIVE MCP - CAN BE LARGE
The Problem
Drive returns full file contents or large metadata sets.
The Solution
// ❌ DON'T:
drive.search({ query: 'report' }) // Returns 1000 files with metadata
// ✅ DO:
drive.search({
query: 'report',
pageSize: 10, // Limit results
fields: 'files(id,name,mimeType)' // Minimal fields
})
📊 TOKEN ESTIMATION GUIDE
Before any MCP call, estimate token impact:
| Operation | Bad Approach | Good Approach |
|---|---|---|
| List 100 WP posts | 1,000,000 tokens | 2,500 tokens (IDs only) |
| Get WP page content | 50,000 tokens | 200 tokens (title + ID) |
| Playwright full DOM | 100,000 tokens | 500 tokens (target elements) |
| Playwright screenshot | 125,000 tokens | 50 tokens (save to file) |
| Notion page search | 80,000 tokens | 1,000 tokens (IDs + titles) |
| Drive file list | 50,000 tokens | 800 tokens (minimal fields) |
🎯 UNIVERSAL MCP RULES
Rule 1: Pre-Call Token Budget
Before EVERY MCP call, ask:
- What's minimum data I actually need?
- Can I filter to specific fields?
- Can I limit result count?
- Will response fit in 2,000 tokens?
If answer to last question is NO → Adjust parameters
Rule 2: Pagination Over Bulk
// ❌ NEVER:
get_all_items({ limit: 1000 })
// ✅ ALWAYS:
get_items({ limit: 10, page: 1 })
// If need more, paginate with user awareness
Rule 3: Save Large Responses
// Any response >5KB → Save to file
// ❌ DON'T:
console.log(large_response)
// ✅ DO:
fs.writeFileSync('/outputs/response.json', JSON.stringify(large_response))
"Response saved: /outputs/response.json"
Rule 4: ID-Based Workflows
// Step 1: Get IDs only
const ids = get_minimal_list()
// Step 2: User picks which ID to work with
"Found ${ids.length} items: ${ids}"
// Step 3: Fetch full details for THAT ONE
const full_item = get_item_by_id(selected_id)
Rule 5: Never Display Raw MCP Responses
// ❌ FATAL ERROR:
"Here's what I found: " + JSON.stringify(mcp_response)
// ✅ CORRECT:
const summary = {
count: mcp_response.length,
items: mcp_response.map(i => i.id)
}
"Found ${summary.count} items: ${summary.items.join(', ')}"
🔧 TOOL-SPECIFIC OPTIMIZATION TABLES
WordPress MCP Field Reference
Never include these fields unless absolutely necessary:
content- Can be 50KB per postexcerpt- Usually 2-5KBfeatured_media- Base64 imagesmeta- Custom fields can be huge_links- Unnecessary metadata
Safe fields (always use these):
id- Essentialtitle- Smallstatus- Tinydate- Smallmodified- Smallslug- Small
Playwright Selector Optimization
Target specific data points:
// Good selectors (minimal data):
'.product-name'
'.price'
'h1'
'meta[name="description"]'
// Bad selectors (huge data):
'body'
'*'
'[class]' // Selects everything
Notion Query Optimization
Minimal query structure:
{
page_size: 10, // Never more than 20
filter: { /* specific filter */ },
sorts: [{ property: 'Modified', direction: 'descending' }]
}
📋 PRE-CALL CHECKLIST
Before EVERY MCP tool call:
- Minimum fields requested?
- Result limit set to <20?
- Estimated response <2,000 tokens?
- Plan to save, not display?
- ID-based workflow possible?
- Pagination strategy ready?
If all YES → Proceed If any NO → Adjust parameters
🚨 EMERGENCY PATTERNS
If MCP Response Already Too Large
// Response came back huge (>50,000 tokens)
// DON'T try to work with it in conversation
// DO this immediately:
// 1. Save full response to file
fs.writeFileSync('/outputs/large_response.json', JSON.stringify(response))
// 2. Extract minimal summary
const summary = {
count: response.length,
first_id: response[0]?.id,
last_id: response[response.length - 1]?.id
}
// 3. Return summary only
"Response too large, saved to /outputs/large_response.json"
"Summary: ${summary.count} items (${summary.first_id} to ${summary.last_id})"
If Conversation Already Dying
// Recognize early warning signs:
// - Response was truncated
// - Error: "context too long"
// - Slow response times
// Immediately save state:
fs.writeFileSync('/outputs/conversation_state.json', {
last_action: "WordPress query",
context: "Querying posts",
next_step: "Process post IDs 123, 456, 789"
})
"⚠️ Large response detected. State saved."
"Continue with: [continuation prompt]"
💡 ADVANCED PATTERNS
Pattern: Progressive Loading
// For large datasets:
// Step 1: Count only
const count = get_count()
"Found ${count} total items"
// Step 2: First batch
const batch1 = get_items({ limit: 10, offset: 0 })
"Loaded first 10: ${batch1.map(i => i.id)}"
// Step 3: Ask before loading more
"Load next batch? (${count - 10} remaining)"
Pattern: Filtered Streaming
// For search operations:
// Apply filters before MCP call:
const filtered_query = {
search: 'keyword',
status: 'publish',
date_after: '2025-01-01',
per_page: 5 // Start tiny
}
const results = wordpress.search(filtered_query)
// Show count, not content:
"${results.length} matches found"
Pattern: Sampling
// For analysis:
// Get first 3 items only:
const sample = get_items({ limit: 3 })
// Analyze pattern:
"Sample structure: ${Object.keys(sample[0])}"
"Apply this pattern to remaining ${total - 3} items?"
📈 SUCCESS METRICS
This skill is working correctly when:
- ✅ No MCP call exceeds 2,000 tokens in response
- ✅ WordPress queries use field filters 100% of time
- ✅ Playwright saves screenshots, never displays
- ✅ Raw MCP responses NEVER appear in conversation
- ✅ ID-based workflows used consistently
- ✅ Large datasets saved to files automatically
🎯 INTEGRATION WITH OTHER SKILLS
Works synergistically with:
- reference-file-system: Save MCP responses, reference by path
- concise-execution-mode: Process MCP calls silently
- data-processing-templates: Save MCP data to files
- conversation-state-persistence: Save state before large MCP operations
⚠️ CRITICAL REMINDERS
- WordPress default is dangerous - Always set field filters
- Playwright screenshots kill conversations - Always save to file
- Notion search is greedy - Always set page_size
- "List all" is forbidden - Always limit results
- Display nothing - Save everything to files
🏆 THE GOLDEN RULES
- Request minimal fields - Only what you need
- Limit results aggressively - 10 items max initially
- Save, don't display - Files, not conversation
- IDs, not content - Work with identifiers
- Paginate on demand - Never bulk load
- Estimate before call - Budget tokens first
Deployment Path: /mnt/skills/user/mcp-response-optimization/SKILL.md
Restart Required: Yes (restart Claude Desktop after deployment)
Token Impact: Prevents 50,000-100,000+ token MCP response disasters
Conversation Survival Rate: From 1 message → full conversation capacity