MCPWrapped
2025-12-05
Background
I was curious about why LLMs seem to never be able to use the MCP tools I gave it. I read LLMTest_NeedleInAHaystack which tested LLM context windows with the following steps:
- Place a random fact or statement (the 'needle') in the middle of a long context window (the 'haystack')
- Ask the model to retrieve this statement
- Iterate over various document depths (where the needle is placed) and context lengths to measure performance
Models lose ability to retrieve the needle as the context windows grow and as the needle is further up the input

Because of this, MCP tools need to be kept fresh at the end of the context window to keep agent aware of the tools.
With large MCP servers, with dozens of tools each with its own description, the context window gets clogged by information of unused tools which competes with the freshness in the model's memory. These unused tools should not have the same relevance to the model as your prompt or other tools you do want.
I found this to be extremely annoying so I created this simple wrapper which that goes wherever your mcp servers go and can filter out only the tools you actually want to use.
How does it work
MCPWrapped is a proxy that sits between the client (your AI tool) and your MCP server of choice. Whenever the client tries to fetch the tools-list, MCPWrapped intercepts it and returns a filtered list.
Here is an example with the playwright-mcp-server, I am using mcp-inspector to get the list of tools and Claude Code's /context to see the token count:
npx @modelcontextprotocol/inspector npx -y @executeautomation/playwright-mcp-server
The playwright-mcp-server has 33 total tools when realistically just want to give my agent access to viewing the html, navigating, and viewing some logs.
All Tools
{
"tools": [
{
"name": "start_codegen_session",
"description": "Start a new code generation session to record Playwright actions",
"inputSchema": {
"type": "object",
"properties": {
"options": {
"type": "object",
"description": "Code generation options",
"properties": {
"outputPath": {
"type": "string",
"description": "Directory path where generated tests will be saved (use absolute path)"
},
"testNamePrefix": {
"type": "string",
"description": "Prefix to use for generated test names (default: 'GeneratedTest')"
},
"includeComments": {
"type": "boolean",
"description": "Whether to include descriptive comments in generated tests"
}
},
"required": [
"outputPath"
]
}
},
"required": [
"options"
]
}
},
{
"name": "end_codegen_session",
"description": "End a code generation session and generate the test file",
"inputSchema": {
"type": "object",
"properties": {
"sessionId": {
"type": "string",
"description": "ID of the session to end"
}
},
"required": [
"sessionId"
]
}
},
{
"name": "get_codegen_session",
"description": "Get information about a code generation session",
"inputSchema": {
"type": "object",
"properties": {
"sessionId": {
"type": "string",
"description": "ID of the session to retrieve"
}
},
"required": [
"sessionId"
]
}
},
{
"name": "clear_codegen_session",
"description": "Clear a code generation session without generating a test",
"inputSchema": {
"type": "object",
"properties": {
"sessionId": {
"type": "string",
"description": "ID of the session to clear"
}
},
"required": [
"sessionId"
]
}
},
{
"name": "playwright_navigate",
"description": "Navigate to a URL",
"inputSchema": {
"type": "object",
"properties": {
"url": {
"type": "string",
"description": "URL to navigate to the website specified"
},
"browserType": {
"type": "string",
"description": "Browser type to use (chromium, firefox, webkit). Defaults to chromium",
"enum": [
"chromium",
"firefox",
"webkit"
]
},
"width": {
"type": "number",
"description": "Viewport width in pixels (default: 1280)"
},
"height": {
"type": "number",
"description": "Viewport height in pixels (default: 720)"
},
"timeout": {
"type": "number",
"description": "Navigation timeout in milliseconds"
},
"waitUntil": {
"type": "string",
"description": "Navigation wait condition"
},
"headless": {
"type": "boolean",
"description": "Run browser in headless mode (default: false)"
}
},
"required": [
"url"
]
}
},
{
"name": "playwright_screenshot",
"description": "Take a screenshot of the current page or a specific element",
"inputSchema": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Name for the screenshot"
},
"selector": {
"type": "string",
"description": "CSS selector for element to screenshot"
},
"width": {
"type": "number",
"description": "Width in pixels (default: 800)"
},
"height": {
"type": "number",
"description": "Height in pixels (default: 600)"
},
"storeBase64": {
"type": "boolean",
"description": "Store screenshot in base64 format (default: true)"
},
"fullPage": {
"type": "boolean",
"description": "Store screenshot of the entire page (default: false)"
},
"savePng": {
"type": "boolean",
"description": "Save screenshot as PNG file (default: false)"
},
"downloadsDir": {
"type": "string",
"description": "Custom downloads directory path (default: user's Downloads folder)"
}
},
"required": [
"name"
]
}
},
{
"name": "playwright_click",
"description": "Click an element on the page",
"inputSchema": {
"type": "object",
"properties": {
"selector": {
"type": "string",
"description": "CSS selector for the element to click"
}
},
"required": [
"selector"
]
}
},
{
"name": "playwright_iframe_click",
"description": "Click an element in an iframe on the page",
"inputSchema": {
"type": "object",
"properties": {
"iframeSelector": {
"type": "string",
"description": "CSS selector for the iframe containing the element to click"
},
"selector": {
"type": "string",
"description": "CSS selector for the element to click"
}
},
"required": [
"iframeSelector",
"selector"
]
}
},
{
"name": "playwright_iframe_fill",
"description": "Fill an element in an iframe on the page",
"inputSchema": {
"type": "object",
"properties": {
"iframeSelector": {
"type": "string",
"description": "CSS selector for the iframe containing the element to fill"
},
"selector": {
"type": "string",
"description": "CSS selector for the element to fill"
},
"value": {
"type": "string",
"description": "Value to fill"
}
},
"required": [
"iframeSelector",
"selector",
"value"
]
}
},
{
"name": "playwright_fill",
"description": "fill out an input field",
"inputSchema": {
"type": "object",
"properties": {
"selector": {
"type": "string",
"description": "CSS selector for input field"
},
"value": {
"type": "string",
"description": "Value to fill"
}
},
"required": [
"selector",
"value"
]
}
},
{
"name": "playwright_select",
"description": "Select an element on the page with Select tag",
"inputSchema": {
"type": "object",
"properties": {
"selector": {
"type": "string",
"description": "CSS selector for element to select"
},
"value": {
"type": "string",
"description": "Value to select"
}
},
"required": [
"selector",
"value"
]
}
},
{
"name": "playwright_hover",
"description": "Hover an element on the page",
"inputSchema": {
"type": "object",
"properties": {
"selector": {
"type": "string",
"description": "CSS selector for element to hover"
}
},
"required": [
"selector"
]
}
},
{
"name": "playwright_upload_file",
"description": "Upload a file to an input[type='file'] element on the page",
"inputSchema": {
"type": "object",
"properties": {
"selector": {
"type": "string",
"description": "CSS selector for the file input element"
},
"filePath": {
"type": "string",
"description": "Absolute path to the file to upload"
}
},
"required": [
"selector",
"filePath"
]
}
},
{
"name": "playwright_evaluate",
"description": "Execute JavaScript in the browser console",
"inputSchema": {
"type": "object",
"properties": {
"script": {
"type": "string",
"description": "JavaScript code to execute"
}
},
"required": [
"script"
]
}
},
{
"name": "playwright_console_logs",
"description": "Retrieve console logs from the browser with filtering options",
"inputSchema": {
"type": "object",
"properties": {
"type": {
"type": "string",
"description": "Type of logs to retrieve (all, error, warning, log, info, debug, exception)",
"enum": [
"all",
"error",
"warning",
"log",
"info",
"debug",
"exception"
]
},
"search": {
"type": "string",
"description": "Text to search for in logs (handles text with square brackets)"
},
"limit": {
"type": "number",
"description": "Maximum number of logs to return"
},
"clear": {
"type": "boolean",
"description": "Whether to clear logs after retrieval (default: false)"
}
},
"required": []
}
},
{
"name": "playwright_resize",
"description": "Resize the browser viewport using manual dimensions or device presets. Supports 143+ device presets including iPhone, iPad, Android devices, and desktop browsers with proper user-agent and touch emulation.",
"inputSchema": {
"type": "object",
"properties": {
"device": {
"type": "string",
"description": "Device preset name (e.g., 'iPhone 13', 'iPad Pro 11', 'Pixel 7', 'Galaxy S24', 'Desktop Chrome'). Automatically configures viewport, user-agent, and device capabilities. Use playwright.devices to see all available devices."
},
"width": {
"type": "number",
"description": "Viewport width in pixels (for manual resize without device preset)"
},
"height": {
"type": "number",
"description": "Viewport height in pixels (for manual resize without device preset)"
},
"orientation": {
"type": "string",
"description": "Device orientation: 'portrait' or 'landscape' (only applies when using device preset)",
"enum": [
"portrait",
"landscape"
]
}
},
"required": []
}
},
{
"name": "playwright_close",
"description": "Close the browser and release all resources",
"inputSchema": {
"type": "object",
"properties": {},
"required": []
}
},
{
"name": "playwright_get",
"description": "Perform an HTTP GET request",
"inputSchema": {
"type": "object",
"properties": {
"url": {
"type": "string",
"description": "URL to perform GET operation"
},
"token": {
"type": "string",
"description": "Bearer token for authorization"
},
"headers": {
"type": "object",
"description": "Additional headers to include in the request",
"additionalProperties": {
"type": "string"
}
}
},
"required": [
"url"
]
}
},
{
"name": "playwright_post",
"description": "Perform an HTTP POST request",
"inputSchema": {
"type": "object",
"properties": {
"url": {
"type": "string",
"description": "URL to perform POST operation"
},
"value": {
"type": "string",
"description": "Data to post in the body"
},
"token": {
"type": "string",
"description": "Bearer token for authorization"
},
"headers": {
"type": "object",
"description": "Additional headers to include in the request",
"additionalProperties": {
"type": "string"
}
}
},
"required": [
"url",
"value"
]
}
},
{
"name": "playwright_put",
"description": "Perform an HTTP PUT request",
"inputSchema": {
"type": "object",
"properties": {
"url": {
"type": "string",
"description": "URL to perform PUT operation"
},
"value": {
"type": "string",
"description": "Data to PUT in the body"
},
"token": {
"type": "string",
"description": "Bearer token for authorization"
},
"headers": {
"type": "object",
"description": "Additional headers to include in the request",
"additionalProperties": {
"type": "string"
}
}
},
"required": [
"url",
"value"
]
}
},
{
"name": "playwright_patch",
"description": "Perform an HTTP PATCH request",
"inputSchema": {
"type": "object",
"properties": {
"url": {
"type": "string",
"description": "URL to perform PATCH operation"
},
"value": {
"type": "string",
"description": "Data to PATCH in the body"
},
"token": {
"type": "string",
"description": "Bearer token for authorization"
},
"headers": {
"type": "object",
"description": "Additional headers to include in the request",
"additionalProperties": {
"type": "string"
}
}
},
"required": [
"url",
"value"
]
}
},
{
"name": "playwright_delete",
"description": "Perform an HTTP DELETE request",
"inputSchema": {
"type": "object",
"properties": {
"url": {
"type": "string",
"description": "URL to perform DELETE operation"
},
"token": {
"type": "string",
"description": "Bearer token for authorization"
},
"headers": {
"type": "object",
"description": "Additional headers to include in the request",
"additionalProperties": {
"type": "string"
}
}
},
"required": [
"url"
]
}
},
{
"name": "playwright_expect_response",
"description": "Ask Playwright to start waiting for a HTTP response. This tool initiates the wait operation but does not wait for its completion.",
"inputSchema": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Unique & arbitrary identifier to be used for retrieving this response later with `Playwright_assert_response`."
},
"url": {
"type": "string",
"description": "URL pattern to match in the response."
}
},
"required": [
"id",
"url"
]
}
},
{
"name": "playwright_assert_response",
"description": "Wait for and validate a previously initiated HTTP response wait operation.",
"inputSchema": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Identifier of the HTTP response initially expected using `Playwright_expect_response`."
},
"value": {
"type": "string",
"description": "Data to expect in the body of the HTTP response. If provided, the assertion will fail if this value is not found in the response body."
}
},
"required": [
"id"
]
}
},
{
"name": "playwright_custom_user_agent",
"description": "Set a custom User Agent for the browser",
"inputSchema": {
"type": "object",
"properties": {
"userAgent": {
"type": "string",
"description": "Custom User Agent for the Playwright browser instance"
}
},
"required": [
"userAgent"
]
}
},
{
"name": "playwright_get_visible_text",
"description": "Get the visible text content of the current page",
"inputSchema": {
"type": "object",
"properties": {},
"required": []
}
},
{
"name": "playwright_get_visible_html",
"description": "Get the HTML content of the current page. By default, all <script> tags are removed from the output unless removeScripts is explicitly set to false.",
"inputSchema": {
"type": "object",
"properties": {
"selector": {
"type": "string",
"description": "CSS selector to limit the HTML to a specific container"
},
"removeScripts": {
"type": "boolean",
"description": "Remove all script tags from the HTML (default: true)"
},
"removeComments": {
"type": "boolean",
"description": "Remove all HTML comments (default: false)"
},
"removeStyles": {
"type": "boolean",
"description": "Remove all style tags from the HTML (default: false)"
},
"removeMeta": {
"type": "boolean",
"description": "Remove all meta tags from the HTML (default: false)"
},
"cleanHtml": {
"type": "boolean",
"description": "Perform comprehensive HTML cleaning (default: false)"
},
"minify": {
"type": "boolean",
"description": "Minify the HTML output (default: false)"
},
"maxLength": {
"type": "number",
"description": "Maximum number of characters to return (default: 20000)"
}
},
"required": []
}
},
{
"name": "playwright_go_back",
"description": "Navigate back in browser history",
"inputSchema": {
"type": "object",
"properties": {},
"required": []
}
},
{
"name": "playwright_go_forward",
"description": "Navigate forward in browser history",
"inputSchema": {
"type": "object",
"properties": {},
"required": []
}
},
{
"name": "playwright_drag",
"description": "Drag an element to a target location",
"inputSchema": {
"type": "object",
"properties": {
"sourceSelector": {
"type": "string",
"description": "CSS selector for the element to drag"
},
"targetSelector": {
"type": "string",
"description": "CSS selector for the target location"
}
},
"required": [
"sourceSelector",
"targetSelector"
]
}
},
{
"name": "playwright_press_key",
"description": "Press a keyboard key",
"inputSchema": {
"type": "object",
"properties": {
"key": {
"type": "string",
"description": "Key to press (e.g. 'Enter', 'ArrowDown', 'a')"
},
"selector": {
"type": "string",
"description": "Optional CSS selector to focus before pressing key"
}
},
"required": [
"key"
]
}
},
{
"name": "playwright_save_as_pdf",
"description": "Save the current page as a PDF file",
"inputSchema": {
"type": "object",
"properties": {
"outputPath": {
"type": "string",
"description": "Directory path where PDF will be saved"
},
"filename": {
"type": "string",
"description": "Name of the PDF file (default: page.pdf)"
},
"format": {
"type": "string",
"description": "Page format (e.g. 'A4', 'Letter')"
},
"printBackground": {
"type": "boolean",
"description": "Whether to print background graphics"
},
"margin": {
"type": "object",
"description": "Page margins",
"properties": {
"top": {
"type": "string"
},
"right": {
"type": "string"
},
"bottom": {
"type": "string"
},
"left": {
"type": "string"
}
}
}
},
"required": [
"outputPath"
]
}
},
{
"name": "playwright_click_and_switch_tab",
"description": "Click a link and switch to the newly opened tab",
"inputSchema": {
"type": "object",
"properties": {
"selector": {
"type": "string",
"description": "CSS selector for the link to click"
}
},
"required": [
"selector"
]
}
}
]
}
This ended up being around 3.6k tokens. For common 128k token models, this would be around ~3% of your context window which is not too bad. For some internal mcp tools at my work, the main monolith mcp server took around ~20k tokens!
Using the mcpwrapped node package
npm install -g mcpwrapped
npx @modelcontextprotocol/inspector mcpwrapped npx -y @executeautomation/playwright-mcp-server --visible_tools=playwright_navigate,playwright_get_visible_html,playwright_console_logs,playwright_click
All Tools
{
"tools": [
{
"name": "playwright_navigate",
"description": "Navigate to a URL",
"inputSchema": {
"type": "object",
"properties": {
"url": {
"type": "string",
"description": "URL to navigate to the website specified"
},
"browserType": {
"type": "string",
"description": "Browser type to use (chromium, firefox, webkit). Defaults to chromium",
"enum": [
"chromium",
"firefox",
"webkit"
]
},
"width": {
"type": "number",
"description": "Viewport width in pixels (default: 1280)"
},
"height": {
"type": "number",
"description": "Viewport height in pixels (default: 720)"
},
"timeout": {
"type": "number",
"description": "Navigation timeout in milliseconds"
},
"waitUntil": {
"type": "string",
"description": "Navigation wait condition"
},
"headless": {
"type": "boolean",
"description": "Run browser in headless mode (default: false)"
}
},
"required": [
"url"
]
}
},
{
"name": "playwright_click",
"description": "Click an element on the page",
"inputSchema": {
"type": "object",
"properties": {
"selector": {
"type": "string",
"description": "CSS selector for the element to click"
}
},
"required": [
"selector"
]
}
},
{
"name": "playwright_console_logs",
"description": "Retrieve console logs from the browser with filtering options",
"inputSchema": {
"type": "object",
"properties": {
"type": {
"type": "string",
"description": "Type of logs to retrieve (all, error, warning, log, info, debug, exception)",
"enum": [
"all",
"error",
"warning",
"log",
"info",
"debug",
"exception"
]
},
"search": {
"type": "string",
"description": "Text to search for in logs (handles text with square brackets)"
},
"limit": {
"type": "number",
"description": "Maximum number of logs to return"
},
"clear": {
"type": "boolean",
"description": "Whether to clear logs after retrieval (default: false)"
}
},
"required": []
}
},
{
"name": "playwright_get_visible_html",
"description": "Get the HTML content of the current page. By default, all <script> tags are removed from the output unless removeScripts is explicitly set to false.",
"inputSchema": {
"type": "object",
"properties": {
"selector": {
"type": "string",
"description": "CSS selector to limit the HTML to a specific container"
},
"removeScripts": {
"type": "boolean",
"description": "Remove all script tags from the HTML (default: true)"
},
"removeComments": {
"type": "boolean",
"description": "Remove all HTML comments (default: false)"
},
"removeStyles": {
"type": "boolean",
"description": "Remove all style tags from the HTML (default: false)"
},
"removeMeta": {
"type": "boolean",
"description": "Remove all meta tags from the HTML (default: false)"
},
"cleanHtml": {
"type": "boolean",
"description": "Perform comprehensive HTML cleaning (default: false)"
},
"minify": {
"type": "boolean",
"description": "Minify the HTML output (default: false)"
},
"maxLength": {
"type": "number",
"description": "Maximum number of characters to return (default: 20000)"
}
},
"required": []
}
}
]
}
This reduced the usage to 848 tokens, ~24% of the size compared using the whole mcp server.
Usage
Here is an example of mcpwrapped in action using the standard FastMCP server configuration:
"mcpServers": {
"playwright-wrapped": {
"disabled": false,
"timeout": 10,
"type": "stdio",
"command": "mcpwrapped",
"args": [
"npx",
"-y",
"@executeautomation/playwright-mcp-server",
"--visible_tools=playwright_navigate,playwright_get_visible_html,playwright_console_logs,playwright_click"
]
}
}
But why
The main benefit of trimming down mcp server context is the ability to reuse existing mcp servers and just focus it down to your current tasks. For example, I narrowed down the playwright-mcp-server to focus on retrieving the website's html content and console logs for debugging websites without having to rewrite my own.
Currently this functionality is not widely available. Even popular coding tools like Claude has this open issue: https://github.com/anthropics/claude-code/issues/7328
Continuation
This is mostly the quick thoughts I had around the topic of MCP tool usage. Some things I want to explore:
- Pulling down
LLMTest_NeedleInAHaystackand modifying it test successful MCP tool usage.- Creating a simple tool for tokenizing MCP server context and seeing how the ranking of the most popular MCP servers compares with its context length.