Commit Graph

16 Commits

Author SHA1 Message Date
Stéphane Tailland c583d28cff fix(catalog-mcp): create-product could never succeed
The tool's request schema omitted productcode, which catalog-api requires
(ProductRequest in openapi.yaml lists it among the eight required fields).
Since the declared schema is the only thing an MCP caller sees, there was no
input that worked: omit it and the API answers
{"productcode":["This field is required."]}; supply it and ai-mcp-proxy rejects
the call first with "Additional properties are not allowed ('productcode' was
unexpected)".

The schema now mirrors ProductRequest — productcode added, the same eight
fields marked required, and the length and format constraints carried over so
a model reads them rather than guessing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 17:34:06 +02:00
Stéphane Tailland 454c4c66e2 Restore SSE on the catalog MCP route
catalog was the only domain that split its MCP route in two: the main
route on POST/DELETE, and a separate -stream route matching GET, whose
only purpose was to carry a request-termination plugin answering 405
"SSE stream is not offered on this endpoint". The other four domains
keep one route with GET, POST and DELETE.

That opt-out is not a fix the other four missed — it gives up a feature
the plugin implements. ai-mcp-proxy handles non-POST itself: its handler
carries an explicit non-POST branch, MCP session handling, an SSE
subscription waiting on notifications/tools/list_changed, and Accept
header validation for application/json and text/event-stream. GET is the
server-to-client channel of MCP Streamable HTTP, and on the other four
domains it reaches the plugin and works.

Nothing in git or in any comment records why catalog gave it up, and the
plugin and binding behind it were applied by hand three days after the
route landed — they were never in git at all. So on a cluster rebuilt
from git the split had no guard behind it: GET fell through to a route
with no plugin whose service points back at the gateway itself.

Drop the stream route and put GET back on the MCP route, so the five
domains are identical again.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 16:50:52 +02:00
Stéphane Tailland a8459f7ecf Fix invalid K8s resource names for read_only/product_manager consumer groups
metadata.name must be an RFC 1123 subdomain (no underscores); spec.name
(the value actually matched against the JWT role claim) stays read_only/
product_manager to match the realm role names exactly.
2026-08-21 18:39:30 +02:00
Stéphane Tailland fc6b60c582 Rebuild catalog-mcp consumer groups on the real 5-role model
KongConsumerGroups were still named sttlab-user/sttlab-admin, a leftover
from before the classic-models realm's role model existed. Real users'
JWTs carry realm roles (admin, read_only, product_manager, customer,
support) via consumer_groups_claim, so no group ever matched and every
tool call was rejected with 403 "consumer groups were not found".

Groups now match the realm roles directly; tool ACLs gate on each role's
catalog:read/catalog:write grant from security-model.yaml.
2026-08-21 18:35:02 +02:00
Stéphane Tailland 95f9b611b3 Capture request/response bodies for audit-log via pre/post-function
Body capture lives in dedicated audit-log-body-capture(-bindings).yaml
files, separate from audit-log.yaml itself, so they can be dropped
without touching the base logging plugin (degrades gracefully to
metadata-only via nil custom_fields). Response body captured in
body_filter (chunk-accumulated) rather than the log phase, which
cannot call kong.response.get_raw_body() directly.
2026-08-21 14:53:52 +02:00
Stéphane Tailland 1c46f9b447 Revert custom_fields_by_lua on audit-log: broke logging entirely
kong.request.get_raw_body()/kong.response.get_raw_body() cannot be
called from the log phase (confirmed via dataplane error logs:
"function cannot be called in log phase") — this silently killed
log_by_lua for every plugin on the request, not just body capture.
Back to metadata-only logging while body capture gets redone properly
(pre-function/post-function storing into kong.ctx.shared, read back
in custom_fields_by_lua).
2026-08-21 14:46:49 +02:00
Stéphane Tailland 6fa122207e Capture request/response bodies in audit-log, extend it to MCP
custom_fields_by_lua adds request_body/response_body to the http-log
payload. Bodies and the raw Authorization header are now both logged
in clear text — dev only, flagged in the plugin file itself.
2026-08-21 14:40:20 +02:00
Stéphane Tailland fa7abfe5bd Drop the manual KongCertificate/KongSNI workaround
Turned out unnecessary: the real blocker was the missing
konghq.com/secret=true label on the Vault-sourced Secret, not a gap
in the Gateway API TLS translation itself. Once that label was added
(k8s-platform), the operator's own auto-generated KongCertificate/SNI
started working and this manual one just collided with it.
2026-08-21 09:51:23 +02:00
Stéphane Tailland 01f873b773 Bind the Gateway TLS secret to Konnect via KongCertificate/KongSNI
Gateway API's listeners[].tls.certificateRefs never gets synced to
Konnect by the gateway operator in this setup — same working pattern
as every other resource here (KongService, KongRoute, ...): a
Kong-native CRD, not the Gateway API's own translation.
2026-08-21 09:47:29 +02:00
Stéphane Tailland 466c657240 Point catalog-api at the classic-models Keycloak realm, drop separate MCP hostname
MCP now shares the REST hostname (routed by path), matching the
/catalog/v1 rework — no second TLS listener/certificate needed.
2026-08-21 09:24:18 +02:00
Stéphane Tailland 4a1c772b80 Expose catalog-api under /catalog/v1, decoupled from the backend path
Public routes no longer leak classic-models-api's own /api/v1 prefix.
request-transformer-advanced rewrites to the real backend path per route.
2026-08-21 09:22:33 +02:00
Stéphane Tailland f929a0f7e4 MCP server 2026-08-20 16:26:48 +02:00
Stéphane Tailland 8b7ab3b846 Additional plugins 2026-08-20 15:03:25 +02:00
Stéphane Tailland 9546953b97 Flux refactoring 2026-08-20 14:37:08 +02:00
Stéphane Tailland 89add4914a First commit 2026-08-20 13:24:10 +02:00
Stéphane Tailland 1275748655 Scaffold catalog-api (routing placeholder for classic-models-api split) 2026-08-18 14:59:46 +02:00