2026年3月13日金曜日

MCP Appとして日報アプリを作成する

これまでに作成した環境の上で、新規にMCP Appとして簡単な日報アプリを作ってみます。

できるだけ作業手順が簡単になるようにAPEXアプリを作成しました。GitHubのリポジトリmcp-appapp/mcp-app-helper.sqlとしてエクスポートが置いていあります。

作成した日報アプリをMCP Inspectorで実行しています。実行環境はAlways FreeのAutonomous AI Databaseです。Microsoft Entra IDでユーザー認証しています。アプリの開発にAIを使うため、開発は壊れてもよいローカルのOracle AI Database 26ai Freeのコンテナ環境で実施しています。


Claude DesktopからSQLcl MCPサーバーでデータベースに接続して、Claude Sonnet 4.6でMCPのツール本体となるPL/SQLファンクションとMCP AppのUIリソースを生成しています。Skills無しで作成しているので少し手間がかかりました。おそらくClaude Codeを使ってSkillsをきちんと用意すると、もっとアプリの完成度が上がるでしょう。

以下より作業を紹介します。UC_AIのインストールおよびmcp-appがインストールされていることを前提とします。

最初に日報を保存する表DAILY_REPORTSを作成します。これもClaude Sonnet 4.6に指示を出して作成してもらいました。

続いて、APEXアプリmcp-app-helperをインストールします。

インストール手順は通常のAPEXアプリケーションと同じです。


インストールされたら、アプリケーションの編集を開きます。


アプリケーションの名前アプリケーションの別名を変更します。アプリケーションの別名はMCP AppにアクセスするURLに現れるORDS RESTモジュール名になります。

例えばAPEXワークスペースの名前がAPEXDEVのときは、MCP AppのURLは以下になります。

http[s]://ホスト/ords/apexdev/アプリケーション別名(小文字)/mcp


アプリケーションを実行します。

ナビゲーション・メニューからHandlersを開きます。

ORDS RESTモジュールとテンプレートmcpを作成し、POSTおよびDELETEハンドラを実装します。

作成をクリックします。


英小文字のアプリケーション別名がデフォルトのモジュール名です。変更不要(変更すると動かなくなります)なので、そのまま作成します。


シャトル・アイテムでモジュールの保護を設定できます。保護するモジュールを右に移動し、Submitします。


モジュールの保護には、ロールORDSUsersおよび権限oracle.example.mcpを使用します。これらはあらかじめ作成しておく必要があります。

置換文字列G_ROLE_NAMEおよびG_PRIVILEGE_NAMEの設定を変更することにより、異なるロールと権限も使用できます。


ナビゲーション・メニューのResourcesを開きます。

作成をクリックします。


UIリソースとして以下を設定します。URLにはAPEXアプリのページにアクセスするURLの指定を想定していますが(こちらの記事を参照のこと)、今回はAIに生成させたので指定していません。

Uri: ui://daily-report/mcp-app.html
Name: Daily Report
Description: 個人の日報を報告するウィジェット。
Mime Type: text/html;profile=mcp-app (デフォルトから変更不要)

DescriptionはAIが参照するので、丁寧に書いたほうが生成されるUIリソースの精度が上がると思われます。


UIリソースのエントリが登録されました。


ナビゲーション・メニューからToolsを開きます。

ツールとしてview_daily_reportおよびupdate_daily_reportを作成します。

作成をクリックします。


作成ツールの情報として以下を設定します。AIへの指示に、この定義を参照するように指示を出すので、DescriptionInput SchemaOutput Schemaの設定は重要です。

TagsにRESTモジュール名(小文字のアプリケーション別名)を含めると、このMCP Appのtools/listのレスポンスに作成したツールが現れます。ツールとして使用するファンクションは、引数としてCLOBを受け取りCLOBを返すようにします。そのため、Function Callの記述は常にreturn ファンクション名(:parameters);にします。

Code: view_daily_report
Description: 指定された担当者の日報を表示します。
Function Call: return view_daily_report(:parameters);
Tags: dailyreport
Resource Id: Daily Report

Input SchemaおよびOutput Schemaの作成に、ベルギーのUnited Codes社が提供しているサービスJSON Schema Builderを使用しています。

Input Schema:
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "title": "view_daily_report",
  "properties": {
    "employeeName": {
      "type": "string",
      "description": "The employee who wrote the daily report."
    },
    "reportDate": {
      "type": "string",
      "description": "Target date of the daily report."
    }
  },
  "required": [
    "reportDate"
  ]
}
Output Schema:
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "title": "view_daily_report",
  "properties": {
    "employeeName": {
      "type": "string",
      "description": "The employee who wrote the daily report."
    },
    "reportDate": {
      "type": "string",
      "description": "Target date of the daily report."
    },
    "summary": {
      "type": "string",
      "description": "Summary of the day"
    },
    "tomorrowPlan": {
      "type": "string",
      "description": "Tomorrow Plan"
    }
  }
}

以下のプロンプトでPL/SQLファンクションを作成しています。

「ビューOJ_MCP_UC_AI_TOOLSで定義されているview_daily_reportの本体となるPL/SQLのファンクションを生成してください。入力はINPUT_SCHEMAで定義されているJSONのCLOB、戻り値はOUTPUT_SCHEMAで定義されているCLOBの形式になります。 PL/SQLファンクションの操作対象は表DAILY_REPORTSです。」

SQLclのMCPサーバーを使用すると、ファンクションの作成に必要な情報をデータベースから引き出してくれます。


AIが生成したコードはmcp-app/daily-reports以下に置いてあります。

同様の手順でツールupdate_daily_reportを作成します。

Code: update_daily_report
Description: 指定された担当者の日報を作成または更新します。
Function Call: return update_daily_report(:parameters);
Tags: dailyreport

Input Schema:
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "title": "update_daily_report",
  "description": "Update Daily Report",
  "properties": {
    "employeeName": {
      "type": "string",
      "description": "The employee who wrote the daily report."
    },
    "reportDate": {
      "type": "string",
      "description": "Target date of the daily report."
    },
    "summary": {
      "type": "string",
      "description": "Summary of the day"
    },
    "tomorrowPlan": {
      "type": "string",
      "description": "Tomorrow Plan"
    }
  },
  "required": [
    "reportDate"
  ]
}
Output Schema:
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "title": "update_daily_report",
  "description": "Update Daily Report",
  "properties": {
    "employeeName": {
      "type": "string",
      "description": "The employee who wrote the daily report."
    },
    "reportDate": {
      "type": "string",
      "description": "Target date of the daily report."
    },
    "summary": {
      "type": "string",
      "description": "Summary of the day"
    },
    "tomorrowPlan": {
      "type": "string",
      "description": "Tomorrow Plan"
    }
  }
}

ツールのDescription、入力および出力パラメータのJSONスキーマ定義、操作の対象となる表のメタデータを参照できるためか、あまりやり取りせずに意図したファンクションが作成されました。修正が必要な部分がないとは言えないですが、Skillsの定義などで対応可能だと思います。


以上でツールが登録できました。

この状態でUIリソースを、以下のプロンプトで作成してもらいました。

「このツールをMCP App(SEP-1865)として表示するHTML/JavaScript/CSSのバンドルを生成して、OJ_MCP_UI_RESOURCESのTEXTを更新して。」

さすがに適当すぎて、何回もやり取りが必要でした。ただ、最終的に動くUIリソースがあったので、それを参照するように指示をしたところ、動きました。

「oj_mcp_ui_resourcesに含まれるget_current_userは、ボタンのクリックが動作します。」

おそらく、これもSkillsを用意していればもっと精度は上がったと予想されます。

とはいえ、以上の作業で動作するMCP Appが作成できました。

これまでの作業で作成したファンクションなどをAutonomous AI Databaseにコピーして、ADBでの動作を確認しています。UIリソースについてはコピーの手間を少なくするため、APEXアプリにBundleページを作成しています。


今回の記事は以上になります。