Skip to content

feat(seo): add product pages, starting with QuickBooks MCP - #421

Merged
jfrench9 merged 2 commits into
mainfrom
feature/product-pages
Sep 29, 2026
Merged

jfrench9 merged 2 commits into
mainfrom
feature/product-pages

Conversation

@jfrench9

Copy link
Copy Markdown
Member

Summary

Adds product pages to roboledger.ai. A product page answers a search query whose results are the product itself (repos, MCP directories, vendor integration pages) rather than articles. For those queries a blog post is the wrong format to compete. The first page is /quickbooks-mcp, for "quickbooks mcp" and "quickbooks mcp server".

Consistency comes from one registry. Each page's metadata, structured data, sitemap entry, llms.txt line and footer link are all derived from it, and a test checks every entry against the SEO checklist. A new page can't ship half-wired.

Changes

  • src/lib/product-pages.ts: the registry, plus productPageMetadata (title, description, self canonical, Open Graph/Twitter) and productPageJsonLd (SoftwareApplication + BreadcrumbList, with no offers, since the site doesn't advertise a price).

  • src/app/(pages)/: a new route group with the marketing header and footer, like the blog. It's server-rendered with no auth gate, so crawlers get the full page.

  • /quickbooks-mcp: the MCP address (/v1/mcp/roboledger), connect steps and per-client setup (Claude, Claude Code, ChatGPT, Cursor/VS Code), what the AI can do, how it differs from Intuit's server and connector (linking to /blog/quickbooks-mcp), what writes back, and requirements. Copy is checked against the product docs (connect-your-books, what-claude-can-do, quickbooks-write-back). The page has its own social card.

  • sitemap.ts: lists product pages, dated by each page's updated date, not the request time.

  • llms.txt: a new ## Product section built from the registry.

  • Footer: Product links now include each registered page.

  • Tests: product-pages.test.ts covers the checklist:

    • the title leads with the query and is 60 characters or fewer
    • the description contains the query and is 160 characters or fewer
    • the query is in the H1
    • the canonical points at the page itself, and the lastmod is a real date
    • there's a docs link, a route and a social card, with unique paths, queries and titles

    Also new: sitemap and llms.txt assertions for product pages.

Deploy Notes

None. After deploy, the launch steps are manual: resubmit the sitemap in Search Console and Bing, and request indexing for /quickbooks-mcp.

Testing

  • npm run test:all: 95 files, 701 tests, plus format, lint and typecheck.
  • npm run build: /quickbooks-mcp and its social cards prerender as static.
  • Served the build with next start and fetched the page as bingbot: one H1, the correct canonical and description, both JSON-LD blocks, the social card at 200, the sitemap entry and the llms.txt line.
  • Screenshots at desktop and 390px width. That found two phone-layout issues, both fixed: the hero gradient stopped mid-paragraph, and the MCP address was cut off (it now wraps).

🤖 Generated with Claude Code

A product page answers a query whose search results are the product itself
rather than articles. One registry drives each page's metadata, structured
data, sitemap entry, llms.txt line and footer link, and a test checks every
entry against the SEO checklist (query-led title, description under 160
characters, query in the H1, self canonical, real lastmod, a docs link,
a route and social card).
@github-actions github-actions Bot added the change:normal Normal change: automated review documented on the pull request before merge label Sep 29, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Change classification: normal — platform or application code changed.

A normal change carries a documented automated review on this pull request before merge, in place of a second human approver. The change-classification job requests that review and passes once it has been posted. Recorded by the change-classification job.

@jfrench9

Copy link
Copy Markdown
Member Author

@claude please review this PR

@claude

claude Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

Claude finished @jfrench9's task in 3m 5s —— View job


Review: PR #421 — product pages (QuickBooks MCP)

  • Gather context (PR diff, changed files)
  • Read registry (src/lib/product-pages.ts) and its tests
  • Read new route files (layout.tsx, quickbooks-mcp/page.tsx, image routes)
  • Read sitemap/llms.txt/footer diffs
  • Post review feedback

Note: this sandbox's Bash tool only permitted plain git commands (npm/node were blocked pending approval), so I read the diff and cross-checked call sites by hand rather than re-running npm run test:all/build. The PR description reports both green, and the code lines up with what those runs would need.

Summary

Clean, well-scoped implementation. The registry-driven approach (one ProductPage entry feeds metadata, JSON-LD, sitemap, llms.txt, and the footer, with a test enforcing the SEO checklist) is exactly the kind of "can't ship half-wired" design this repo's CLAUDE.md asks for. I didn't find any correctness bugs — /quickbooks-mcp is correctly excluded from robots.ts's disallow list, the canonical/OG/Twitter metadata is self-consistent, the SoftwareApplication + BreadcrumbList JSON-LD mirrors the existing homepage pattern (src/lib/structured-data.ts) without colliding with it, and REGISTER_PATH/docs links (e.g. /docs/connect-your-books, already referenced from src/app/(app)/connections/content.tsx) check out.

Two small reuse observations, non-blocking:

  1. src/app/(pages)/layout.tsx is a byte-for-byte duplicate of src/app/(blog)/layout.tsx (same min-h-screen bg-black / Header / pt-24 / Footer shell, differing only in the comment and function name). Worth a shared MarketingLayout component the two route groups both use, so a future change (e.g. header height) can't drift between copies — the same concern CLAUDE.md calls out for the old per-page filter bars.
  2. A third inline copy of the JSON-LD escape wrapper. quickbooks-mcp/page.tsx:91-99 inlines <script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(...).replace(/</g, '\\u003c') }} /> — the same few lines already factored out as a local JsonLd helper in both BlogJsonLd.tsx and DocsJsonLd.tsx. With a third occurrence (soon to be more, once a second product page lands), pulling that helper into a shared spot (e.g. src/components/JsonLd.tsx) would remove the duplicate escaping logic rather than growing a third/fourth copy.

One item to confirm outside the diff, not a code issue: the "Read the full comparison →" link to /blog/quickbooks-mcp and the docs links resolve against externally-hosted catalogs (S3 blog catalog, docs repo) that this checkout can't verify statically — the PR description says these were checked live (bingbot fetch, screenshots), so I'm taking that as confirmed rather than re-verifying.

Fix this →

… MCP page

The page's address, /v1/mcp/roboledger, connects RoboLedger graphs only, so
comparing with public companies takes a second connection at /v1/mcp.
@jfrench9
jfrench9 merged commit ff7764e into main Sep 29, 2026
7 checks passed
@jfrench9
jfrench9 deleted the feature/product-pages branch September 29, 2026 06:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

change:normal Normal change: automated review documented on the pull request before merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant