Produced by k8s-platform/tools/generate-mcp-tools.py: deck file openapi2mcp does the conversion, and the wrapper re-injects the five things Kong's generator cannot emit — per-tool ACLs, the MCP behavioural annotations, the response schemas the plugin uses for structuredOutput, the real upstream host, and JSON-only request bodies. Each is a documented gap; see k8s-platform/MCP-TOOL-GENERATION.md. tools-policy.yaml carries the ACLs and doubles as the allow-list: an operation becomes a tool only if the policy names it. Kong would otherwise expose every operation in the spec, DELETE included, and can express no authorization at all from a bearer-token spec — silently, exit 0, no warning. Same tools as before, same ACLs. What changed: query parameters are now declared and callable, each tool carries its response schema so calls return structured content instead of raw JSON, and request bodies are no longer tripled across three identical content types. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
payments-api
Kong routing/policy layer for the payments slice of
classic-models-api (see /Users/stephanetailland/git/classic-models-api),
split out per the DDD decomposition in ../kong-konnect-deployment-strategy.md.
The backend itself stays a single monolith — only Kong does the domain
split. Structure mirrors ../customers-api, not ../catalog-api — see
../STATUS.md section 18 for why (catalog-api's two-route MCP pattern
has a latent bug; customers-api's single-route pattern is the corrected
one to copy going forward).
Two exposure surfaces, both proxying to the real classic-models-api
backend:
- REST (
kong/rest/) — 6 routes overPayment, which uses a composite key (customernumber+checknumber, not a single id likeCustomer/Product) — both URI segments are captured and passed through to the backend's own composite-key URL pattern. - MCP (
kong/mcp/) —list-payments/get-payment(all 5 roles) andcreate-payment(admin only — verified againstPaymentPermission's actual logic inauthentication/permissions.py, not guessed: writes are admin-only in practice,customercan only write their own via the REST API directly, not exposed as an MCP tool). OneKongRoutefor all three MCP HTTP methods, oneai-mcp-proxybinding — see../STATUS.mdsection 18 for why this matters (a second route + binding for the same plugin creates a duplicate-tool-name bug, found and fixed oncustomers-mcp, built correctly here from the start).
| Route | Method | Backend path |
|---|---|---|
/payments/v1/payments/ |
GET, POST | /classic-models/api/v1/payments/ |
/payments/v1/payments/{customernumber}/{checknumber}/ |
GET, PUT, PATCH, DELETE | /classic-models/api/v1/payments/{customernumber}/{checknumber}/ |
/payments-mcp |
GET, POST, DELETE | MCP streamable-http (tools call into the REST routes above) |
Auth
openid-connect on both surfaces, distinct audience each (payments-api
for REST, mcp-payments for MCP) with a real RFC 8693 exchange chain:
MCP → payments-api → classic-models-api, same pattern as
catalog-api/customers-api. IdP is the local Keycloak
(https://keycloak.sttlab.local/realms/classic-models) — see
../STATUS.md sections 15/16. payments-api-audience and
mcp-payments-audience client scopes + payments-api/mcp-payments
resource clients created on the local Keycloak, attached to
token-exchange-broker's default scopes (REST audience) and to
librechat's optional scopes (MCP audience) respectively — no dedicated
protocol mappers on either new client itself, same practice as every
other domain built this session. mcp-payments's OAuth callback added to
librechat's redirectUris, and a payments-mcp server entry added to
LibreChat's librechat.yaml (infra-librechat repo).
Other policies
Per-consumer rate limiting, response caching (GET/HEAD), audit log
(http-log, endpoint still a placeholder — no real log collector in this
lab, same caveat as the other domains).
Structure
Same dev/ + kong/rest/ layout as customers-api. Deploy key
(kong-v2/payments-api/git-deploy-key) is a copy of catalog-api's,
not a freshly generated one — per the user's explicit instruction this
session ("no reason a working key for catalog-api wouldn't work for
customers-api"), the same reused key is used here too rather than minting
yet another one per domain.