Stop Drowning Your Cursor IDE in MCP & SKILL Noise — Everything You Need to Know A new analysis warns that AI agent harnesses waste tokens and degrade model performance by dumping every MCP tool schema into the LLM context on every request, citing Google Calendar's events.list which exposes over 20 properties when only 5 fields are needed for a simple query like 'When is my first meeting tomorrow?'. The author argues that manually toggling tools or typing commands to skip irrelevant tools is an ineffective fight against context bloat and rising token costs. Are you manually toggling MCP tools on and off? Typing “use the XYZ tool” to skip irrelevant tools? You’re fighting a losing battle against context bloat — and paying for every wasted token. Agent harnesses dump every MCP tool into the LLM context on every request. Even frontier models suffer from information overload, hunting for needles in haystacks while your token bill grows. Take Google Calendar’s events.list: it exposes 20+ properties. For a simple prompt like "When is my first meeting tomorrow?" , only 5 fields matter. The rest are schema noise that confuse the model and burn tokens and bloat the context window. { "name": "GOOGLECALENDAR EVENTS LIST", "description": "Returns events on the specified calendar.", "input schema": { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "q": { "type": "string", "description": "Free text search terms to find events that match these terms in various fields. Optional." }, "iCalUID": { "type": "string", "description": "Specifies an event ID in the iCalendar format to be provided in the response. Optional. Use this if you want to search for an event by its iCalendar ID." }, "orderBy": { "type": "string", "description": "The order of the events returned in the result. Optional. The default is an unspecified, stable order. Acceptable values are: \"startTime\", \"updated\". When set to \"startTime\", singleEvents must be true. The action automatically sets singleEvents=true when orderBy='startTime'." }, "timeMax": { "type": "string", "description": "Upper bound exclusive for an event's start time to filter by. Optional. If unset, no start-time upper bound is applied. Must be an RFC3339 timestamp with mandatory time zone offset e.g., 2011-06-03T10:00:00-07:00 or 2011-06-03T10:00:00Z . Milliseconds may be provided but are ignored. If timeMin is set, timeMax must be greater than timeMin." }, "timeMin": { "type": "string", "description": "Lower bound exclusive for an event's end time to filter by. Optional. If unset, no end-time lower bound is applied. Must be an RFC3339 timestamp with mandatory time zone offset e.g., 2011-06-03T10:00:00-07:00 or 2011-06-03T10:00:00Z . Milliseconds may be provided but are ignored. If timeMax is set, timeMin must be smaller than timeMax." }, "timeZone": { "type": "string", "description": "Time zone used in the response. Optional. Use an IANA time zone identifier e.g., America/Los Angeles . Defaults to the user's primary time zone. Offsets e.g., '-03:00', 'UTC+0' and abbreviations e.g., 'IST', 'PST' are invalid." }, "pageToken": { "type": "string", "description": "Token specifying which result page to return. Optional." }, "syncToken": { "type": "string", "description": "Token from nextSyncToken to return only entries changed since the last list. Cannot be combined with iCalUID, orderBy, privateExtendedProperty, q, sharedExtendedProperty, timeMin, timeMax, or updatedMin. Deletions since the previous list are always included; showDeleted cannot be false in this mode. The action automatically removes conflicting parameters when syncToken is provided." }, "calendarId": { "type": "string", "description": "Calendar identifier. To retrieve calendar IDs call the calendarList.list method. If you want to access the primary calendar of the currently logged in user, use the \"primary\" keyword.\nExample: \"primary\"" }, "eventTypes": { "type": "string", "description": "Event types to return. Optional. This parameter can be repeated multiple times to return events of different types. If unset, returns all event types. Acceptable values are: \"birthday\", \"default\", \"focusTime\", \"fromGmail\", \"outOfOffice\", \"workingLocation\"." }, "maxResults": { "type": "integer", "description": "Maximum number of events returned on one result page. The number of events in the resulting page may be less than this value, or none at all, even if there are more events matching the query. Incomplete pages can be detected by a non-empty nextPageToken field in the response. By default the value is 250 events. The page size can never be larger than 2500 events. Optional." }, "updatedMin": { "type": "string", "description": "Lower bound for an event's last modification time RFC3339 . When specified, entries deleted since this time are always included regardless of showDeleted. Optional." }, "showDeleted": { "type": "boolean", "description": "Include cancelled events status=\"cancelled\" . Optional; default is false. This surfaces cancelled soft-deleted events, not items in the Trash. When syncToken or updatedMin is used, deletions since those markers are included regardless of showDeleted. Recurring interaction: if singleEvents=false and showDeleted=false, cancelled instances of a recurring series may still be included; if showDeleted=true and singleEvents=true, only single deleted instances not parent series are returned." }, "maxAttendees": { "type": "integer", "minimum": 1, "description": "The maximum number of attendees to include in the response. If there are more than the specified number of attendees, only the participant is returned. Optional. Must be = 1 if provided." }, "singleEvents": { "type": "boolean", "description": "Whether to expand recurring events into instances and only return single one-off events and instances of recurring events. Optional. The default is False." }, "alwaysIncludeEmail": { "type": "boolean", "description": "Deprecated and ignored." }, "showHiddenInvitations": { "type": "boolean", "description": "Whether to include hidden invitations in the result. Optional. The default is False. Hidden invitations are events where your attendee entry has responseStatus='needsAction' and attendees .self==true. When true, such invitations are included." }, "sharedExtendedProperty": { "type": "string", "description": "Extended properties constraint specified as propertyName=value. Matches only shared properties. This parameter might be repeated multiple times to return events that match all given constraints." }, "privateExtendedProperty": { "type": "string", "description": "Extended properties constraint specified as propertyName=value. Matches only private properties. This parameter might be repeated multiple times to return events that match all given constraints." } }, "required": "calendarId" , "additionalProperties": false }} See more tool examples here https://github.com/qdrddr/chunk-your-tools/tree/main/examples . Skills have the opposite problem — not enough signal. The agent sees only a short frontmatter description. Write it too short, and the LLM misses it if semantically distant. Write it too long, and you drown the context. Either way, you end up with the same avoidable pattern: manually adding and removing .md files mid-project or asking “ read xyz skill ”. See more skill examples here https://github.com/qdrddr/chunk-your-skills/tree/main/examples . CYT clear-your-tools automatically dynamically injects only the relevant context your agent needs, when it needs it. It works by chunking tools and skills into granular pieces, running them through a pruning pipeline, and recomposing a “skinny” version: CYT uses three gating strategies — evaluating relevance with BM25 lexical search, reranking, or a weak LLM — all much cheaper than the primary coding model, to decide what survives without breaking JSON schema or markdown structure. Example: A GitHub create issue tool has properties title required , body, and labels. If your prompt doesn't need labeling, CYT drops labels entirely. The tool still validates; the primary coding LLM just sees less noise. Install Cargo https://doc.rust-lang.org/cargo/getting-started/installation.html , then cargo install chunk-your-tools Decompose your tools into Chunkschunk-your-tools decompose \ --input examples/input/tools.json \ --output examples/catalog Restore the tools back keeping only some tools, properties and enumschunk-your-tools recompose \ --catalog-dir examples/catalog \ --survivors examples/input/survivors-named.json \ --output examples/output/named.json See more tool examples here https://github.com/qdrddr/chunk-your-tools/tree/main/examples . Claude Code and Codex allow HTTP request interception via a reverse proxy. Cursor does not unless you BYOK . So CYT uses hooks instead. Here’s the catch with hooks: They do not allow manipulating tools in the agent anyway; so we have to move tools out of Cursor and into an MCP aggregator. MCPC is that aggregator. It hosts your servers; CYT dynamically exposes only the relevant subset back to Cursor via project rules. Here’s another problem we address in CYT: Cursor’s beforeSubmitProm hook currently cannot add additional context. Until it supports additional content, we work around it by moving all content into the .cursor/rule/cyt-indexer.mdc file that Cursor always reads. 1. Install CYT and MCPCuv tool install 'clear-your-tools all 'npm install -g @apify/mcpc 2. Add the hookscyt hook cursor 3. Backup your Cursor MCP config, then clear itcp ~/.cursor/mcp.json ~/.mcpc/cursor.jsonjq '. + {mcpServers: {}}' ~/.mcpc/cursor.json ~/.cursor/mcp.json 4. Re-connect servers via MCPC insteadmcpc connect ~/.mcpc/cursor.json:servername @servername Or if all are STDIO:mcpc connect ~/.mcpc/cursor.json --stdio CYT will now create and update .cursor/rules/cyt-indexer.mdc automatically as you submit prompts, injecting only the tool schemas and skill chunks relevant to your current intent. Stop copying SKILL.md files across projects. Host skills remotely and let CYT pull and prune them on every request: mcpc connect https://mcp.skillsovermcp.com/mcp/upstash/context7 @context7-skillcyt hook daemon restartcyt hook cursor Want granular pruning in your own harness or an app? The underlying chunkers are open-source Apache 2.0 with bindings for Python, TypeScript, C, Go, and natively in Rust: cargo install chunk-your-toolscargo install chunk-your-skills Test with CLI before embedding into your appchunk-your-skills decompose --helpchunk-your-skills recompose --helpchunk-your-tools decompose --helpchunk-your-tools recompose --help Give the repos a star if you find them useful. Q: Why not just use progressive tool discovery? A: Discovery adds expensive reasoning steps, and the LLM still can’t select a tool it has never seen. CYT closes that gap by direct injection. Q: Why MCPC specifically? A: It is reliable under heavy load where other aggregators flake or flap. Q: What if Cursor adds additional content support to hooks? A: CYT already implements it. The moment Cursor enables it, injection will work natively without the MCPC workaround. In your config.yaml, you just need to set skills.hook.cursor rule file.enabled: false Q: Will I benefit from prompt prefix caching when using CYT? A: Yes, the prefixes stay stable for both proxy & hook injection paths. Q: Can I use other prompt optimizers and token savers? A: Absolutely. We recommend using CYT hooks with other optimizers such as RTK, Headroom, and others; typically it should work with any other optimizer. When you use proxy injection, then other proxies like Headroom may still be used but could be cumbersome for non-advanced users. Ready to cut the noise? Install UV https://docs.astral.sh/uv/getting-started/installation and then uv tool install 'clear-your-tools all 'cyt hook cursor Stop Drowning Your Cursor IDE in MCP & SKILL Noise — Everything You Need to Know 🧠 https://pub.towardsai.net/stop-drowning-your-cursor-ide-in-mcp-skill-noise-everything-you-need-to-know-ec8d56b64eed was originally published in Towards AI https://pub.towardsai.net on Medium, where people are continuing the conversation by highlighting and responding to this story.