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?

Oracle Autonomous AI Database の MCPサーバを Claude Desktop につないで「いま重いSQL」を聞けるようにしてみた

0
Last updated at Posted at 2026-04-26

1. はじめに

前回、Oracle Autonomous AI Database (以降 ADB) と統合された MCP (Model Context Protocol) サーバの基本的な使い方を VSCode + Cline で試しました。

ただ、前回はサンプルとして「SCOTT スキーマのテーブル一覧を返すツール」を動かすところまでで、実用イメージには踏み込めていませんでした。今回はもう一歩進めて、DBA が日常的に欲しい「いま重い SQL の上位 N 件」を自然言語で聞けるようにする という、より実務寄りのユースケースを組み立てます。クライアントも前回の VSCode + Cline ではなく Claude Desktop に切り替えてみます。

公式 Docs にも Claude Desktop 接続のサンプルが掲載されています (MCPサーバーの使用 | Oracle Docs)。本記事ではこのサンプルを Windows 環境で動かし、ADB 上に作った独自ツールを Claude Desktop から呼び出すところまで通します。

今回の検証ゴール

# 検証項目
1 専用 DB ユーザ (dba_copilot) で V$SQLAREA を読む TOP SQL ツールを ADB に作れる
2 Claude Desktop (Windows) から ADB MCP サーバに接続し、上記ツールが認識される
3 自然言語で意図通りに呼び出され、結果まで読み解いてくれる

結論先出し

  • ADB 側に PL/SQL Function 1 本 + DBMS_CLOUD_AI_AGENT.CREATE_TOOL だけで、Claude Desktop から自然言語で TOP SQL を引っ張れる仕組みが組めました。
  • TOP SQL を取るだけでなく「読み解きと次アクションの提案」までチャット内で一緒に返ってくる のが、本構成の一番の価値でした。
  • IDE を開いていない場面でも使えるため、DBA や運用担当に開放しやすい配布形態として広げやすい構成になりました。

2. 検証環境と全体像

項目 内容
マシン Windows 11 Pro / Node.js v24.12
MCP クライアント Claude Desktop (Windows 版)
DB Oracle Autonomous AI Database (Serverless) / ap-tokyo-1
DB ユーザ dba_copilot (本記事で新規作成・最小権限)

Claude Desktop は MCP サーバへ直接 HTTP で繋ぐのではなく、mcp-remote というローカル Node プロセスを stdio 経由で起動し、そこから HTTPS + OAuth で ADB の MCP エンドポイントに接続します。OAuth ログインは初回だけブラウザが立ち上がる仕組みです。

ADB 側で MCP サーバを使うには、OCI コンソールで対象 ADB にフリーフォームタグ adb$feature = {"name":"mcp_server","enable":true} を付けておきます (詳細は公式 Docs)。


3. ADB 側の準備

ADB 側でやることは「専用ユーザ作成」「Function 作成」「ツール登録」の 3 つです。

3.1. 専用ユーザの作成と GRANT

ADMIN ユーザで以下を実行します。前回は ADMIN 直接でツールを作りましたが、本番運用を前提にして専用ユーザに最小権限で持たせます。

-- ADMIN で実行
CREATE USER dba_copilot IDENTIFIED BY "<strong_password>";

GRANT CREATE SESSION, CREATE PROCEDURE TO dba_copilot;
GRANT EXECUTE ON DBMS_CLOUD_AI_AGENT   TO dba_copilot;
GRANT SELECT  ON SYS.V_$SQLAREA        TO dba_copilot;
ALTER USER dba_copilot QUOTA UNLIMITED ON DATA;

動的ビューは SELECT_CATALOG_ROLE でまとめてではなく、SYS.V_$SQLAREA のように 直接 GRANT します。PL/SQL の定義者権限ブロックではロール経由の権限が効かないためです。

3.2. TOP SQL を返す Function を作る

dba_copilot で Function を作成します。p_sort_by (ELAPSED / CPU / GETS / IO)、p_top_n (上限 100) を受け取り、JSON 文字列を返すシグネチャです。

Top SQL取得Function
CREATE OR REPLACE FUNCTION get_top_sql(
    p_sort_by IN VARCHAR2 DEFAULT 'ELAPSED',
    p_top_n   IN NUMBER   DEFAULT 10
) RETURN CLOB
AS
    v_sort_by VARCHAR2(30);
    v_top_n   NUMBER;
    v_data    CLOB;  v_row CLOB;  v_count NUMBER;  v_result CLOB;
BEGIN
    -- 入力バリデーション (LLM 由来の不正値もここで吸収)
    v_sort_by := UPPER(NVL(p_sort_by, 'ELAPSED'));
    IF v_sort_by NOT IN ('ELAPSED','CPU','GETS','IO') THEN v_sort_by := 'ELAPSED'; END IF;
    v_top_n := NVL(p_top_n, 10);
    IF v_top_n <= 0 THEN v_top_n := 10; ELSIF v_top_n > 100 THEN v_top_n := 100; END IF;

    DBMS_LOB.CREATETEMPORARY(v_data, TRUE);
    DBMS_LOB.APPEND(v_data, TO_CLOB('['));
    FOR rec IN (
        SELECT sql_id,
               SUBSTR(sql_text, 1, 200) AS sql_text_preview,
               executions,
               ROUND(elapsed_time / GREATEST(executions,1) / 1e6, 3) AS elapsed_per_exec_sec,
               ROUND(cpu_time     / GREATEST(executions,1) / 1e6, 3) AS cpu_per_exec_sec,
               ROUND(buffer_gets  / GREATEST(executions,1))          AS gets_per_exec,
               ROUND(disk_reads   / GREATEST(executions,1))          AS reads_per_exec,
               ROW_NUMBER() OVER (ORDER BY
                   CASE v_sort_by WHEN 'ELAPSED' THEN elapsed_time
                                  WHEN 'CPU'     THEN cpu_time
                                  WHEN 'GETS'    THEN buffer_gets
                                  WHEN 'IO'      THEN disk_reads END DESC NULLS LAST) AS rn
          FROM v$sqlarea
         ORDER BY CASE v_sort_by WHEN 'ELAPSED' THEN elapsed_time
                                 WHEN 'CPU'     THEN cpu_time
                                 WHEN 'GETS'    THEN buffer_gets
                                 WHEN 'IO'      THEN disk_reads END DESC NULLS LAST
         FETCH FIRST v_top_n ROWS ONLY
    ) LOOP
        IF rec.rn > 1 THEN DBMS_LOB.APPEND(v_data, TO_CLOB(',')); END IF;
        SELECT JSON_OBJECT('sql_id' VALUE rec.sql_id,
                           'sql_text_preview' VALUE rec.sql_text_preview,
                           'executions' VALUE rec.executions,
                           'elapsed_time_per_exec_sec' VALUE rec.elapsed_per_exec_sec,
                           'cpu_time_per_exec_sec'    VALUE rec.cpu_per_exec_sec,
                           'buffer_gets_per_exec'     VALUE rec.gets_per_exec,
                           'disk_reads_per_exec'      VALUE rec.reads_per_exec
                           RETURNING CLOB) INTO v_row FROM dual;
        DBMS_LOB.APPEND(v_data, v_row);
        v_count := rec.rn;
    END LOOP;
    DBMS_LOB.APPEND(v_data, TO_CLOB(']'));

    -- ラッパー JSON は手組み
    v_result := '{"status":"' || CASE WHEN NVL(v_count,0)=0 THEN 'no_data' ELSE 'success' END
             || '","row_count":' || NVL(v_count,0)
             || ',"sort_by":"'   || v_sort_by
             || '","data":';
    DBMS_LOB.APPEND(v_result, v_data);
    DBMS_LOB.APPEND(v_result, TO_CLOB('}'));
    RETURN v_result;
EXCEPTION
    WHEN OTHERS THEN
        RETURN '{"status":"error","error_msg":"' || SUBSTR(SQLERRM,1,300) || '"}';
END;
/

ポイントは 3 つだけです。

  • 戻り値は CLOB の JSON 文字列: LLM が読み取って自然言語で要約しやすい構造 (status / row_count / sort_by / data の固定フォーマット) にする
  • 入力バリデーションを内部で吸収: LLM が組み立てる引数値はブレるので、不正値は ELAPSED / 上限 100 にフォールバック
  • EXCEPTION WHEN OTHERS でも JSON を返す: ツール戻り値を担う Function は例外を投げず、エラーも JSON に包んで LLM に解釈させる

3.3. MCP ツールとして登録する

DBMS_CLOUD_AI_AGENT.CREATE_TOOL で、いま作った Function を MCP ツールとして登録します。

BEGIN
    DBMS_CLOUD_AI_AGENT.CREATE_TOOL(
        tool_name => 'GET_TOP_SQL_TOOL',
        attributes => '{
            "function": "GET_TOP_SQL",
            "instruction": "Returns the top heaviest SQL statements currently in the shared pool (V$SQLAREA), ranked by the chosen metric. Returns JSON with status, row_count, sort_by, and data array.",
            "tool_inputs": [
                {"name": "P_SORT_BY", "description": "Sort metric. One of: ELAPSED (default), CPU, GETS, IO."},
                {"name": "P_TOP_N",   "description": "Number of top SQL statements to return. Default 10, max 100."}
            ]
        }'
    );
END;
/

attributes のキーは function / instruction / tool_inputs の 3 つ (公式 Docs の LIST_SCHEMAS サンプルと同じ構造)。これで ADB 側の準備は完了です。


4. Claude Desktop からの接続

公式 Docs の Claude Desktop 向けサンプル (MCPサーバーの使用) を Windows 向けに書き換えます。配置先は %APPDATA%\Claude\claude_desktop_config.json です。

{
  "mcpServers": {
    "adb-dba-copilot": {
      "command": "C:\\Program Files\\nodejs\\npx.cmd",
      "args": [
        "-y", "mcp-remote",
        "https://dataaccess.adb.<region-identifier>.oraclecloudapps.com/adb/mcp/v1/databases/<your-database-ocid>"
      ]
    }
  }
}

Windows 向けに書き換える際のポイントは 2 つです。

  • commandnpx ではなく npx.cmd のフルパス を指定する (Claude Desktop は GUI 起動コンテキストのため PATH 解決が IDE と同じには効かない)
  • 設定ファイルは BOM 無し UTF-8 で書き込む (BOM 付きだと Claude Desktop が「アプリ設定を読み込めませんでした」で起動失敗する)

Claude Desktop を起動すると、初回のみブラウザで OAuth ログイン画面が開きます。dba_copilot でログインすると、ツール一覧に GET_TOP_SQL_TOOL が表示されます。

04_claude_tool_permission.png


5. 実際に使ってみる

5.1. シナリオA: 「いま重い SQL 上位 3 件教えて」

05_scenario_A_top3.png

Claude Desktop が自動で GET_TOP_SQL_TOOL を選び、P_TOP_N=3 を組み立てて呼び出してくれました。返ってきた JSON を表に整形した上で、

  • 3 件とも Buffer Gets ≒ Disk Reads (ヒット率ほぼゼロ) でディスクから毎回読み直している
  • Elapsed > CPU の差が大きく、I/O 待ちが支配的
  • 検索条件にインデックスが整備されていない可能性が高い

という考察と、「次に見るならこの SQL の実行計画」「APP_TEST_USERS のインデックス一覧確認」という次アクションの提案まで自動で出してくれました。ただ TOP SQL を引っ張ってくるだけでなく、その読み解きと次アクションまで一緒に出してくれる のが、Claude Desktop で扱う一番の旨味です。Function 単体では JSON が返るだけ、SQL Worksheet では数値の表が出るだけですが、Claude Desktop では「で、何をすればいい?」までが 1 メッセージで返ってきます。

5.2. シナリオB: 「CPU 使ってる SQL を 5 つ見せて」

ソート軸を変えて聞いてみます。

シナリオB: CPU で TOP5

「CPU を使ってる SQL」と日本語で書いただけで、Claude が P_SORT_BY=CPU / P_TOP_N=5 を引数として組み立てて呼び出しています。ELAPSED ソート (シナリオA) でも CPU ソート (シナリオB) でも 1 位は 9531gp733y3hg で同じですが、2 位以下の顔ぶれや、Elapsed と CPU のどちらで効いているかという見え方が変わってきます。観点を切り替えて見られるのが、この設計の利点です。


6. Claude Desktop ならではのうれしさ

前回の VSCode + Cline と比べて、Claude Desktop に切り替えたことで何が変わったかを 1 行ずつでまとめます。

観点 前回 (VSCode + Cline) 今回 (Claude Desktop)
起動 IDE を開いて Cline タブへ スタンドアロンアプリで起動が軽い
主な利用者層 開発者 (IDE 使い) DBA / 運用 / 企画も同じ UI で使える
インシデント時 開発機が無いとつらい 別 PC・出先でも入れれば即使える

特に 「IDE を開いていない場面でも DB に質問できる」 という点が大きな違いでした。前回はあくまで「VSCode 利用者向けの便利機能」感が抜けませんでしたが、Claude Desktop なら 開発者以外の DBA や運用担当にも配布対象を広げやすい と感じました。インシデント初動で「とりあえず Claude Desktop 開いて、いま重い SQL 教えて」と言える、というのが現実味のある運用イメージとして見えてきました。


7. このサンプルの今後の活用方法

GET_TOP_SQL_TOOL 自体は単機能ですが、土台としてはいろいろ伸ばせます。

(1) 同じ型でツールを増やす: get_top_sql の関数構造 (RETURN CLOB + 入力バリデ + JSON 組み立て + EXCEPTION で エラー JSON) はそのまま転用可能で、GET_WAIT_EVENTS_TOOL (待機イベント) / GET_SQL_PLAN_TOOL (実行計画) / GET_BLOCKING_SESSIONS_TOOL (ブロッキング) などを同じ型で並べていけます。これらが揃ってくると、Claude Desktop 上で 「TOP SQL → 実行計画 → 待機イベント」を 1 スレッドで深掘りする DBA Copilot 的な対話 が成立しそうです。

(2) 他 DB への横展開: 関数中身は Oracle 特有ですが、設計パターン (戻り値を JSON で返す → LLM が要約する → 同じ型で関数を増やす) は DB 種別を選びません。PostgreSQL なら pg_stat_statements、MySQL なら performance_schema を使って同等のツールに展開できそうです。

非 DBA への開放も現実的です。アプリ開発チームに「自分の SQL が TOP に居座っていないか」を、本人が Claude Desktop で気軽に確認できるようにする、という運用が回せます。


8. まとめ

# 検証項目 結論
1 専用ユーザで TOP SQL ツールを ADB に作れる 可能。dba_copilot に最小権限を直接 GRANT し、Function 1 本 + CREATE_TOOL 1 回で完了
2 Claude Desktop から接続して認識される 可能。mcp-remote 経由 + npx.cmd フルパス + BOM 無し UTF-8 で接続
3 自然言語で意図通りに呼び出され、結果も読み解いてくれる 可能。読み解きと次アクション提案まで 1 メッセージで返ってくる

前回の「動かしてみた」レベルから、自然言語で TOP SQL を聞いてその場で読み解きまで返ってくる という実用に近いところまで一歩進められました。同じ型でツールを増やしていけば、ADB MCP サーバを「DBA Copilot のバックエンド」として育てていけそうです。

GET_WAIT_EVENTS_TOOL / GET_SQL_PLAN_TOOL みたいなものを追加すると、TOP SQL → 待機イベント → 実行計画の流れを Claude Desktop 1 本で確認してくれそうですよね。

参考

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?