Conversation
Added a description_append to the retrieve_payment_transaction_checkout enrichment overlay so a static curl block is baked into the page at build time and picked up by the MCP indexer. Previously the curl snippet was rendered client-side by ApiExplorer and never appeared in docs_fetch responses from docs.ottu.com/mcp.
…CP response" This reverts commit 97cde5f.
…pages
The MCP server indexes the static HTML of generated .api.mdx pages so AI
tools (via docs_fetch) can retrieve usable request examples. The OpenAPI
plugin's interactive ApiExplorer already renders a curl example for human
readers, so duplicating it visibly in the page body would be redundant and
hurt the reading experience.
This adds a clean two-stage pipeline to thread per-operation MCP hints
through without surfacing them to users:
- enrich-api-spec.ts: support an optional `mcp_hint` field on operation
enrichment overlays, copied verbatim onto the operation in the enriched
spec (alongside permissions/description enrichments already supported)
- inject-mcp-hints.ts (new): reads `mcp_hint` back out of the enriched spec
after gen-api-docs runs, and patches the matching .api.mdx with a
visually-hidden <span style={{display:"none"}}> just before the Request
heading, so the MCP's static-HTML indexer picks up the text while it
stays invisible in the rendered page
- package.json: wire `inject-mcp-hints` as the final step of `gen-api`,
after extract-schemas, so generated docs always carry their hints
Keeping enrichment (write mcp_hint to the enriched spec) and injection
(read mcp_hint, patch .mdx) as separate scripts mirrors the existing
fetch -> enrich -> generate separation of concerns in this pipeline.
…te page
Populates the mcp_hint overlay field (added in the previous commit's
enrichment-engine support) for retrieve_payment_transaction_checkout — the
first operation to carry one — with a runnable curl example pointing at the
sandbox host:
curl -X GET "{{sandboxUrl}}/b/checkout/v1/pymt-txn/{session_id}/" \
-H "Authorization: Api-Key YOUR_API_KEY"
Regenerating via `npm run gen-api` resolves the {{sandboxUrl}} template
variable to the real sandbox host, threads the hint through to the enriched
spec's operation object, and lets inject-mcp-hints.ts patch it into
retrieve-payment-transaction-checkout.api.mdx as a visually-hidden span —
giving the MCP server's docs_fetch a concrete request example for this
operation without changing what human readers see on the page.
…operation
The hand-curated mcp_hint approach doesn't scale — it requires an overlay
author to write and maintain a runnable curl string per operation, and only
the one operation that gets a hint shows up in docs_fetch results. Every
other operation's MCP example would stay missing indefinitely.
Rewrites inject-mcp-hints.ts to auto-generate a curl example for ALL
operations in the enriched spec, using the exact same conversion and
snippet-rendering pipeline the interactive ApiExplorer uses client-side:
- openapi-to-postmanv2 (SchemaPack) converts the spec into a Postman
collection, mirroring the plugin's own createPostmanCollection step
- postman-code-generators renders the curl snippet, with the same default
cURL variant options the ApiExplorer uses (mirrored from
docusaurus-theme-openapi-docs's CodeSnippets/languages.json)
- the openapi-docs plugin's own sampleRequestFromSchema builds the JSON
request body example for write endpoints, so the body in the generated
curl matches the example already shown in the page's request schema
instead of a garbled schema-faker dump
- @apidevtools/json-schema-ref-parser dereferences the spec first, since
sampleRequestFromSchema can't fake a bare unresolved $ref
The generated curl is injected as a JSON.stringify-wrapped JS string
expression inside the hidden span — {${JSON.stringify(curl)}} rather than
raw text — because the curl text contains literal `{`/`}` (path templates,
JSON bodies) that MDX's acorn-based expression parser would otherwise choke
on ("Could not parse expression with acorn").
Operation matching reuses the plugin's own path-to-regex templating
(bindCollectionToApiItems / pathTemplateToRegex) to bind each Postman
collection item back to its OpenAPI operation by method + path shape.
The new auto-generation injector (previous commit) produces a curl example for every operation directly from the spec — it no longer reads mcp_hint. The hand-curated overlay field and the enrichment-engine support that wrote it through to the spec are now dead weight that would only mislead future overlay authors into thinking they need to write one per operation. Removes: - the mcp_hint value from the retrieve_payment_transaction_checkout overlay - the mcp_hint?: string field from OperationEnrichment and the block in enrichOperations() that copied it onto the operation Net effect on enrich-api-spec.ts and checkout-api.yaml is a clean revert to their pre-mcp_hint state; the stale hint span left behind in retrieve-payment-transaction-checkout.api.mdx gets replaced by the auto-generated one in the next commit's full regeneration.
Runs the upgraded inject-mcp-hints.ts (two commits back) across a fresh
gen-api-docs build, so every one of the 28 operations now carries a hidden,
auto-generated curl example — not just the single operation that previously
had a hand-written mcp_hint.
For read endpoints this is a plain authenticated request against the
sandbox host (e.g. retrieve-payment-transaction-checkout's GET on
/b/checkout/v1/pymt-txn/{session_id}/); for write endpoints
(create/full-update/partial-update-payment-transaction-checkout, etc.) the
curl now also carries a clean JSON body generated by the same
sampleRequestFromSchema function the page's own request-schema example uses
— e.g. {"type": "e_commerce", "amount": "10.000", "currency_code": "KWD",
"pg_codes": ["knet"]} — instead of the garbled schema-faker dumps a naive
conversion would produce.
Each example is invisible to human readers (display:none) but gets indexed
by the MCP server's static-HTML crawler, so docs_fetch now returns a
copy-paste-ready request example for every endpoint in the API reference,
matching what the interactive ApiExplorer shows on the page.
@apidevtools/json-schema-ref-parser, openapi-to-postmanv2, postman-collection, and postman-code-generators are imported directly by scripts/inject-mcp-hints.ts but were only present as transitive dependencies of docusaurus-plugin-openapi-docs. That's fragile: a future major bump of docusaurus-plugin-openapi-docs could drop or replace any of them, silently breaking curl generation at gen-api time with no build failure (the script just stops finding any operations to inject). Adds all four as explicit devDependencies, pinned to the versions already resolved in package-lock.json. No transitive resolution changes.
applyServerAndAuth previously stuffed the full base URL (including scheme) into a single-element host array and cleared protocol, which works but isn't how postman-collection's Url model expects host/protocol to be structured. Parse the base URL and set protocol and host (as dot-separated segments) the canonical way instead. No change in generated output: regenerated all 28 .api.mdx files and diffed against the committed versions — byte-identical.
The MCP plugin converts page HTML to Markdown via rehype-remark and remark-stringify, which escape `_` and `:` in plain text nodes per CommonMark rules. Since the hidden curl example was a <span>, its text content came out of docs_fetch with stray backslashes (https\://, session\_id, YOUR\_API\_KEY), making it unsafe to copy-paste directly. Code blocks aren't escaped by remark-stringify, so switching to <pre> makes rehype-remark emit a fenced code block instead, preserving the curl verbatim with real line breaks.
Output of npm run gen-api after the <pre> injection fix — every hidden curl example switches from <span> to <pre> so docs_fetch returns a clean, copy-paste-ready command instead of one with stray markdown escape backslashes.
INJECTION_ANCHOR required <Heading and id={"request"} to be on
separate lines with a specific indentation pattern. If
docusaurus-plugin-openapi-docs ever reformats this JSX (single line,
different indentation, etc.), the anchor silently stops matching and
the curl example is dropped without warning.
\s+ matches any whitespace including newlines, so the anchor now
matches regardless of line breaks or indentation between the two
tokens.
…n failures
Previously a missing servers[0].url silently returned {injected: 0,
failures: []} from processSource, which main() treated as success even
though every operation in that source was skipped. Add an optional
SourceResult.error field for source-level failures (distinct from
per-operation failures) and report both separately. Either kind of
failure now exits with code 1, and the "Done — N file(s) patched"
summary only prints on a fully clean run.
Policy: docs.ottu.com must advertise only the public sandbox merchant sandbox.ottu.net. This switches every DISPLAYED host to sandbox.ottu.net: - checkout-sdk/web.mdx: merchant_id x11 -> sandbox.ottu.net - src/constants/api.ts: OTTU_CONNECT_BASE_URL -> sandbox.ottu.net (flows into the reports / psq / invoices / wallet curl + response samples) - getting-started/api-fundamentals.md: "shared sandbox" prose -> sandbox.ottu.net NOT changed here (the LIVE surfaces): the interactive demos (ACTIVE_CONNECT), the API-explorer "try it", and the wallet-seed backend still run on ksa.ottu.dev. They can only move once wallet exists on sandbox.ottu.net — verified today it does NOT (0 wallet gateways on sandbox.ottu.net vs 15 on ksa.ottu.dev). See the ACTIVE_CONNECT comment in src/utils/sandbox.ts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The WalletDemo filters wallet gateways with tags: ["demo"] (WALLET_DEMO.pgFilter), but the "no wallet gateway" error message told users to tag the PG "docs". The API rejects tags: ["docs"] outright (HTTP 400, "docs" is not a valid choice), so the guidance was actively wrong. Align the message with the actual filter + the working KSA config. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
injectSpan had no way to detect an existing injection, so re-running inject-mcp-hints standalone (without a clean-api-docs pass first) would prepend a second hidden curl block into every already-patched .api.mdx file. Skip files that already contain the injection marker.
docs(api): add curl example as hidden content in the APIs generated pages to let MCP detect it and return it in the response for all APIS
Wallet is now live on sandbox.ottu.net (PG `ottu-sandbox-usd`, USD), so the last KSA-pinned surfaces move to the public sandbox merchant: - ACTIVE_CONNECT -> SANDBOX: Checkout / Recurring / PaymentJourney / Wallet demos now run against sandbox.ottu.net - WALLET_DEMO currency KWD -> USD (the sandbox wallet PG is USD-only) - API-explorer "try it" + auto-generated MCP curls: base URL -> sandbox.ottu.net. The enrich script now forces `servers` from `_variables.yaml` sandboxUrl, so gen-api (incl. inject-mcp-hints) stays correct after a raw-spec re-fetch; the explorer's demo key now tracks ACTIVE_CONNECT instead of a hardcoded KSA key - regenerated docs/developers/apis/ (servers + MCP curls now sandbox.ottu.net) - raw static/Ottu_API.yaml servers URL -> sandbox.ottu.net NOTE: the wallet SEED backend (mcp-server/server.mjs -> KSA_OTTU_DEV) still mints on KSA's wallet service. The wallet demo's seed->pay flow needs sandbox.ottu.net's wallet service URL + Keycloak realm/clientId + secret before it works end-to-end. Discovery + session + display are already on sandbox.ottu.net. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The docs run on sandbox.ottu.net now, so the wallet seed backend mints on that merchant's wallet instead of KSA: - config.mjs: add SANDBOX_OTTU_NET — merchant sandbox.ottu.net, shared wallet.ottu.dev + auth.ottu.dev, realm sandbox.ottu.net, client backend, secret env SANDBOX_KEYCLOAK_CLIENT_SECRET (confirmed in Keycloak) - server.mjs: handleSeedWallet seeds via SANDBOX_OTTU_NET Verified end-to-end locally: POST /seed-wallet for ottu-sandbox-usd (USD) -> 201, balance minted. DO mcp component has SANDBOX_KEYCLOAK_CLIENT_SECRET set on docs-dev (docs-prod set at merge time). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this does
Moves everything on
docs.ottu.comto the public sandbox merchantsandbox.ottu.net— display, live demos, API reference, and the wallet demo's seed backend. Replaces the earlier KSA-pointed state.Changes
sandbox.ottu.net: Checkout SDKmerchant_id×11, all curl/JSON samples (viaOTTU_CONNECT_BASE_URL), "shared sandbox" prose.ACTIVE_CONNECT = SANDBOX): Checkout / Recurring / PaymentJourney / Wallet now run onsandbox.ottu.net.ottu-sandbox-usdis USD-only); stale"docs"discovery-tag guidance fixed to"demo".sandbox.ottu.net. The enrich script now forcesserversfrom_variables.yaml sandboxUrl(fetch-proof); explorer demo key tracksACTIVE_CONNECT.mcp-server): newSANDBOX_OTTU_NETconfig (merchantsandbox.ottu.net, sharedwallet.ottu.dev+auth.ottu.dev, realmsandbox.ottu.net, clientbackend, secret envSANDBOX_KEYCLOAK_CLIENT_SECRET).Verification
POST /seed-wallet→ 201, balance minted.npm run build+npm run typecheckpass; API reference regenerated (0ksa.ottu.dev).SANDBOX_KEYCLOAK_CLIENT_SECRETset on themcpcomponent of both DO apps (docs-dev + docs-prod).🤖 Generated with Claude Code