| name | mcp-response-optimization |
| description | Prevent massive MCP responses from WordPress, Playwright, Notion, and other MCPs that can kill conversations on first call. Sophisticated token management for all MCP tool usage. |
MCP Response Optimization
Core Principle
REQUEST MINIMAL, DISPLAY NOTHING, SAVE EVERYTHING
Single MCP calls can kill entire conversations. This skill ensures every MCP call is filtered, limited, silent, extracted, and saved for maximum token efficiency.
When to Activate
ALWAYS activate before ANY MCP tool call, especially:
- WordPress MCP operations (list posts, get pages, media queries)
- Playwright browser automation (DOM queries, screenshots)
- Notion API queries (search, database queries)
- Any search or list operations across MCPs
- Large data retrievals from any source
This skill is MANDATORY for all MCP usage in Claude Desktop.
The Critical Problem
One MCP call = conversation dead:
- 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 ends on first message
MCP Integration
This skill optimizes these MCPs:
- wordpress-mcp: WordPress content management
- playwright: Browser automation and testing
- notion / notionApi: Notion workspace integration
- filesystem: File system operations (for saving large responses)
- gmail: Email management
- supabase: Database queries
- firecrawl-mcp: Web scraping
WordPress MCP - The Worst Offender
The Problem
WordPress returns EVERYTHING by default:
- Full post content (50KB per post)
- All metadata, revisions, images (base64)
- Author details, comments, categories
- One query = conversation killer
The Solution: Aggressive Filtering
Rule 1: Always Use Field Filters
wordpress.list_posts({ per_page: 100 })
wordpress.list_posts({
per_page: 10,
fields: 'id,title,date,status',
context: 'view'
})
Rule 2: Limit Results Aggressively
per_page: 10
page: 1
Rule 3: Request Specific Fields Only
For Posts:
fields: 'id,title,status,date'
fields: 'id,title,status,date,excerpt'
fields: 'id,title,content'
For Pages:
fields: 'id,title,status,parent'
For Media:
fields: 'id,title,source_url'
Rule 4: Use Targeted Queries
wordpress.list_posts({ search: 'conference' })
wordpress.list_posts({
search: 'conference',
per_page: 5,
fields: 'id,title,date',
status: 'publish',
orderby: 'date',
order: 'desc'
})
Rule 5: Never Display Full Responses
console.log(response)
const ids = response.map(post => ({ id: post.id, title: post.title }))
console.log(`Found ${ids.length} posts`)
WordPress Response Patterns
Pattern: List Posts
const posts = wordpress.list_posts({
per_page: 10,
fields: 'id,title,status,date',
status: 'publish'
})
const post_ids = posts.map(p => p.id)
`Found ${posts.length} posts: ${post_ids.join(', ')}`
Pattern: Get Single Post
const post = wordpress.get_post({
id: 123,
fields: 'id,title,content,status'
})
`Retrieved post: ${post.title} (${post.id})`
Pattern: Search and Filter
const results = wordpress.list_posts({
search: 'keyword',
per_page: 5,
fields: 'id,title,date'
})
const filtered = results.filter()
`Found ${filtered.length} matches: [${filtered.map(p => p.id)}]`
Playwright MCP - DOM and Screenshot Killer
The Problem
- Full page HTML (200KB)
- Complete DOM tree with attributes
- Base64 screenshots (500KB-2MB)
- One screenshot = conversation over
The Solution: Selective Queries
Rule 1: Use CSS Selectors, Not Full DOM
playwright.evaluate("document.documentElement.outerHTML")
playwright.locator('.product-title').textContent()
Rule 2: Target Specific Elements
const data = {
title: await page.locator('h1').textContent(),
price: await page.locator('.price').textContent(),
available: await page.locator('.stock-status').textContent()
}
Rule 3: Screenshots - Save, Never Display
const screenshot = await page.screenshot()
await page.screenshot({ path: '/outputs/screenshot.png' })
`Screenshot saved: /outputs/screenshot.png`
Rule 4: Limit Element Extraction
const products = await page.locator('.product-card').all()
const product_data = []
for (const product of products.slice(0, 10)) {
product_data.push({
name: await product.locator('.name').textContent(),
price: await product.locator('.price').textContent()
})
}
fs.writeFileSync('/outputs/products.json', JSON.stringify(product_data))
`Extracted ${product_data.length} products → /outputs/products.json`
Playwright Response Patterns
Pattern: Page Scraping
await page.goto(url)
const data = {
title: await page.title(),
h1: await page.locator('h1').first().textContent(),
meta: await page.locator('meta[name="description"]').getAttribute('content')
}
fs.writeFileSync('/outputs/page_data.json', JSON.stringify(data))
`Page data saved: /outputs/page_data.json`
Pattern: Element Analysis
const count = await page.locator('.product-item').count()
`Found ${count} products`
const sample = await page.locator('.product-item').first().textContent()
`Sample product: ${sample}`
Pattern: Screenshots for User
await page.screenshot({
path: '/mnt/user-data/outputs/page.png',
fullPage: false
})
`[View screenshot](computer:///mnt/user-data/outputs/page.png)`
Notion MCP - Large Page Objects
The Problem
Notion returns full page objects with all blocks - can be massive.
The Solution
notion.search({ query: 'project' })
notion.search({
query: 'project',
page_size: 10,
filter: { property: 'Status', select: { equals: 'Active' } }
})
const page_ids = results.map(r => r.id)
`Found ${page_ids.length} pages: ${page_ids}`
Universal MCP Rules
Rule 1: Pre-Call Token Budget
Before EVERY MCP call:
- 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
get_all_items({ limit: 1000 })
get_items({ limit: 10, page: 1 })
Rule 3: Save Large Responses
console.log(large_response)
fs.writeFileSync('/outputs/response.json', JSON.stringify(large_response))
`Response saved: /outputs/response.json`
Rule 4: ID-Based Workflows
const ids = get_minimal_list()
`Found ${ids.length} items: ${ids}`
const full_item = get_item_by_id(selected_id)
Rule 5: Never Display Raw MCP Responses
`Here's what I found: ${JSON.stringify(mcp_response)}`
const summary = {
count: mcp_response.length,
items: mcp_response.map(i => i.id)
}
`Found ${summary.count} items: ${summary.items.join(', ')}`
Token Estimation Guide
| Operation | Bad Approach | Good Approach |
|---|
| List 100 WP posts | 1,000,000 tokens | 2,500 tokens |
| Get WP page content | 50,000 tokens | 200 tokens |
| Playwright full DOM | 100,000 tokens | 500 tokens |
| Playwright screenshot | 125,000 tokens | 50 tokens |
| Notion page search | 80,000 tokens | 1,000 tokens |
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
fs.writeFileSync('/outputs/large_response.json', JSON.stringify(response))
const summary = {
count: response.length,
first_id: response[0]?.id,
last_id: response[response.length - 1]?.id
}
`Response saved to /outputs/large_response.json`
`Summary: ${summary.count} items (${summary.first_id} to ${summary.last_id})`
Success Checklist
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
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
Integration with Other Skills
Works with:
- curv-design-system - On-brand outputs with token efficiency
- dashboard-auto-generation - Data dashboards without token bloat
Deployment: Import .zip to Claude Desktop → Settings → Skills
Impact: Prevents 50,000-100,000+ token MCP disasters
Result: Full conversation capacity instead of 1-message death