本エントリはKong Advent Calendar 2025の12/10の投稿です。
はじめに
Kong Gatewayのリリースの度に様々なプラグインが追加されていますが、2025年10月に登場したKong Gateway 3.12には少し特殊なAI MCP Proxyプラグインが発表されました。このプラグインは一般的なREST APIへのリクエストではなく、MCPによるリクエストに対応する為のプラグインです。このプラグインについてはこちらのエントリで紹介しています。
本エントリでは、AI MCP Proxyプラグイン(以降MCP Proxy)を使ったサンプルを通して、このプラグインの利用方法やアプローチについて紹介します。
尚、本エントリ、ならびに続くMCP Proxyのサンプル環境はGitHubのこちらのレポジトリに用意しいますので、実機で試して頂くことも可能です。しかしながら、本エントリの目的は、読み物としてMCP Proxyがどう言うものかを理解してもらう事を目的としています。なのでなるべく詳しく説明する形で書きます。
デモ環境を実際にお試しになるには、環境にはDocker Compose、Kongの設定にはdecKが必要となります。
MCP ProxyのプラグインはKong Gatewayのエンタープライズ機能の一つである為、利用にはライセンスが必要です。Kong Konnectを利用すると1ヶ月間お試しいただけます。
デモ環境 - Account APIとTransaction API
このデモでは、Account APIとTransaction APIという2つのサービスから特定の処理のみ選択/統合して1つのMCPサーバを構築します。複数のAPIをコンそりする目的である為、OpenWeather APIのデモのケースとは異なり、複数のKong AI MCP Proxyプラグインを組み合わせて利用します。具体的には、各REST APIとのマッピング/MCP定義の用にそれぞれconversion-onlyモードを1つずつ、MCPサーバとしてのエンドポイントをKong上に公開する為のlistenerモードを1つ、計3つのMCP Proxyを組み合わせた構成となります。
デモ環境にはデモスクリプトと、APIアクセスに必要なInsomnia Collectionを用意していますので参照してください。
AccountサービスとTransactionサービスにアクセス
今回のデモ環境では、既にAccountサービスとTransactionサービスがそれぞれコンテナとして動いています。Accountサービスは8081、Transactionサービスは8082からアクセス出来ます。これらサービスへのアクセスは、conversion-transactions.yamlに定義しているので
deck gateway sync conversion-transactions.yaml
の適用によりKong Gateway経由でのアクセスが可能となります。
提供しているInsomnia Collectionには、これらサービスに対するリクエストを含んでいます。

Accountsで始まるリクエストはAccountサービス用、Transactionsで始まるリクエストはTransactionサービス用です。TransactionサービスはAccountサービスに依存しており、「誰に対する」という対象としてAccountサービスで再版されるアカウントIDを併せて定義する必要があります。Transactionサービスで登録したトランザクションはAccountにおける残高に反映されます。一方、Accountサービスでもトランザクションを管理する事が可能(Debit/Creditで指定)ですが、あくまでAccountサービス内の機能である為Credit/Debitを利用した場合Transactionサービスのレコードとして登録されません。
今回のサンプルでは、意図的にAccountサービスのCredit/DebitはMCPにはToolとして公開せず、かつAccountとTransactionの2つのサービスに対して1つのMCPサーバを提供するアプローチを取ります。
Kongの設定
Kongの設定はconversion-transactions-mcp.yamlに纏めています。
先ほど同様、decKコマンドの実行でプラグインが適用されます。
deck gateway sync conversion-transactions-mcp.yaml
MCPサーバとToolの集約
エージェントから見てMCPサーバは1つ。そのMCPサーバには4つのToolが登録されていますが、Toolは2つのサービスに跨って定義されています。
MCPはBFFとは異なりますが、特定のエージェントに対して幾つかのサービスへのアクセスを一元的に提供出来るメリットが大きいケースも多くあります。特に、現時点ではToolの数が多いとLLMでのTool利用の判断精度が下がるといったリサーチや、実際にLLM側でTool参照数のリミットが設けられている事もあり、ある程度エージェントに参照させるToolの制御をMCPサーバ側で担う必然性はあります。
MCP Proxyの定義
複数のAPIに対して適切な粒度でTool化するにはどうするか?まずはMCP Proxyの設定内容をより細かく説明します。上記Account/Transactionサービスアクセス用のyamlとの差分であるプラグイン定義のみ抽出してみます。
plugins:
...
- name: ai-mcp-proxy
route: accounts-mcp
config:
mode: listener
server:
tag: accounts-mcp
logging:
log_payloads: false
log_statistics: true
- name: ai-mcp-proxy
tags:
- accounts-mcp
route: accounts
config:
mode: conversion-only
tools:
- annotations:
title: Create an account
description: Create an account with specified initial values.
method: POST
request_body:
content:
application/json:
schema:
type: object
properties:
type:
description: Type of the account. It could be either savings or checking.
type: string
enum:
- savings
- checking
initial_balance:
description: Initial amount in US dollars. Only the natural number is allowed.
type: integer
format: int64
required: [type, initial_balance]
- annotations:
title: List all accounts
description: Get a list of accounts with id, account type, and balance.
method: GET
- name: ai-mcp-proxy
route: transactions
tags:
- accounts-mcp
config:
mode: conversion-only
tools:
- annotations:
title: Create a transaction
description: Create a new credit transaction for a given Account ID.
method: POST
path: /transactions/{accountId}
parameters:
- name: accountId
in: path
description: Account ID for the transactions.
schema:
type: string
required: [accountId]
request_body:
content:
application/json:
schema:
type: object
properties:
amount:
description: The amount to be credited.
type: integer
format: int64
description:
description: Description of this credit transaction.
type: string
transaction_type:
description: Type of the transaction. It could be either credit or debit.
type: string
enum:
- credit
- debit
required: [amount, description, transaction_type]
- annotations:
title: List all transactions
description: Get a list of transaction with id, date, amount, description, and transaction type for a given account.
method: GET
path: /transactions/{accountId}
parameters:
- name: accountId
in: path
description: Account ID for the transactions.
schema:
type: string
required: [accountId]
listener
listnerの役割はMCPサーバのエンドポイントの定義と、UpstreamにあたるREST APIとの紐付けにありますが、定義は至ってシンプルです。
plugins:
...
- name: ai-mcp-proxy
route: accounts-mcp
config:
mode: listener
server:
tag: accounts-mcp
logging:
log_payloads: false
log_statistics: true
...
listnerは特定のRouteと紐付ける事により、そのRouteをMCPサーバとします。具体的には、ここではroute: accounts-mcpと直接Routeを指定しています。実際には他のプラグイン同様このRouteに対してMCP Proxyを適用しているだけではあるのですが、効果としてそのRouteがMCPサーバとなります。
次にconfig.server.tagとして、このlistenerにタグを指定しています。listner自体の定義にはどのREST APIと繋がっているかの定義はありません。逆に、REST APIとの変換処理において、このタグを参照する事によりlistenerと紐付けます。
conversion-only
文字通り、REST APIとMCPのプロトコル変換のみを行うMCP Proxyです。今回のサンプルでは2つ定義されていますが、1つだけ取り出して見てみます。
plugins:
...
- name: ai-mcp-proxy
+ tags:
+ - accounts-mcp
route: accounts
config:
mode: conversion-only
tools:
- annotations:
title: Create an account
description: Create an account with specified initial values.
method: POST
request_body:
content:
application/json:
schema:
type: object
properties:
type:
description: Type of the account. It could be either savings or checking.
type: string
enum:
- savings
- checking
initial_balance:
description: Initial amount in US dollars. Only the natural number is allowed.
type: integer
format: int64
required: [type, initial_balance]
- annotations:
title: List all accounts
description: Get a list of accounts with id, account type, and balance.
method: GET
...
conversion-onlyモードのMCP Proxyの定義は、conversion-listenerモードを利用したサンプルと非常に似ています。ここでの定義におけるconfig.routeはUpstreamであるREST APIを指しており、これがlistenerの時と異なります。
違いはtagsにて、listenerのタグを指定している点です。(Diffとして表示)conversion-onlyの場合、REST APIとMCPの変換は出来ますが、MCPサーバとして機能している訳ではないのでlistnerとの紐付けが必要です。ここでは前述のlistnerで定義したconfig.server.tagと同じタグを付加する事によりlistenerを指定しています。
もう一点、conversion-onlyでのタグ指定はtagsとなっており、複数のlistnerと紐づける事が可能です。これにより、REST/MCPの変換は1箇所に定義した上、異なるMCPサーバに紐付けてToolとして後悔する事も可能です。
MCP Inspectorの起動
では実際にMCPサーバにアクセスしてみます。エージェントとMCPサーバの通信ステップを把握する為、本サンプルではMPC Inspectorを使ってそのステップを確認します。MCP Inspectorを起動すると
npx @modelcontextprotocol/inspector
MCP Inspectorがブラウザに立ち上がります。上記のアドレスを指定の上、ConnectとするとMCPサーバとの対話が開始されます。

Tools Discovery (tools/list)
エージェントはまず、接続したMCPサーバから利用可能なToolの仕様を取得します。
{
"method": "tools/list",
"params": {}
}
MCPサーバー側(つまりMCP Proxy)からのレスポンスは以下です。
{
"tools": [
{
"name": "create-a-transaction",
"description": "Create a new credit transaction for a given Account ID.",
"inputSchema": {
"type": "object",
"properties": {
"path_accountId": {
"description": "Account ID for the transactions.",
"type": "string"
},
"body": {
"additionalProperties": false,
"properties": {
"amount": {
"description": "The amount to be credited.",
"type": "integer",
"format": "int64"
},
"description": {
"type": "string",
"description": "Description of this credit transaction."
},
"transaction_type": {
"enum": [
"credit",
"debit"
],
"type": "string",
"description": "Type of the transaction. It could be either credit or debit."
}
},
"type": "object",
"required": [
"amount",
"description",
"transaction_type"
]
}
},
"required": [
"body",
"path_accountId"
],
"additionalProperties": false
},
"annotations": {
"title": "Create a transaction"
},
"id": "19ef3dc2-98cd-4393-9334-a628ae978461"
},
{
"name": "create-an-account",
"description": "Create an account with specified initial values.",
"inputSchema": {
"type": "object",
"properties": {
"body": {
"additionalProperties": false,
"properties": {
"type": {
"enum": [
"savings",
"checking"
],
"type": "string",
"description": "Type of the account. It could be either savings or checking."
},
"initial_balance": {
"description": "Initial amount in US dollars. Only the natural number is allowed.",
"type": "integer",
"format": "int64"
}
},
"type": "object",
"required": [
"type",
"initial_balance"
]
}
},
"required": [
"body"
],
"additionalProperties": false
},
"annotations": {
"title": "Create an account"
},
"id": "59bc9181-736c-47a9-a1d0-73a56456363e"
},
{
"name": "list-all-accounts",
"description": "Get a list of accounts with id, account type, and balance.",
"inputSchema": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"annotations": {
"title": "List all accounts"
},
"id": "59bc9181-736c-47a9-a1d0-73a56456363e"
},
{
"name": "list-all-transactions",
"description": "Get a list of transaction with id, date, amount, description, and transaction type for a given account.",
"inputSchema": {
"type": "object",
"properties": {
"path_accountId": {
"description": "Account ID for the transactions.",
"type": "string"
}
},
"required": [
"path_accountId"
],
"additionalProperties": false
},
"annotations": {
"title": "List all transactions"
},
"id": "19ef3dc2-98cd-4393-9334-a628ae978461"
}
]
}
ここでは2つのMCP Proxy(つまり2つのサービス)に跨って定義した4つのToolが一覧として返されます。エージェント観点では、これらToolがどの様な構成になっているかを意識せず、Toolを1つのグループとして扱う事が出来ます。
Tool Execution (tools/call)
エージェントは利用できるToolを特定した後、定義されたパラメータを指定して実行します。ここではlist-transactionsを選択し、Transactionに必要なアカウントIDを指定の上リストを取得します。
Request
{
"method": "tools/call",
"params": {
"name": "list-all-transactions",
"arguments": {
"path_accountId": "9233a8e8-d18b-4ff1-be1c-62d850f99cd4"
},
"_meta": {
"progressToken": 1
}
}
}
Response
{
"content": [
{
"type": "text",
"text": "[{\"transaction_id\":\"tx-d03682aa33\",\"date\":\"2025-11-24T12:36:31.208434Z\",\"amount\":5000.0,\"description\":\"Salary Payment\",\"transaction_type\":\"credit\"},{\"transaction_id\":\"tx-f6767e2100\",\"date\":\"2025-11-24T12:36:48.520480Z\",\"amount\":12000.0,\"description\":\"Annual Bonus Payment\",\"transaction_type\":\"credit\"},{\"transaction_id\":\"tx-144f9123e7\",\"date\":\"2025-11-24T12:37:30.653307Z\",\"amount\":-300.0,\"description\":\"Purchase - Amazon\",\"transaction_type\":\"debit\"},{\"transaction_id\":\"tx-9748b56bee\",\"date\":\"2025-11-24T12:37:45.110398Z\",\"amount\":-800.0,\"description\":\"Monthly Tuition\",\"transaction_type\":\"debit\"}]"
}
],
"isError": false
}
レスポンスのJSONの中身は以下となります。
[
{
"transaction_id": "tx-d03682aa33",
"date": "2025-11-24T12:36:31.208434Z",
"amount": 5000,
"description": "Salary Payment",
"transaction_type": "credit"
},
{
"transaction_id": "tx-f6767e2100",
"date": "2025-11-24T12:36:48.520480Z",
"amount": 12000,
"description": "Annual Bonus Payment",
"transaction_type": "credit"
},
{
"transaction_id": "tx-144f9123e7",
"date": "2025-11-24T12:37:30.653307Z",
"amount": -300,
"description": "Purchase - Amazon",
"transaction_type": "debit"
},
{
"transaction_id": "tx-9748b56bee",
"date": "2025-11-24T12:37:45.110398Z",
"amount": -800,
"description": "Monthly Tuition",
"transaction_type": "debit"
}
]
おわりに
本エントリでは、MCP Proxyを利用して複数REST APIを束ねたMCPサーバを構築しました。listnerモードとconversion-onlyモードで指定した複数のMCP Proxyをタグで紐付けることにより、MCPサーバとUpstreamのREST APIを多対多の関係性で柔軟に組み合わせる事が可能となります。
次回は、実際にVolcanoSDKを利用したAIエージェントと、AI Proxy Advanced、MCP Proxyを組み合わせた実践的なサンプルをご紹介します。

