0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

MCP Proxy Pluginで複数のAPIを1エンドポイントでMCP化する

0
Posted at

KongのMCP Proxy Pluginにはmodeがあり、conversion-listenerpassthrough-listenerconversion-onlylistener の4種類がある。
これらのうち前者2つは1MCPサーバにつき1エンドポイントを提供するような機能だが、後者2つは複数のMCPサーバを1エンドポイントで提供することが出来るような機能を持っている。
今回はこのconversion-onlylistenerの2つのモードについて、役割や用途を解説する。

conversion-onlylistener

listenerとついているものはMCP用のエンドポイントを公開する、という役割がある。
またconversionとついているものはREST APIをMCPツール定義に変換する、という役割がある。
なので、conversion-listenerは変換とエンドポイント公開の両方を行うスタンドアロン型のモードであるのに対し、conversion-onlyは変換だけを行い、エンドポイント公開はlistenerで行うのがconversion-onlylistenerconversion-listenerの違いである。
イメージ的には以下のような感じで、複数のAPIを1エンドポイントで捌くことが出来るようになる。MCP利用時のtools/listではconversion-onlyが生成したそれぞれのツール定義が見えるようになり、listenerはそれを集約してクライアントに提供する。

動作検証

ここでは天気API(https://wttr.in)とニュースAPI(https://jsonplaceholder.typicode.com  ※実際は適当なレスポンスを返すAPI)を例に、conversion-onlylistenerの組み合わせでMCPエンドポイントを公開する設定例を紹介する。

以下のdeck用のYAMLを適用して検証する。

deck.yaml (クリックして全表示)
_format_version: "3.0"

services:
  - name: mcp-listener-service
    protocol: https
    host: dummy.dummy
    routes:
      - name: mcp-listener-route
        paths:
          - /mcp
        plugins:
          - name: ai-mcp-proxy
            config:
              mode: listener
              server:
                tag: mcp-tools
  - name: weather-service
    protocol: https
    host: wttr.in
    port: 443
    routes:
      - name: weather-route
        paths:
          - /weather
        plugins:
          - name: ai-mcp-proxy
            tags:
              - mcp-tools
            config:
              mode: conversion-only
              tools:
                - name: get-weather
                  description: Get current weather for a specified city in JSON format.
                  method: GET
                  path: /weather/{city}
                  annotations:
                    title: Get Weather
                    read_only_hint: true
                    idempotent_hint: true
                  parameters:
                    - name: city
                      in: path
                      required: true
                      description: The city name (e.g. Tokyo, London, New York).
                      schema:
                        type: string
                    - name: format
                      in: query
                      description: Response format. Use j1 for JSON.
                      schema:
                        type: string
                        default: j1

  - name: news-service
    protocol: https
    host: jsonplaceholder.typicode.com
    port: 443
    routes:
      - name: news-route
        paths:
          - /news
        plugins:
          - name: ai-mcp-proxy
            tags:
              - mcp-tools
            config:
              mode: conversion-only
              tools:
                - name: get-articles
                  description: Get a list of news articles. Optionally filter by userId.
                  method: GET
                  path: /news/posts
                  annotations:
                    title: Get Articles
                    read_only_hint: true
                    idempotent_hint: true
                  parameters:
                    - name: userId
                      in: query
                      description: Filter articles by author ID.
                      schema:
                        type: integer
                - name: get-article-by-id
                  description: Get a single news article by its ID.
                  method: GET
                  path: /news/posts/{id}
                  annotations:
                    title: Get Article by ID
                    read_only_hint: true
                    idempotent_hint: true
                  parameters:
                    - name: id
                      in: path
                      required: true
                      description: The article ID.
                      schema:
                        type: integer

記載内容を解説する。
まず、以下はlistenerモードのプラグイン設定である。

services:
  - name: mcp-listener-service
    protocol: https
    host: dummy.dummy
    routes:
      - name: mcp-listener-route
        paths:
          - /mcp
        plugins:
          - name: ai-mcp-proxy
            config:
              mode: listener
              server:
                tag: mcp-tools

/mcpに対してMCPのリクエストを受け付けられるようにするための設定である。
これ自体はServiceを利用せず、conversion-onlyモードのプラグインで定義したServiceを叩くため、ホストに関してはダミーの値を設定している。
重要なのはconfig.server.tagの値で、これと同じタグを持つconversion-onlyモードのプラグインが生成したツール定義を集約して提供する、という役割がある。
この例ではmcp-toolsというタグを設定しているので、同じタグを持つconversion-onlyモードのプラグインが生成したツール定義がこのlistenerに集約されることになる。

以降はconversion-onlyモードのプラグイン設定となる。
最初は天気API(https://wttr.in)の設定である。

  - name: weather-service
    protocol: https
    host: wttr.in
    port: 443
    routes:
      - name: weather-route
        paths:
          - /weather
        plugins:
          - name: ai-mcp-proxy
            tags:
              - mcp-tools
            config:
              mode: conversion-only
              tools:
                - name: get-weather
                  description: Get current weather for a specified city in JSON format.
                  method: GET
                  path: /weather/{city}
                  annotations:
                    title: Get Weather
                    read_only_hint: true
                    idempotent_hint: true
                  parameters:
                    - name: city
                      in: path
                      required: true
                      description: The city name (e.g. Tokyo, London, New York).
                      schema:
                        type: string
                    - name: format
                      in: query
                      description: Response format. Use j1 for JSON.
                      schema:
                        type: string
                        default: j1

ポイントは、タグにmcp-toolsを設定している点で、タグの付け方は他のエンティティとかと同じで特別な付け方がある訳ではないので、通常のタグと同様に複数設定することも可能である。
このタグがlistenerconfig.server.tagと一致することで、このツール定義がlistenerに集約されることになる。
あとは、通常のREST APIをMCPツール定義に変換するための設定を行う。
この例では、/weather/{city}というエンドポイントを呼び出すためのツール定義を作成している。
同様に、ニュースAPI(https://jsonplaceholder.typicode.com)の設定も行う。
設定は天気APIとほぼ一緒なので説明は割愛する。

動作確認する。
まず、各APIがAPIとして動作するか確認する。天気APIは/weather/{city}、ニュースAPIは/news/postsに対してリクエストを投げてみる。
最初に天気APIを呼び出す。以下のように、/weather/Tokyoにクエリformat=j1を付与してリクエストを投げると、東京の天気情報がJSON形式で返ってくる。

curl -s localhost:8000/weather/Tokyo?format=j1 
{
  "current_condition": [
    {
      "FeelsLikeC": "16",
      "FeelsLikeF": "61",
 ...

次にニュースAPIを呼び出す。以下のように、/news/postsにリクエストを投げると、ニュースではないけどJSONであるダミーデータが返ってくる。

curl -s localhost:8000/news/posts
[
  {
    "userId": 1,
    "id": 1,
    "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
  ...

APIとしては問題なそうだ。
今度はこれをMCPクライアントから呼び出してみる。
今回はMCP Inspectorを利用して/mcpにに接続し、ツール定義が正しく集約されているか、またツール呼び出しが正しく動作するかを確認する。
接続してtools/listを取得したのが以下の結果である。
image.png
ニュースAPIの方で定義したarticle関連のtoolや天気APIのget-weatherが取れている事が分かる。

以下、get-weatherを実行した結果となる。
image.png
またget-articlesを実行した結果はこちら。
image.png
2種類のツールを1つのエンドポイントに集約できていることが確認できた。

まとめ

conversion-listenerでAPIをMCP化すると、特にAPIが多い場合はAgentは複数のMCPサーバに接続する必要が出てくるが、conversion-only + listener なら、Agentは1つのエンドポイントだけ知っていれば済む。
また、conversion-only + listener は、チームごとに自分のAPIだけ管理できるという運用上のメリットがあり、例えば今回の例で言うと

  • 天気チーム → weather-service の conversion-only だけ管理
  • ニュースチーム → news-service の conversion-only だけ管理
  • プラットフォームチーム → listener だけ管理(集約・ACL・ポリシー適用)

みたいな運用の棲み分けが可能で、各チームは他チームの設定を知らなくてよく、mcp-tools タグを付けるだけで自動的に集約することができる。
使い方次第ではかなり使える機能だと思うので、APIのMCP化の際には利用を検討するといいと思う。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?