From a4967d13eed69fc01df1f7fceec32766c015cea1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Tailland?= Date: Sun, 23 Aug 2026 15:00:57 +0200 Subject: [PATCH] Addition of MCP server --- dev/flux-kustomization.yaml | 6 ++ kong/kustomization.yaml | 9 ++ kong/mcp/ai-mcp-proxy.yaml | 102 ++++++++++++++++++ kong/mcp/audit-log-body-capture-bindings.yaml | 35 ++++++ kong/mcp/audit-log-body-capture.yaml | 27 +++++ kong/mcp/audit-log.yaml | 17 +++ kong/mcp/consumergroups.yaml | 59 ++++++++++ kong/mcp/openid-connect.yaml | 27 +++++ kong/mcp/pluginbindings.yaml | 53 +++++++++ kong/mcp/routes.yaml | 42 ++++++++ kong/mcp/service.yaml | 17 +++ 11 files changed, 394 insertions(+) create mode 100644 kong/mcp/ai-mcp-proxy.yaml create mode 100644 kong/mcp/audit-log-body-capture-bindings.yaml create mode 100644 kong/mcp/audit-log-body-capture.yaml create mode 100644 kong/mcp/audit-log.yaml create mode 100644 kong/mcp/consumergroups.yaml create mode 100644 kong/mcp/openid-connect.yaml create mode 100644 kong/mcp/pluginbindings.yaml create mode 100644 kong/mcp/routes.yaml create mode 100644 kong/mcp/service.yaml diff --git a/dev/flux-kustomization.yaml b/dev/flux-kustomization.yaml index 7de0302..9eb2641 100644 --- a/dev/flux-kustomization.yaml +++ b/dev/flux-kustomization.yaml @@ -32,6 +32,12 @@ spec: AUDIT_LOG_ENDPOINT: http://audit-log-collector.observability.svc.cluster.local/logs REST_HOSTNAME: customers-dev.sttlab.local TOKEN_EXCHANGE_CLIENT_ID: kong-token-exchange-broker + # Unused today — same dead variable already present in catalog-api's + # flux-kustomization.yaml (kong/mcp/openid-connect.yaml uses + # API_AUDIENCE for its audience_required/token_exchange target, not + # this). Kept for parity/documentation of what the MCP layer's real + # token audience is meant to be. + MCP_AUDIENCE: '["mcp-customers"]' substituteFrom: - kind: Secret name: customers-token-exchange-broker diff --git a/kong/kustomization.yaml b/kong/kustomization.yaml index 2fead1b..afc03db 100644 --- a/kong/kustomization.yaml +++ b/kong/kustomization.yaml @@ -12,3 +12,12 @@ resources: - rest/audit-log-body-capture.yaml - rest/audit-log-body-capture-bindings.yaml - rest/pluginbindings.yaml + - mcp/service.yaml + - mcp/routes.yaml + - mcp/consumergroups.yaml + - mcp/ai-mcp-proxy.yaml + - mcp/openid-connect.yaml + - mcp/audit-log.yaml + - mcp/audit-log-body-capture.yaml + - mcp/audit-log-body-capture-bindings.yaml + - mcp/pluginbindings.yaml diff --git a/kong/mcp/ai-mcp-proxy.yaml b/kong/mcp/ai-mcp-proxy.yaml new file mode 100644 index 0000000..3a355dd --- /dev/null +++ b/kong/mcp/ai-mcp-proxy.yaml @@ -0,0 +1,102 @@ +apiVersion: configuration.konghq.com/v1 +kind: KongPlugin +metadata: + name: ${NAME}-customers-mcp-ai-mcp-proxy + namespace: ${NAMESPACE} +plugin: ai-mcp-proxy +config: + mode: conversion-listener + include_consumer_groups: true + tools: + - name: list-customers + description: Retrieves a paginated list of customers in the classic-models CRM. + annotations: + title: List all customers + read_only_hint: true + idempotent_hint: true + destructive_hint: false + open_world_hint: false + acl: + allow: + - admin + - read_only + - product_manager + - customer + - support + method: GET + scheme: http + host: ${REST_HOSTNAME} + path: /customers/v1/customers/ + - name: get-customer + description: Retrieves a single customer by customer number. + annotations: + title: Get a customer + read_only_hint: true + idempotent_hint: true + destructive_hint: false + open_world_hint: false + acl: + allow: + - admin + - read_only + - product_manager + - customer + - support + method: GET + scheme: http + host: ${REST_HOSTNAME} + path: /customers/v1/customers/{customernumber}/ + parameters: + - name: customernumber + in: path + required: true + description: The customer's unique number. + schema: {} + - name: create-customer + description: Creates a new customer in the CRM. + annotations: + title: Create a customer + read_only_hint: false + idempotent_hint: false + destructive_hint: false + open_world_hint: false + acl: + allow: + - admin + method: POST + scheme: http + host: ${REST_HOSTNAME} + path: /customers/v1/customers/ + request_body: + required: true + content: + application/json: + schema: + type: object + properties: + customername: + type: string + contactlastname: + type: string + contactfirstname: + type: string + phone: + type: string + addressline1: + type: string + city: + type: string + country: + type: string + salesrepemployeenumber: + type: integer + creditlimit: + type: string + required: + - customername + - contactlastname + - contactfirstname + - phone + - addressline1 + - city + - country diff --git a/kong/mcp/audit-log-body-capture-bindings.yaml b/kong/mcp/audit-log-body-capture-bindings.yaml new file mode 100644 index 0000000..2962683 --- /dev/null +++ b/kong/mcp/audit-log-body-capture-bindings.yaml @@ -0,0 +1,35 @@ +apiVersion: configuration.konghq.com/v1alpha1 +kind: KongPluginBinding +metadata: + name: ${NAME}-customers-mcp-audit-capture-request-binding + namespace: ${NAMESPACE} +spec: + controlPlaneRef: + type: konnectNamespacedRef + konnectNamespacedRef: + name: ${CONTROL_PLANE_REF_NAME} + pluginRef: + name: ${NAME}-customers-mcp-audit-capture-request + targets: + serviceRef: + group: configuration.konghq.com + kind: KongService + name: ${NAME}-customers-mcp +--- +apiVersion: configuration.konghq.com/v1alpha1 +kind: KongPluginBinding +metadata: + name: ${NAME}-customers-mcp-audit-capture-response-binding + namespace: ${NAMESPACE} +spec: + controlPlaneRef: + type: konnectNamespacedRef + konnectNamespacedRef: + name: ${CONTROL_PLANE_REF_NAME} + pluginRef: + name: ${NAME}-customers-mcp-audit-capture-response + targets: + serviceRef: + group: configuration.konghq.com + kind: KongService + name: ${NAME}-customers-mcp diff --git a/kong/mcp/audit-log-body-capture.yaml b/kong/mcp/audit-log-body-capture.yaml new file mode 100644 index 0000000..ca405d2 --- /dev/null +++ b/kong/mcp/audit-log-body-capture.yaml @@ -0,0 +1,27 @@ +# dev only: captures raw request/response bodies into kong.ctx.shared for +# audit-log.yaml's custom_fields_by_lua to read back in the log phase. +# Drop this file (and its bindings) to stop body capture without touching +# audit-log.yaml itself — it degrades gracefully to metadata-only logging. +apiVersion: configuration.konghq.com/v1 +kind: KongPlugin +metadata: + name: ${NAME}-customers-mcp-audit-capture-request + namespace: ${NAMESPACE} +plugin: pre-function +config: + access: + - "kong.ctx.shared.audit_request_body = kong.request.get_raw_body()" +--- +apiVersion: configuration.konghq.com/v1 +kind: KongPlugin +metadata: + name: ${NAME}-customers-mcp-audit-capture-response + namespace: ${NAMESPACE} +plugin: post-function +config: + body_filter: + - | + local chunk = ngx.arg[1] + if chunk then + kong.ctx.shared.audit_response_body = (kong.ctx.shared.audit_response_body or "") .. chunk + end diff --git a/kong/mcp/audit-log.yaml b/kong/mcp/audit-log.yaml new file mode 100644 index 0000000..1401c76 --- /dev/null +++ b/kong/mcp/audit-log.yaml @@ -0,0 +1,17 @@ +apiVersion: configuration.konghq.com/v1 +kind: KongPlugin +metadata: + name: ${NAME}-customers-mcp-audit-log + namespace: ${NAMESPACE} +plugin: http-log +# dev only: the raw Authorization (JWT) header is captured in clear text, +# not acceptable as-is for a non-dev environment. request_body/response_body +# below read from kong.ctx.shared, populated by audit-log-body-capture.yaml +# — nil (and silently omitted) if that file isn't applied. +config: + http_endpoint: ${AUDIT_LOG_ENDPOINT} + method: POST + content_type: application/json + custom_fields_by_lua: + request_body: "return kong.ctx.shared.audit_request_body" + response_body: "return kong.ctx.shared.audit_response_body" diff --git a/kong/mcp/consumergroups.yaml b/kong/mcp/consumergroups.yaml new file mode 100644 index 0000000..b18fe72 --- /dev/null +++ b/kong/mcp/consumergroups.yaml @@ -0,0 +1,59 @@ +apiVersion: configuration.konghq.com/v1beta1 +kind: KongConsumerGroup +metadata: + name: ${NAME}-admin + namespace: ${NAMESPACE} +spec: + controlPlaneRef: + type: konnectNamespacedRef + konnectNamespacedRef: + name: ${CONTROL_PLANE_REF_NAME} + name: admin +--- +apiVersion: configuration.konghq.com/v1beta1 +kind: KongConsumerGroup +metadata: + name: ${NAME}-read-only + namespace: ${NAMESPACE} +spec: + controlPlaneRef: + type: konnectNamespacedRef + konnectNamespacedRef: + name: ${CONTROL_PLANE_REF_NAME} + name: read_only +--- +apiVersion: configuration.konghq.com/v1beta1 +kind: KongConsumerGroup +metadata: + name: ${NAME}-product-manager + namespace: ${NAMESPACE} +spec: + controlPlaneRef: + type: konnectNamespacedRef + konnectNamespacedRef: + name: ${CONTROL_PLANE_REF_NAME} + name: product_manager +--- +apiVersion: configuration.konghq.com/v1beta1 +kind: KongConsumerGroup +metadata: + name: ${NAME}-customer + namespace: ${NAMESPACE} +spec: + controlPlaneRef: + type: konnectNamespacedRef + konnectNamespacedRef: + name: ${CONTROL_PLANE_REF_NAME} + name: customer +--- +apiVersion: configuration.konghq.com/v1beta1 +kind: KongConsumerGroup +metadata: + name: ${NAME}-support + namespace: ${NAMESPACE} +spec: + controlPlaneRef: + type: konnectNamespacedRef + konnectNamespacedRef: + name: ${CONTROL_PLANE_REF_NAME} + name: support diff --git a/kong/mcp/openid-connect.yaml b/kong/mcp/openid-connect.yaml new file mode 100644 index 0000000..552371b --- /dev/null +++ b/kong/mcp/openid-connect.yaml @@ -0,0 +1,27 @@ +apiVersion: configuration.konghq.com/v1 +kind: KongPlugin +metadata: + name: ${NAME}-customers-mcp-openid-connect + namespace: ${NAMESPACE} +plugin: openid-connect +config: + issuer: ${OIDC_ISSUER} + audience_required: ${API_AUDIENCE} + auth_methods: + - bearer + display_errors: false + ssl_verify: true + consumer_groups_claim: + - realm_access + - roles + client_id: + - ${TOKEN_EXCHANGE_CLIENT_ID} + client_secret: + - ${TOKEN_EXCHANGE_CLIENT_SECRET} + token_exchange: + subject_token_issuers: + - issuer: ${OIDC_ISSUER} + conditions: {} + request: + audience: ${API_AUDIENCE} + scopes: [] diff --git a/kong/mcp/pluginbindings.yaml b/kong/mcp/pluginbindings.yaml new file mode 100644 index 0000000..0fa96a4 --- /dev/null +++ b/kong/mcp/pluginbindings.yaml @@ -0,0 +1,53 @@ +apiVersion: configuration.konghq.com/v1alpha1 +kind: KongPluginBinding +metadata: + name: ${NAME}-customers-mcp-openid-connect-binding + namespace: ${NAMESPACE} +spec: + controlPlaneRef: + type: konnectNamespacedRef + konnectNamespacedRef: + name: ${CONTROL_PLANE_REF_NAME} + pluginRef: + name: ${NAME}-customers-mcp-openid-connect + targets: + serviceRef: + group: configuration.konghq.com + kind: KongService + name: ${NAME}-customers-mcp +--- +apiVersion: configuration.konghq.com/v1alpha1 +kind: KongPluginBinding +metadata: + name: ${NAME}-customers-mcp-ai-mcp-proxy-binding + namespace: ${NAMESPACE} +spec: + controlPlaneRef: + type: konnectNamespacedRef + konnectNamespacedRef: + name: ${CONTROL_PLANE_REF_NAME} + pluginRef: + name: ${NAME}-customers-mcp-ai-mcp-proxy + targets: + routeRef: + group: configuration.konghq.com + kind: KongRoute + name: ${NAME}-customers-mcp +--- +apiVersion: configuration.konghq.com/v1alpha1 +kind: KongPluginBinding +metadata: + name: ${NAME}-customers-mcp-audit-log-binding + namespace: ${NAMESPACE} +spec: + controlPlaneRef: + type: konnectNamespacedRef + konnectNamespacedRef: + name: ${CONTROL_PLANE_REF_NAME} + pluginRef: + name: ${NAME}-customers-mcp-audit-log + targets: + serviceRef: + group: configuration.konghq.com + kind: KongService + name: ${NAME}-customers-mcp diff --git a/kong/mcp/routes.yaml b/kong/mcp/routes.yaml new file mode 100644 index 0000000..edb900d --- /dev/null +++ b/kong/mcp/routes.yaml @@ -0,0 +1,42 @@ +apiVersion: configuration.konghq.com/v1alpha1 +kind: KongRoute +metadata: + name: ${NAME}-customers-mcp + namespace: ${NAMESPACE} +spec: + serviceRef: + type: namespacedRef + namespacedRef: + name: ${NAME}-customers-mcp + name: customers-mcp + hosts: + - ${REST_HOSTNAME} + methods: + - POST + - DELETE + paths: + - /customers-mcp + strip_path: false + tags: + - customers +--- +apiVersion: configuration.konghq.com/v1alpha1 +kind: KongRoute +metadata: + name: ${NAME}-customers-mcp-stream + namespace: ${NAMESPACE} +spec: + serviceRef: + type: namespacedRef + namespacedRef: + name: ${NAME}-customers-mcp + name: customers-mcp-stream + hosts: + - ${REST_HOSTNAME} + methods: + - GET + paths: + - /customers-mcp + strip_path: false + tags: + - customers diff --git a/kong/mcp/service.yaml b/kong/mcp/service.yaml new file mode 100644 index 0000000..5340cfe --- /dev/null +++ b/kong/mcp/service.yaml @@ -0,0 +1,17 @@ +apiVersion: configuration.konghq.com/v1alpha1 +kind: KongService +metadata: + name: ${NAME}-customers-mcp + namespace: ${NAMESPACE} +spec: + controlPlaneRef: + type: konnectNamespacedRef + konnectNamespacedRef: + name: ${CONTROL_PLANE_REF_NAME} + name: customers-mcp + host: ${REST_HOSTNAME} + port: 80 + protocol: http + path: / + tags: + - customers