Strip unnecessary fields in wpcom_request response#3005
Conversation
ff2e207 to
a3c7f6a
Compare
The WP.com /sites/{id} endpoint returns a plan object whose features
sub-field alone is 60K+ characters, pushing the total response past
Claude Code's ~100K character MCP tool result limit. The agent only
needs product_slug, is_free, and expired to gate features, since the
system prompt hardcodes what each plan tier can and can't do. Strip
plan.features and keep only essential plan properties.
d6c4b3e to
5fccba7
Compare
📊 Performance Test ResultsComparing b7eca66 vs trunk app-size
site-editor
site-startup
Results are median values from multiple test runs. Legend: 🟢 Improvement (faster) | 🔴 Regression (slower) | ⚪ No change (<50ms diff) |
List endpoints like GET /templates can return 80K+ chars of block markup. Strip content and _links from array items when the response exceeds 80K chars — the agent can still identify items by slug/title and fetch individual ones for full content.
apps/cli/ai/wpcom-tools.ts
Outdated
| } | ||
|
|
||
| // Case 1: plan.features can be 60K+ chars — keep only essential plan properties | ||
| if ( ! Array.isArray( result ) && result.plan?.features ) { |
There was a problem hiding this comment.
For me this seems too specific to a given endpoint, Is there a way to apply a generic fix instead (like overriding the token limit or telling the agent to use the minim required properties...). I don't believe we should have specifics like that because it will be hard to scale and adapt as the endpoints evolve...
There was a problem hiding this comment.
Good point about scalability. I looked into a few alternatives:
Prompt-based: Tell the agent to always use _fields (wp/v2) or fields (v1.1) to request only what it needs. Works for most endpoints but doesn't fix the plan case. Even with fields=ID,name,description,URL,plan, the plan field alone is 63K+ because features is deeply nested and the API doesn't support sub-field filtering like fields=plan.product_slug. Plus the agent might not always follow the instruction.
Increasing the token limit: Would work but the LLM ends up processing 60K+ chars of plan.features that it never reads. The system prompt already hardcodes what each plan tier can do, so the agent only looks at product_slug and is_free. Same story with array listings where content and _links get ignored when the agent is just browsing. Feels wasteful to bump the token budget for data that gets thrown away.
On the coupling concern: I don't think these endpoint shapes will change that much. plan.features has had this structure for a long time, and content/_links are standard WP REST API fields. But I get the concern about hardcoding endpoint-specific stuff.
Proposal: Add prompt guidance so the agent uses _fields/fields by default, and keep compactResponse() as a safety net for what field filtering can't solve (mainly plan.features).
That said, if you'd rather avoid the coupling I'm also fine with just increasing the token limit and updating the prompt to use fields. What do you think?
There was a problem hiding this comment.
If it's just one specific case, I'm fine with it if we add a good comment to explain that it's a special case and not a pattern to follow, with the fields guidance for the generic case.
Do you think the fields guidance will solve all the other cases?
Curious about @lezama's thoughts as well as he was working on alternative solutions.
There was a problem hiding this comment.
I have applied the suggestions as part of c457f53 and tested different scenarios, and I have not found the error. The suggested changes seem to do the trick.
@lezama, the approach of trimming the output suggested here is an alternative option that can be applied on top of this changes so we ensure the error is not displayed to the user. In some testing, I have found that the limit is not exact so we can add some extra margin to prevent the error.
Add system prompt hint instructing the agent to use _fields on wp/v2 listing endpoints to minimize response sizes. Scope compactResponse() to only handle plan.features stripping with a clear comment that this is a special case (the API doesn't support sub-field filtering). Remove the generic array compaction in favor of prompt-driven field selection.
sejas
left a comment
There was a problem hiding this comment.
I think it makes sense to remove plan features if they are 60K!. I left a couple of suggestions.
apps/cli/ai/wpcom-tools.ts
Outdated
| * the system prompt instructs the agent to use `_fields` (wp/v2) or `fields` (v1.1) | ||
| * query params to request only the properties it needs. | ||
| */ | ||
| function compactResponse( result: ApiResponse ): ApiResponse { |
There was a problem hiding this comment.
I wonder if we should use a more specific function name, like stripPlanFeatures.
There was a problem hiding this comment.
I agree, I have renamed it as part of e21a84e
| * system prompt hardcodes what each plan tier can do. | ||
| * | ||
| * This is NOT a pattern to follow for other endpoints. For general large responses, | ||
| * the system prompt instructs the agent to use `_fields` (wp/v2) or `fields` (v1.1) |
There was a problem hiding this comment.
Is this accurate? I didn’t see the system prompt mention fields for v1.1. Should we add that information to the system prompt?
There was a problem hiding this comment.
Great catch! I have updated the system prompt as part of e21a84e as I identified that sometimes fields were failing. I have added a suggestion to use both _fields for wp/v2 or fields for v1.1 to reduce the response size. I have tested multiple scenarios and they all work fine. I have tested:
- What's the current name, tagline, and active theme of this site?
- Redesign my site to be more polished
- Can you update the
coming-soontemplate to have a light background instead of the current one?
And I have not obtained the error
Update system prompt to instruct the agent to use fields (v1.1) and _fields (wp/v2) to minimize response sizes, always including ID for v1.1 requests. Rename compactResponse to stripOversizedFields for clarity.
apps/cli/ai/wpcom-tools.ts
Outdated
|
|
||
| return textResult( JSON.stringify( result, null, 2 ) ); | ||
| const compacted = stripOversizedFields( result ); | ||
| return textResult( JSON.stringify( compacted, null, 2 ) ); |
There was a problem hiding this comment.
I think it’s better to use JSON.stringify(result) instead of JSON.stringify(compacted, null, 2), since the added indentation increases token usage. I learned this in #3011. I think we can close that PR in favor of this one?
* Strip bloated plan.features from wpcom_request responses
The WP.com /sites/{id} endpoint returns a plan object whose features
sub-field alone is 60K+ characters, pushing the total response past
Claude Code's ~100K character MCP tool result limit. The agent only
needs product_slug, is_free, and expired to gate features, since the
system prompt hardcodes what each plan tier can and can't do. Strip
plan.features and keep only essential plan properties.
* Also compact large array responses by stripping content and _links
List endpoints like GET /templates can return 80K+ chars of block markup.
Strip content and _links from array items when the response exceeds 80K
chars — the agent can still identify items by slug/title and fetch
individual ones for full content.
* Add _fields prompt guidance and scope plan compaction as special case
Add system prompt hint instructing the agent to use _fields on wp/v2
listing endpoints to minimize response sizes. Scope compactResponse()
to only handle plan.features stripping with a clear comment that this
is a special case (the API doesn't support sub-field filtering). Remove
the generic array compaction in favor of prompt-driven field selection.
* Add fields guidance for v1.1, rename stripOversizedFields
Update system prompt to instruct the agent to use fields (v1.1) and
_fields (wp/v2) to minimize response sizes, always including ID for
v1.1 requests. Rename compactResponse to stripOversizedFields for
clarity.
* Remove indentation in JSON to save tokens
Related issues
How AI was used in this PR
AI assisted with debugging the root cause (identifying that
plan.featureswas 63K+ chars) and writing the compact extraction logic.Proposed Changes
_fieldson wp/v2 listing endpoints (templates, posts, pages, etc.) to request only lightweight fields and exclude heavy ones likecontent. This is the generic solution for large responses.plan.features: Add acompactResponse()function that stripsplan.features(60K+ chars) from/sites/{id}responses, keeping onlyproduct_id,product_slug,product_name_short,expired, andis_free. This can't be solved via query params because the v1.1 API doesn't support sub-field filtering (e.g.fields=plan.product_slug). The function includes a clear comment explaining this is a special case, not a pattern to follow.Testing Instructions
npm run cli:build. Then start studio ai by runningnode apps/cli/dist/cli/main.mjs aiwpcom_request(e.g., "What's the current name, tagline, and active theme of this site?")Error: result (96,907 characters) exceeds maximum allowed tokens. Output has been saved to…Pre-merge Checklist