Repository navigation
Single-source provider guides: bundled docs/guides catalog serving MCP, /guides pages, and raw text/markdown - #1255
Conversation
…pages, and raw text/markdown
|
Warning Review limit reached
Next review available in: 24 minutes You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (11)
📝 WalkthroughWalkthroughThe pull request adds a build-time guide catalog with YAML metadata, provider setup documentation, HTML/JSON/Markdown delivery, MCP lookup, web guide pages, copyable code blocks, and provider-specific setup links. ChangesGuide catalog and content
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant Visitor
participant GuidesRoute
participant GuideApi
participant GuideCatalog
participant MarkdownView
Visitor->>GuidesRoute: Open /guides
GuidesRoute->>GuideApi: Request guide summaries
GuideApi->>GuideCatalog: List bundled guides
GuideCatalog-->>GuideApi: Return metadata
GuideApi-->>GuidesRoute: Return JSON
GuidesRoute-->>Visitor: Render grouped guide links
Visitor->>MarkdownView: Open a guide
MarkdownView-->>Visitor: Render Markdown and copyable code
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 00d3bf8. Configure here.
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (1)
packages/worker/client/routes/integration-provider-catalog.node.test.ts (1)
8-24: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick winStrengthen the guide parity test.
The test checks only that a provider identifier appears in the prompt. It can pass if expected provider mappings are removed or if the guide load, minimal-access, save, or smoke-test instructions regress. Assert the intended provider-to-slug mappings and the guide-specific wording, including the absence of generic OAuth-app instructions.
Proposed assertions
const guideBacked = integrationProviderSuggestions.filter( (provider) => provider.guideSlug, ) - expect(guideBacked.length).toBeGreaterThan(0) + expect( + new Set(guideBacked.map((provider) => `${provider.id}:${provider.guideSlug}`)), + ).toEqual( + new Set([ + 'github:github', + 'google:google', + 'notion:notion', + 'spotify:spotify', + 'discord:discord', + ]), + ) for (const provider of guideBacked) { // existing catalog assertions const prompt = buildIntegrationSetupPrompt(provider) expect(prompt).toContain(`provider_${provider.guideSlug}`) + expect(prompt).toContain('Load the official Kody setup guide') + expect(prompt).toContain('smoke test') + expect(prompt).not.toContain('creating my own OAuth app') }The PR objective defines five verified provider guides and guide-backed setup prompts.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/worker/client/routes/integration-provider-catalog.node.test.ts` around lines 8 - 24, Strengthen the test around integrationProviderSuggestions, getGuideBySlug, and buildIntegrationSetupPrompt to verify the five expected provider-to-guide slug mappings explicitly. For each mapped provider, assert the prompt contains guide-specific wording covering guide loading, minimal access, saving, and smoke testing, and assert generic OAuth-app instructions are absent; retain the existing guide category validation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/guides/providers/github.md`:
- Around line 71-74: Update the GitHub OAuth token lifecycle documentation to
state that OAuth App tokens are automatically revoked after one year of API
inactivity, despite having no fixed expiration. Add guidance to reconnect the
saved secret using a fresh OAuth grant when authentication fails or an
inactivity-expiration error occurs, while preserving the existing refresh-token
and manual-revocation details.
In `@docs/guides/providers/spotify.md`:
- Around line 35-44: Update the Spotify Development Mode guidance to state that
new developers can create only one client ID. In the refresh-token guidance,
explain that rotation may omit a replacement token, so retain the existing token
when none is returned; note that tokens expire six months after authorization
and instruct users to reauthorize after invalid_grant, reflecting the rules
active as of August 6, 2026.
In `@packages/worker/src/app/handlers/guides.tsx`:
- Around line 47-58: Update the negotiated response handling in the guides
handler, including the renderAppPage branches around the referenced flow, to
ensure every HTML response includes Vary: Accept while preserving any existing
Vary header values. Reuse the response/header mechanism already used by
renderAppPage rather than replacing other headers.
In `@packages/worker/src/app/markdown-negotiation.ts`:
- Around line 18-37: Update prefersMarkdown to apply text/* and */* quality
values to both Markdown and HTML while resolving each representation by
media-range specificity. Track exact, type-wildcard, and global wildcard
qualities separately so an exact q=0 overrides less-specific ranges; preserve
the existing return comparison using the resolved qualities.
---
Nitpick comments:
In `@packages/worker/client/routes/integration-provider-catalog.node.test.ts`:
- Around line 8-24: Strengthen the test around integrationProviderSuggestions,
getGuideBySlug, and buildIntegrationSetupPrompt to verify the five expected
provider-to-guide slug mappings explicitly. For each mapped provider, assert the
prompt contains guide-specific wording covering guide loading, minimal access,
saving, and smoke testing, and assert generic OAuth-app instructions are absent;
retain the existing guide category validation.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: d548b6f3-9df9-452b-aa43-c88fe5bd6827
📒 Files selected for processing (46)
docs/guides/README.mddocs/guides/account-package-invocation-token-setup.mddocs/guides/account-secret-setup.mddocs/guides/integration-backed-app-happy-path.mddocs/guides/integration-bootstrap.mddocs/guides/oauth.mddocs/guides/openapi-integrations.mddocs/guides/package-authoring.mddocs/guides/package-lifecycle.mddocs/guides/package-service-pattern.mddocs/guides/package-subscriptions.mddocs/guides/platform-friction.mddocs/guides/providers/discord.mddocs/guides/providers/github.mddocs/guides/providers/google.mddocs/guides/providers/notion.mddocs/guides/providers/spotify.mddocs/guides/secret-backed-integration.mddocs/use/first-steps.mddocs/use/index.mdpackages/worker/client/copy-code-block.tsxpackages/worker/client/lazy-route.tsxpackages/worker/client/markdown-view.tsxpackages/worker/client/routes/account-integrations.tsxpackages/worker/client/routes/blog-area.tspackages/worker/client/routes/guide-detail.tsxpackages/worker/client/routes/guides.tsxpackages/worker/client/routes/index.tsxpackages/worker/client/routes/integration-provider-catalog.node.test.tspackages/worker/client/routes/integration-provider-catalog.tspackages/worker/client/site-footer.tsxpackages/worker/client/site-header.tsxpackages/worker/src/app/handlers/blog.tsxpackages/worker/src/app/handlers/guides.node.test.tspackages/worker/src/app/handlers/guides.tsxpackages/worker/src/app/loader-data.tspackages/worker/src/app/markdown-negotiation.node.test.tspackages/worker/src/app/markdown-negotiation.tspackages/worker/src/app/onboarding-data.tspackages/worker/src/app/router.tspackages/worker/src/app/routes.tspackages/worker/src/guides/catalog.node.test.tspackages/worker/src/guides/catalog.tspackages/worker/src/guides/parse-frontmatter.tspackages/worker/src/mcp/capabilities/coding/kody-official-guide.node.test.tspackages/worker/src/mcp/capabilities/coding/kody-official-guide.ts
| GitHub supports S256 PKCE and recommends it, but PKCE does not replace the | ||
| client secret — the secret is still required at the token endpoint, so the Kody | ||
| flow is `confidential`. OAuth App tokens do not expire and there are no refresh | ||
| tokens; revoke the grant from GitHub settings to kill a token. |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== locate file =="
git ls-files | grep -F 'docs/guides/providers/github.md' || true
echo "== relevant lines =="
if [ -f docs/guides/providers/github.md ]; then
nl -ba docs/guides/providers/github.md | sed -n '55,85p'
fi
echo "== nearby token lifecycle mentions =="
rg -n "expire|revok|refresh|OAuth App|confidential|perman|token" docs/guides/providers/github.md .github README.md 2>/dev/null | head -120 || true
echo "== deterministic check: does current snippet imply permanent tokens? =="
python3 - <<'PY'
from pathlib import Path
p=Path('docs/guides/providers/github.md')
if p.exists():
text=p.read_text()
print("contains OAuth App tokens do not expire:", "OAuth App tokens do not expire" in text)
print("contains refresh tokens:", "refresh tokens" in text)
PY
echo "== public docs availability probe =="
python3 - <<'PY'
import urllib.request, ssl
url="https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/token-expiration-and-revocation"
ctx=ssl.create_default_context()
try:
with urllib.request.urlopen(url, timeout=20, context=ctx) as r:
print("status", r.status)
# Search for relevant inactivity revocation phrasing.
data=r.read(50000).decode("utf-8", errors="replace")
hits=['after one year', 'one year', 'inactive', 'OAuth', 'OAuth App', 'no longer valid']
for h in hits:
print(f"{h}: {h.lower() in data.lower()}")
# Print compact excerpt around first 'one year'.
idx=data.lower().find('one year')
if idx != -1:
print("excerpt:")
print(data[max(0,idx-500):idx+700])
except Exception as e:
print("probe_failed", repr(e))
PYRepository: kentcdodds/kody
Length of output: 264
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== relevant lines =="
if [ -f docs/guides/providers/github.md ]; then
awk '{printf "%6d\t%s\n", NR, $0}' docs/guides/providers/github.md | sed -n '55,85p'
fi
echo "== nearby token lifecycle mentions =="
rg -n "expire|revok|refresh|OAuth App|confidential|perman|token" docs/guides/providers/github.md .github README.md 2>/dev/null | head -120 || true
echo "== deterministic check: does current snippet imply permanent tokens? =="
python3 - <<'PY'
from pathlib import Path
p=Path('docs/guides/providers/github.md')
if p.exists():
text=p.read_text()
print("contains OAuth App tokens do not expire:", "OAuth App tokens do not expire" in text)
print("contains refresh tokens:", "refresh tokens" in text)
PY
echo "== public docs availability probe =="
python3 - <<'PY'
import urllib.request, ssl
url="https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/token-expiration-and-revocation"
ctx=ssl.create_default_context()
try:
with urllib.request.urlopen(url, timeout=20, context=ctx) as r:
print("status", r.status)
data=r.read(50000).decode("utf-8", errors="replace")
hits=['after one year', 'one year', 'inactive', 'OAuth', 'OAuth App', 'no longer valid']
for h in hits:
print(f"{h}: {h.lower() in data.lower()}")
idx=data.lower().find('one year')
if idx != -1:
print("excerpt:")
print(data[max(0,idx-500):idx+700].replace('\n', ' '))
except Exception as e:
print("probe_failed", repr(e))
PYRepository: kentcdodds/kody
Length of output: 6583
🌐 Web query:
GitHub OAuth App tokens no longer valid after one year of inactivity token expiration revocation
💡 Result:
GitHub automatically revokes OAuth app access tokens and personal access tokens if they have not been used for one year [1][2]. This security measure is designed to limit the impact of potentially exposed tokens that are no longer actively required [1][2]. Key details regarding GitHub token inactivity and expiration include: - Scope of Inactivity Revocation: This one-year inactivity policy applies to both OAuth app access tokens and personal access tokens [1][2]. - Automatic Revocation: Once a token reaches the one-year threshold of inactivity, it is automatically revoked by GitHub and cannot be restored [1][2]. If the application or service requires continued access, a new token must be generated [1][2]. - Distinction from GitHub App Tokens: It is important to distinguish these from GitHub App user access tokens, which operate under a different lifecycle. By default, GitHub App user access tokens expire after eight hours and utilize refresh tokens for renewal [3][4]. - Token Limits: For OAuth apps, GitHub maintains a limit of ten active tokens per user/application/scope combination [5][1]. If an application exceeds this limit, the oldest tokens are automatically revoked to make room for new ones [5][1]. - Manual Revocation: Users can manually revoke access for any authorized OAuth app at any time through their personal account settings, which will invalidate all tokens associated with that app [6]. For more information on managing credentials, you can refer to GitHub's official documentation on token expiration and revocation [1][2].
Citations:
- 1: https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/token-expiration-and-revocation
- 2: https://docs.github.com/en/enterprise-cloud@latest/authentication/keeping-your-account-and-data-secure/token-expiration-and-revocation
- 3: https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/refreshing-user-access-tokens
- 4: https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-user-access-token-for-a-github-app
- 5: https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps
- 6: https://docs.github.com/en/organizations/managing-programmatic-access-to-your-organization/github-credential-types
🌐 Web query:
site:docs.github.com "OAuth App" "do not expire" "one year"
💡 Result:
In the context of GitHub, OAuth tokens do not have a fixed one-year expiration simply by existing, but they are subject to automatic revocation if they remain unused for one year [1][2]. Specifically, GitHub automatically revokes an OAuth token or personal access token if it has not been used to authenticate requests for a period of one year [1][3][2]. This is a security measure designed to ensure that inactive credentials do not remain valid indefinitely [1]. It is important to distinguish between different credential types on GitHub, as they have different lifecycle policies: - OAuth app access tokens: These are generally long-lived but are revoked automatically if unused for one year [3]. - GitHub App user access tokens: These are short-lived by default (expiring after 8 hours) and require the use of a refresh token (which expires after 6 months) [4][5]. Owners can configure these to never expire, though this is not recommended [1][2]. - Personal access tokens (fine-grained): These have configurable expiration dates, up to a maximum of one year, or can be set to never expire [3]. In summary, while an OAuth app token is not designed to expire after exactly one year of its own accord, the one-year mark serves as a threshold for automatic revocation due to inactivity [1][3][2].
Citations:
- 1: https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/token-expiration-and-revocation
- 2: https://docs.github.com/en/enterprise-cloud@latest/authentication/keeping-your-account-and-data-secure/token-expiration-and-revocation
- 3: https://docs.github.com/en/organizations/managing-programmatic-access-to-your-organization/github-credential-types
- 4: https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/refreshing-user-access-tokens
- 5: https://docs.github.com/en/enterprise-server@3.20/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-user-access-token-for-a-github-app
Document GitHub’s one-year inactivity revocation for OAuth App tokens.
OAuth App tokens do not have a fixed lifetime, but GitHub revokes them automatically after one year without use through the API. If authentication fails or an expired inactivity error appears, reconnect the saved secret with a fresh OAuth grant.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/guides/providers/github.md` around lines 71 - 74, Update the GitHub
OAuth token lifecycle documentation to state that OAuth App tokens are
automatically revoked after one year of API inactivity, despite having no fixed
expiration. Add guidance to reconnect the saved secret using a fresh OAuth grant
when authentication fails or an inactivity-expiration error occurs, while
preserving the existing refresh-token and manual-revocation details.
| - Since February 2026, Development Mode requires the app owner to hold Spotify | ||
| Premium. If Premium lapses, the app stops working until it is restored. | ||
| - New developers can create up to 25 client IDs. | ||
| - Some Web API endpoints are unavailable in Development Mode; see the | ||
| [February 2026 migration guide](https://developer.spotify.com/documentation/web-api/tutorials/february-2026-migration-guide) | ||
| for the endpoint list. | ||
| - Extended Quota Mode is only available to organizations with at least 250k | ||
| monthly active users — not a realistic path for individuals. Personal use | ||
| stays in Development Mode, which is fine for one user: the app owner can | ||
| always authorize their own app. |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Correct the Spotify Development Mode and refresh-token guidance.
Line 37 states that new developers can create 25 client IDs. Spotify limits new Development Mode developers to one client ID.
Line 79 states that PKCE refresh tokens rotate on every refresh. A refresh response can omit a replacement token. Refresh tokens also expire six months after authorization. Tell users to retain the prior token when no replacement is returned and to reauthorize after invalid_grant. As of August 6, 2026, these rules are active. (developer.spotify.com)
Also applies to: 79-80
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/guides/providers/spotify.md` around lines 35 - 44, Update the Spotify
Development Mode guidance to state that new developers can create only one
client ID. In the refresh-token guidance, explain that rotation may omit a
replacement token, so retain the existing token when none is returned; note that
tokens expire six months after authorization and instruct users to reauthorize
after invalid_grant, reflecting the rules active as of August 6, 2026.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/worker/src/guides/rewrite-relative-links.ts`:
- Line 41: Update the link-matching replacement in rewriteRelativeLinks to
recognize an optional Markdown link title after the destination, rewrite only
the destination, and preserve the title in the output. Add a regression case
covering a titled relative guide link such as `[OAuth](./oauth.md "OAuth
guide")` and verify it resolves correctly.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 6a4f5bf0-e269-4ed5-800d-cda9fedbb70e
📒 Files selected for processing (4)
packages/worker/src/guides/catalog.node.test.tspackages/worker/src/guides/catalog.tspackages/worker/src/guides/rewrite-relative-links.node.test.tspackages/worker/src/guides/rewrite-relative-links.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- packages/worker/src/guides/catalog.node.test.ts
- packages/worker/src/guides/catalog.ts
…e facts, GitHub token revocation nuance
…ng it against the new integration

Summary
Provider connect guides existed only as agent-improvised prompts ("walk me through creating my own OAuth app"), and
coding_guide_getfetcheddocs/guides/*.mdfromraw.githubusercontent.comat request time. This PR makesdocs/guides/a single bundled source of truth served three ways, and adds five verified provider connect guides.packages/worker/src/guides/): every guide carries YAML frontmatter (id,title,summary,category,provider,lastVerified); markdown is imported at build time (same pattern as blog posts) so MCP, web, and raw-markdown responses always serve identical deployed content.coding_guide_getnow serves the bundled catalog — no request-time GitHub dependency; enum, per-guide descriptions, and search keywords derive from frontmatter.openapi_integrationsbecomes loadable for the first time./guidesand/guides/:slug(blog-area chunk, SSR +.jsontwins), with a Guides link in the site header and footer./guides.md,/guides/:slug.md,/blog/:slug.md, plusAccept: text/markdownon the HTML routes (Vary: Accept; HTML wins ties so browsers are unaffected).docs/guides/providers/): Google, GitHub, Notion, Spotify, Discord — verified console steps (August 2026), prefilled/connect/oauth//account/secrets/newlinks, personal-token lanes where they exist, minimal-scope tiers, execute smoke tests, and the sharp edges (Google's Testing-mode 7-day refresh-token trap, Spotify's Premium requirement, Discord's OAuth-vs-bot split, Gmail restricted scopes).provider_*guides; docs updated (docs/guides/README.md,docs/use/first-steps.md,docs/use/index.md). A parity test pins suggestionguideSlugs to real catalog entries.first-partylink policy (root-relative links like/connect/oauth?..., user-scope paths still refused) and copyable code blocks (copy-code-block.tsx); the untrusted default is unchanged.Supersedes #1254 (recreated non-draft so CI and review run).
Testing
npm run validatefully green (all 12 jobs: 1882 unit tests, 8 Playwright e2e, MCP e2e, lint/format/typecheck/docs checks).coding_guide_getserving every bundled guide, integration-suggestion ↔ catalog parity..mdtwins,Accept: text/markdown, browser Accept → HTML, blog twin) and a recorded browser walkthrough.System recap — extends existing primitives (medium risk)
Mode: recap · Base:
main@34614dc1· Head:00d3bf8dClassification: extends — no new taxonomy primitive;
app-uigains routes/negotiation andcapability-registry'scoding_guide_getchanges its content source from runtime GitHub fetch to the build-time guide catalog (new shared modulepackages/worker/src/guides/, plumbing under existing primitives).Primitives touched
app-ui/guidesroutes,.mdtwins +Accept: text/markdownnegotiation, first-party markdown links, copyable code blockscapability-registrycoding_guide_getserves the bundled catalog; enum/keywords derive from guide frontmatterapp-sessionsonboarding-data.ts)System map
Guide markdown flows from
docs/guides/through the bundled catalog into both the MCP capability and the web routes.Legend: green = composes (wiring only) · amber = extended by this PR · red = new primitive · gray = context (unchanged, included only when an edge crosses it).
Before / after
Invariants
Per-user isolation untouched: guides are static repo content with no user data; the untrusted markdown link policy (user-scope refusal) stays the default and applies under the first-party policy too.
Summary by CodeRabbit
New Features
Documentation