Skip to content

docs: make service pages usable and fix config examples that silently no-op - #193

Open
IKatsuba wants to merge 1 commit into
vercel-labs:mainfrom
IKatsuba:docs/make-service-pages-usable
Open

docs: make service pages usable and fix config examples that silently no-op#193
IKatsuba wants to merge 1 commit into
vercel-labs:mainfrom
IKatsuba:docs/make-service-pages-usable

Conversation

@IKatsuba

Copy link
Copy Markdown

Why

I tried to use emulate from the docs and kept getting stuck. Two things kept happening:

Service pages tell you nothing about how to use the service. You click Stripe in the sidebar and get a list of endpoints. No start command, no port, nothing about how to point the Stripe SDK at it. I checked all 14 pages: 12 had no start command at all, and Stripe, Resend and MongoDB Atlas had no connection information of any kind.

Copying a config example from the docs can leave you with an empty store and no error. Seed config is never validated (start.ts passes the parsed YAML straight to seedFromConfig), so a key that doesn't exist in the schema is dropped silently. Several documented keys don't exist.

The clearest example: the Stripe seed block in docs/configuration and in the Stripe package README both say

prices:
  - product: Pro Plan

but StripeSeedConfig declares product_name, and the matcher at packages/@emulators/stripe/src/index.ts:89 filters on pr.product_name === p.name. Copy the documented example and you get zero seeded prices, no warning, and checkout fails later with No such price. The correct key is already used in skills/stripe/SKILL.md and in the emulate init template in registry.ts — so emulate init generates a config that contradicts the docs, on the same "Pro Plan" example.

I verified every claim in this PR against the source rather than against other docs.

What changed

Service pages are now usable

Every service page opens with the same three sections before the endpoint list:

  • Start — the command, and which port the service actually lands on
  • Point your SDK at it — a real snippet for that SDK: new Octokit({ baseUrl }), Slack's slackApiUrl with its required trailing /api/, AWS needing endpoint and forcePathStyle: true, Stripe's host/port/protocol, Resend's RESEND_BASE_URL, Okta's orgUrl, OIDC discovery for Apple/Microsoft/Google
  • Seed config — a correct YAML block, with the traps for that service noted inline

Linear and Twilio already had this shape, so they are largely unchanged — the other 12 were brought up to match them.

Config keys corrected

All of these silently seed nothing today:

Where Was Is
Stripe product:, recurring: product_name:; recurring removed (the emulator hardcodes type: "one_time")
Okta firstName, lastName first_name, last_name
Okta authorization_servers without id id documented as required — it becomes server_id, which the OAuth routes look up
Resend api_keys: contacts: (api_keys is not in ResendSeedConfig)
GitHub App -----BEGIN RSA PRIVATE KEY----- -----BEGIN PRIVATE KEY----- + the openssl pkcs8 -topk8 command

The PKCS#1 one is worth calling out: auth.ts:85 uses importPKCS8, which cannot read PKCS#1, and the exception is swallowed by an empty catch. GitHub hands out App keys in PKCS#1, so following the docs gives you a JWT that is silently treated as unauthenticated. This was documented in five places.

The port rule is now written down

A port is --port (default 4000) plus the service's index in the enabled set — not a fixed number per service. --service github binds 4000, not 4001. This was not stated anywhere, and it had broken several skills, which showed a start command producing one port and then used a different one throughout:

  • skills/aws used localhost:4006 in 45 places, which is Okta — with a full fleet running, those requests silently hit the wrong emulator instead of failing
  • skills/apple, skills/microsoft, skills/slack, skills/github, skills/google had the same mismatch
  • hero-terminal.tsx had AWS and Okta swapped and was missing three services; the landing page said "11 services" and used the wrong Stripe port

Fixed by pinning --port in each skill's start command, so the examples are valid both standalone and under a bare npx emulate. Also documents the <service>.port seed key, which is the only way to pin a port and was not mentioned anywhere.

Undocumented runtime behaviour

  • createEmulator seed nesting. api.ts:45 reads seed[service], so the seed object must repeat the service name even though service is passed separately. The adapters take the opposite shape. Both had one line of docs — "same shape as YAML config" — and no example. Now both are shown, with the wrong version marked.
  • Rate limiting. Every service applies 5000 req/hr per token and returns 403, not 429, with all anonymous traffic in one bucket. This was not mentioned in any doc, and GitHub's emulated /rate_limit reports static values that don't reflect it.
  • Token checking. Any non-empty bearer token is accepted; only a missing Authorization header gives 401. Two skills stated this backwards. A test asserting "invalid token → 401" passes against the real API and fails here.
  • A config file acts as an implicit --service. npx emulate init --service stripe then npx emulate starts Stripe alone on 4000, not all 14.

Missing docs added

  • Clerk — page, package README, skill. It was the only one of 14 services with no documentation at all, while being listed as supported in six places, including the docs-site AI assistant's system prompt (which has no Clerk content in its corpus).
  • Okta and MongoDB Atlas skills — both had pages but no skill, so an agent asked to "emulate Okta locally" had nothing to match on.
  • Troubleshooting page — the silent-failure modes above, plus DEBUG=1/EMULATE_DEBUG=1, EADDRINUSE, and --portless/--base-url being mutually exclusive.
  • Connecting page — the general "point your SDK at a different base URL" pattern in one place.
  • examples/README.md — the seven examples weren't linked from any human-facing doc.

Examples

Six of seven examples don't start by following their README. They import @emulators/* from dist/, which is gitignored with no postinstall, so pnpm build is required first — documented in only one of them. Also: examples/oauth told you to run npx emulate --seed emulate.config.yaml, but that file has never existed in the repo history (git log --all), and the CLI exits 1 when a seed file is missing. The app works fine with a bare npx emulate, which is what the README now says. Four READMEs pointed at http://localhost:3000 for apps served through portless on a random port.

Docs contributor notes

AGENTS.md had no build, test, lint or type-check commands, and its "adding a service" checklist pointed at packages/emulate/src/index.ts, which has no service list — registration is in registry.ts. The checklist also omitted the four separate navigation files, which is why /docs/twilio shipped with no nav entry and Clerk shipped with nothing.

Note on tables

This MDX setup calls createMDX() with no plugins, so there's no remark-gfm and pipe tables render as a paragraph of run-together text. All tables here are HTML, matching the existing pages. The tables on the Linear and Twilio pages were already broken this way and are fixed in this PR.

The root cause is still there — the next markdown table will fail the same way, and the build won't complain. A one-line createMDX({ options: { remarkPlugins: [remarkGfm] } }) plus the dependency would fix it, but that's a build change rather than a docs change, so I left it out. Happy to add it if you want.

Testing

  • pnpm --filter web build passes; all 30 routes generate, including the three new pages
  • pnpm --filter web lint clean
  • Checked every service skill's port references against registry.ts with a script
  • Verified all 21 internal README anchors resolve

One pre-existing failure is unrelated and untouched: pnpm lint fails on examples/nuxt-embedded, which reproduces on a clean checkout of main.

Not included

No source or build changes — this is documentation only. Two structural fixes from the same investigation are deliberately left out, and without them this class of drift will come back:

  1. Validate seed config. Every silent failure above exists because nothing checks unknown keys. Warning on unrecognized top-level keys in start.ts and api.ts would turn the whole class into a loud error.
  2. One source of navigation. The docs nav is duplicated across docs-nav.tsx, docs-mobile-nav.tsx and lib/docs-navigation.ts. Twilio was in one of the three, which is why its page was reachable by search but not by the sidebar.

Happy to follow up with either.

… no-op

Service doc pages were endpoint catalogues: 12 of 14 had no start command and
Stripe, Resend and MongoDB Atlas said nothing about how to connect an SDK. Every
page now opens with Start, a working SDK snippet, and a seed config block before
the endpoint list.

Also fixes documented config keys that do not exist in the schemas. Since seed
config is never validated, these produced an empty store with no error:

- Stripe `product:` -> `product_name:`, dropped the non-existent `recurring:`
- Okta `firstName`/`lastName` -> `first_name`/`last_name`, added required
  `authorization_servers[].id`
- Resend `api_keys:` -> `contacts:`
- GitHub App `private_key` PKCS#1 -> PKCS#8, with the openssl conversion command

Documents the port rule (base + index over the *enabled* set, not a fixed number
per service) and corrects ports in six skills, the landing page and the
authentication page. Documents `seed` nesting for `createEmulator`, the 5000/hr
rate limiter that returns 403, and the permissive token check.

Adds the missing Clerk docs (page, package README, skill), Okta and MongoDB Atlas
skills, a Troubleshooting page, a Connecting page, and an examples index.

Converts markdown tables to HTML. This MDX setup has no remark-gfm, so pipe
tables render as a paragraph of text; the tables on the Linear and Twilio pages
were already broken this way.
@vercel

vercel Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

@IKatsuba is attempting to deploy a commit to the Vercel Labs Team on Vercel.

A member of the Team first needs to authorize it.

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