* fix(mcp): render full row data in tool text output, not just counts
MCP clients that surface only the text content block (not structuredContent)
saw count/truncated summaries instead of the actual rows. Add a shared table
renderer (server/mcp/table.ts) and use it so every row ships in the text block
across research_keywords, get_ranked_keywords, get_keyword_metrics,
search_local_businesses, get_local_serp_results, get_google_business_questions,
find_serp_competitors, get_backlinks_profile, get_backlinks_overview,
get_domain_keyword_suggestions, get_rank_tracker, get_serp_results, and
get_search_console_performance.
* test(mcp): assert tool text output renders full row tables
Cover the shared table renderer (table.test.ts) and add per-tool assertions
that each tool's text content block contains the actual row data (exact
rendered rows, null->em-dash), guarding against a column wired to the wrong
field. Adds tool-text-output.test.ts for the service-backed tools and text
assertions to the DataForSEO and Search Console tool tests.
* release: v0.0.23
* style(mcp): prettier-format tool table columns
* feat: add paginated get_backlinks_profile MCP tool
Exposes detailed per-link backlink rows over MCP, reusing BacklinksService
paginated row fetching. Closes#36.
* fix(test): use type-narrowing text assertion to satisfy oxlint
expect.stringContaining inside toMatchObject tripped
typescript-eslint(no-unsafe-assignment), breaking ci:check. Match the
.toContain() pattern used elsewhere in the MCP tool tests.
* refactor(mcp): simplify get_backlinks_profile handler + fix cost estimate
- Drop the redundant backlinksRowsPageRequestSchema.parse re-validation in
the handler; the MCP SDK already validates args against inputSchema. Build
the service request straight from args, removing the duplicated defaults
that could silently diverge.
- Preserve the target length cap by adding .max(2048) to the input schema
(previously enforced only via the re-parse).
- Correct the credit estimate in the tool description: measured cost is
~30 credits/page, not the ~200-500 copied from get_backlinks_overview.
- Minor: type the statuses accumulator.
* fix(mcp): correct get_backlinks_overview credit estimate
Measured real cost: summary (~26 credits) + history for domain scope
(~29 credits) = ~50 per domain, ~25 for a single page. The previous
~200-500 figure was a ~5-10x overestimate (same one get_backlinks_profile
inherited).
---------
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Ben Senescu <bensenescu@gmail.com>