Skip to content

feat(content): add get_content_summary tool for audit workflows#21

Open
kleintech wants to merge 1 commit into
InstaWP:mainfrom
kleintech:feature/get-content-summary
Open

feat(content): add get_content_summary tool for audit workflows#21
kleintech wants to merge 1 commit into
InstaWP:mainfrom
kleintech:feature/get-content-summary

Conversation

@kleintech
Copy link
Copy Markdown

Problem

The existing list_content, get_content, and find_content_by_url tools return full WordPress REST post objects. On recipe blog posts (with WP Recipe Maker active) these can exceed 50KB per post because of the rendered Recipe Maker card HTML embedded in content.rendered. For audit and lookup workflows the LLM only needs identity, status, and SEO-relevant metadata — paying tokens for the full payload on every read is wasteful.

Solution

A new get_content_summary tool that returns a fixed minimal shape:

  • id, title, slug, status, link
  • excerpt — HTML stripped to plain text
  • date_modified
  • categories, tags, featured_media
  • word_count — from yoast_head_json.schema.@graph[].wordCount when available, otherwise computed from rendered content with HTML stripped
  • yoast_focus_keyword, yoast_meta_title, yoast_meta_description

Look up by id (with optional content_type, defaulting to post) or by url (content type detected from the URL). The two are mutually exclusive — providing both, or neither, returns a clear error.

The tool internally bypasses the response trim added in #16 via the documented rawResponse: true escape hatch so it can read yoast_head_json. The trim still applies to all other tools.

When looking up by URL, the existing URL→post resolution logic from find_content_by_url is reused — extracted into a new exported helper findContentByUrl(url, siteId) in src/tools/unified-content.ts. The existing find_content_by_url handler is refactored to call the helper; behavior is preserved (same priority types, same fallback to all-types search, same error messages), and the previously duplicated update/return logic is collapsed.

Test coverage

No automated tests in this PR — there is no test framework set up in the repo yet (planned as a separate PR). The pure helpers (htmlToPlainText, countWords, extractYoastWordCount, buildContentSummary) were sanity-checked manually against:

  • a post with full Yoast data (wordCount sourced from yoast_head_json)
  • a post with no Yoast data (wordCount computed from content; Yoast fields null)
  • an empty/malformed post (all fields default to safe values)
  • HTML stripping with entities, nested tags, empty/undefined input
  • extractYoastWordCount with null input, missing graph, missing wordCount, and a wordCount present

The build (npm run build) compiles cleanly except for the two pre-existing TS errors in src/server.ts (TS2589) and src/tools/media.ts (TS2345), both unrelated to this change and present on main already (also called out in #16).

Sample request / response

Request — by URL:

{
  "url": "https://example.com/blog/easy-smoked-asparagus/"
}

Response (Yoast-active post):

{
  "id": 4274,
  "title": "Easy Smoked Asparagus & Hot Honey",
  "slug": "easy-smoked-asparagus",
  "status": "publish",
  "link": "https://example.com/blog/easy-smoked-asparagus/",
  "excerpt": "Smoky asparagus with hot honey.",
  "date_modified": "2026-04-30T10:14:00",
  "categories": [12, 7],
  "tags": [33],
  "featured_media": 9012,
  "word_count": 875,
  "yoast_focus_keyword": "smoked asparagus",
  "yoast_meta_title": "Easy Smoked Asparagus | Example",
  "yoast_meta_description": "Smoky charred asparagus finished with chili-lime hot honey."
}

Request — by ID with a custom post type:

{
  "id": 4274,
  "content_type": "wprm_recipe"
}

Notes on Yoast meta key exposure

yoast_focus_keyword will be null in most installs. WordPress core only exposes meta keys that are registered with show_in_rest, and Yoast SEO does not register _yoast_wpseo_focuskw for core REST exposure by default — see #17 for context on the broader meta-key REST exposure issue. The summary tool keeps the field in the response shape for forward compatibility once a companion plugin registers it.

Test plan

  • npm run build — only the two pre-existing TS errors should show
  • Call get_content_summary with { "id": <known post id> } against a live WP install — verify minimal shape, taxonomy IDs returned, word_count present
  • Call with { "url": "<known post url>" } — verify same shape, content_type detected from URL
  • Call with both id and url — expect error "Provide exactly one of id or url, not both."
  • Call with neither — expect error "Provide one of id or url."
  • Confirm existing find_content_by_url still works (refactored to use the new helper)

🤖 Generated with Claude Code

Returns a minimal, fixed-shape summary (id, title, slug, status, link,
excerpt, modified date, taxonomy IDs, featured media, word count, Yoast
SEO fields) for audit and lookup workflows where the full WP REST
response is overkill — recipe posts can exceed 50KB because of rendered
Recipe Maker card HTML.

Look up by `id` (with optional `content_type`, defaulting to "post") or
by `url`. Bypasses the response trim added in PR InstaWP#16 via the documented
`rawResponse: true` escape hatch so it can read yoast_head_json for the
SEO fields and Yoast's wordCount.

Also extracts the URL→post resolution from `find_content_by_url` into
a reusable `findContentByUrl` helper, dropping the duplicated update/
return logic along the way.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant