Oracle Forms について
Oracle Forms は、ディレクトリ内の Forms モジュールを変換・索引化し、検索可能な情報として提供する MCP サーバーです。レガシーアプリの調査、レビュー、文書化、モダナイズを行うチーム向けで、注釈や相互参照も保持します。
主な機能
- 01モジュールの走査とページ表示
- 02構造化インデックスへの変換
- 03ブロック、トリガー、プログラムユニットの照会
- 04PL/SQL と XML ソースの検索
- 05要素への注釈と相互参照の保存
活用シーン
- 01レガシーアプリのモジュール構成の把握
- 02トリガーやプログラムユニットの PL/SQL の確認
- 03モジュール横断でのプロシージャ呼び出し検索
- 04フォーム要素に関する保守知識の記録
MCP 対応
stdio と HTTP 経由で MCP クライアントにツールとリソースを公開し、モジュール索引、ソース断片、XML を提供します。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
このサーバーのパッケージやリモートエンドポイントはまだ取得できていません。プロジェクトの README の手順に従ってください。
ツール
ローカルサーバーのツールはマシン上で起動して初めて取得できます。メンテナーはまだツール一覧を提供していません。
詳細
翻訳後に原文が更新されています。最新の内容は原文をご確認ください。
Oracle Forms MCP
ディレクトリ内にある Oracle Forms モジュール(.fmb フォーム、.mmb メニュー、.pll PL/SQL ライブラリ、.olb オブジェクトライブラリ)の内容を提供するMCP サーバーです。AI アシスタントは、Forms Builder を開かずにブロック、アイテム、トリガー、プログラムユニット、生のオブジェクト XML を調査できます。
Kotlin Multiplatform コア(純粋な @Serializable モデルとポート)を基盤とし、その上に JVM MCP サーバーを構築しています。単一の FormsService を介した宣言型ツールアダプター、stdio および HTTP トランスポート、フィンガープリントベースのディスクキャッシュを備えています。Oracle のツール変換によってストリーミング StAX パーサーにデータが渡され、Forms XML が構造化インデックスに変換されます。
理由
1990~2000 年代に作られた Oracle Forms アプリケーションは、今も重要なビジネスプロセスを支えています。しかし、そのロジックは Forms Builder でしか開けないバイナリの .fmb/.pll モジュール内に閉じ込められています。そのため、最新の AI ツールからは内容が分かりにくく、レビュー、文書化、移行も困難です。
Oracle Forms MCP は、これらのモジュールを構造化された検索可能なコンテンツに変換し、AI アシスタントが次のことを行えるようにします。
- レガシーアプリを理解する — Forms IDE を使わずに、ブロック、アイテム、トリガー、プログラムユニットを列挙できます。
- PL/SQL をレビューして文書化する — デコードしたトリガーやプログラムユニットの本文を、モデルのコンテキストに直接取り込めます。
- モダナイゼーションを支援する — 数十年前のビジネスロジックをアシスタントに渡して、APEX や Java への移行、または書き換えに活用できます。また、すべてのモジュールのソースを横断検索できます。
- 知識を記録して保持する — アシスタントが個々の要素に関するメモ、タグ、相互参照を記録できます。これらはセッションや再インデックス作成後も保持され、誰も全容を覚えていないフォームの、長く使えるマップを作り上げます。
Oracle Forms のモダナイゼーション、リバースエンジニアリング、コードレビュー、文書化に取り組む開発者やチームを対象としています。手作業でフォームを開くより速く、Forms のロジックを読みたいすべての方に役立ちます。
動作を見る
同梱の sample-forms ディレクトリを使った一般的なセッションの例:
You: What does ORDERS.fmb do?
AI → list_modules → ORDERS.fmb (NOT_CACHED), MAINMENU.mmb, UTILS.pll …
AI → fetch_module ORDERS.fmb → converted + indexed (3 blocks, 3 triggers, 3 program units)
AI → get_module_overview ORDERS → blocks, triggers, LOVs, record groups, windows, canvases …
You: Show me the validation logic on the ORDERS block.
AI → list_triggers block=ORDERS → WHEN-VALIDATE-ITEM (on ORDER_ID), WHEN-VALIDATE-RECORD
AI → get_trigger ORDERS WHEN-VALIDATE-ITEM → the decoded PL/SQL body
You: Where else is the CALC_TOTAL procedure called?
AI → search_source "calc_total" scope=plsql → hits across triggers and program units
You: That validation is the legacy pre-2010 path — note it so we remember.
AI → annotate_element ORDERS trigger WHEN-VALIDATE-ITEM kind=note "Legacy pre-2010 validation path" → saved
(next session) get_trigger ORDERS WHEN-VALIDATE-ITEM → body + the stored note inline
仕組み
list_modulesは設定された--forms-dir(非再帰)をスキャンし、各モジュールのキャッシュ状態を報告します:NOT_CACHED、CACHED、STALE(ディスク上でソースが変更されています)、またはSOURCE_MISSING。 本番環境の Forms ディレクトリには数千のモジュールが格納されているため、結果はフィルタリングされ、ページ分割されます。pattern/type/statusで絞り込み、残りはnextCursorを使って取得し、該当する全体の内訳はcountsByStatusで確認してください。fetch_moduleはモジュールのテキスト形式をキャッシュ内に生成し、インデックスを作成します:ORACLE_HOMEが設定されている場合 — バイナリファイルは%ORACLE_HOME%\binにある Oracle ツールで変換されます:.fmb/.mmb/.olbにはfrmf2xml(XML)、.pllにはfrmcmp_batch(Module_Type=LIBRARY Script=YES。.pld形式のテキストダンプ)を使用します。ORACLE_HOMEが設定されていない場合 — 変換済みファイルがモジュールと同じ場所にあることを想定し(orders_fmb.xml、dupes_fmb.xml、picker_fmb.xml、toolbar_fmb.xml、mainmenu_mmb.xml、objects_olb.xml、utils.pld)、キャッシュにコピーします。
- 1 回の StAX パスで XML を解析し、構造化インデックス(アイテムを含むブロック、デコード済み PL/SQL を含むトリガー、プログラムユニット、LOV、レコードグループ、ウィンドウ、キャンバスなど)を作成します。PL/SQL 本文は
.sqlサイドカーファイルに抽出されます。名前付きの XML 要素にはすべて行範囲参照が付与されるため、get_object_xmlで変換済みファイルから該当部分を切り出せます。 - その他のツールはキャッシュ済みのインデックスを読み取ります。キャッシュはフィンガープリント(ソースファイルのサイズ + mtime + sha256)に基づいています。モジュールを編集すると
STALEとマークされ、読み取りツールは再取得を求めます。 annotate_elementとrelate_elementsを使うと、アシスタントは個々の要素に関する永続的なメタ情報(メモ、タグ、要約、分類、相互参照)を書き込めます。これは派生インデックスとは別のストアに保存されるため、再取得後も保持され、読み取りツールでインライン表示されます。ソース変更前に作成された注釈にはフラグが付けられますが、削除されることはありません。
ツール
| ツール | 返される内容 |
|---|---|
list_modules | forms ディレクトリ内のモジュールを、タイプ、サイズ、キャッシュ状態とともに一覧表示します。pattern/regex、type、status で絞り込み、limit/cursor でページを指定できます |
fetch_module | 1 つのモジュールを変換してインデックス化します(冪等。進行状況の通知あり) |
get_module_overview | すべてのセクション名と件数を返します。fetch 後の最初の呼び出しでは、verbosity=detailed を指定するとウィンドウとキャンバスのオブジェクトも追加されます |
list_blocks | ベーステーブル、項目数、トリガー数とともにブロックを一覧表示します |
get_block | 1 つのブロックの項目(タイプ、プロパティクラス、プロンプトとトリガーの名前を含む)と、そのマスター・ディテール関係を返します。verbosity=detailed を指定すると、データ型、列、キャンバス、サイズ、表示/必須/LOV、および各項目のプロパティクラスを通じて解決された DML プロパティが追加されます。columns=true を指定すると、ベーステーブルの列と、どの項目にも設定されていない列が追加されます |
list_triggers | レベル/スコープとともにトリガーを一覧表示します。ブロック、項目、レベルで絞り込めます(verbosity=detailed を指定すると PL/SQL のプレビューが追加されます) |
get_trigger | 1 つのトリガーのデコード済み PL/SQL 本文を返します(同名のトリガーは ownerPath または level で区別します)。resolve はサブクラス化ポインターをたどって、キャッシュ済みの親モジュールを参照します |
list_program_units | 行数とともに、プロシージャ、関数、パッケージ仕様部/本体を一覧表示します |
get_program_unit | 1 つのプログラムユニットの PL/SQL を返します(仕様部/本体は unitType で区別します)。resolve の動作は get_trigger と同じです |
search_source | フェッチ済みの 1 つのモジュール内を行単位で検索します。抽出済みの PL/SQL(plsql)、生の XML(xml)、またはその両方を検索できます。offset/nextOffset でページを指定できます |
search_modules | キャッシュ済みのすべてのモジュールを横断して同じ検索を行います(フォーム間呼び出し、共有 :GLOBAL、サブクラス化ポインターなど)。到達できなかったモジュールを報告し、不透明な cursor でページを指定します |
read_source | キャッシュ済みファイルの行範囲を返します。モジュール名を含む uri、または module と file を指定します。変換済み XML または抽出済み PL/SQL サイドカーファイルを読み取れます |
get_object_xml | 名前を指定した任意のオブジェクトの生の XML フラグメントを返します。最後の手段として使用できます |
各ツールは、実行前に引数を自身の入力スキーマと照合します。存在しない引数が黙って無視されることはありません。呼び出しは、すべての問題をまとめて示して失敗します(最も可能性の高い引数名を示す不明な引数、指定可能な値を示す不足している引数、列挙型に含まれない値)。ツールに呼び出し例がある場合は、併せて表示されます。
オブジェクトの意味が存在する場所
Forms のプロパティのうち、モジュール内のほかのどの要素よりも多くの意味を持つものが 2 つありますが、以前はどちらも一度に 1 回の get_object_xml 呼び出しでしか取得できませんでした。
アイテムのプロパティクラスは、通常、その役割を示します。Forms ではオブジェクトの機能に基づいてクラス名を付けるため、プッシュボタンが LOV ボタンであり、テキストアイテムが filter フィールドであるのは、継承するクラスによるものです。get_block は各アイテムとブロックの propertyClass を報告し、そのモジュールで実際に宣言されているクラスであることを確認しています。
ウィンドウのモダリティは、フォームの読み取り方を決めます。モーダルウィンドウはダイアログなので、入力するコードと結果を使用するコードは、1 回のインタラクションの両側に位置します。get_module_overview(verbosity: "detailed") は、名前リスト 2 つの背後にあるウィンドウオブジェクトとキャンバスオブジェクトを返します。そこには、モダリティ、サイズ、ツールバーキャンバス、および各キャンバスが配置されているウィンドウが含まれます。
Forms が書き込んでいないプロパティは null として返されます。これは Forms のデフォルトが維持されていることを意味し、false ではありません。プロパティクラスがあるアイテムでは、それだけでもデフォルトを意味するわけではありません。クラスが値を提供するためです。実際のフォームでは、アイテムの振る舞いの大半はそこに定義されています。そこで get_block(verbosity: "detailed") は両方を返します。items[].dml はアイテム自体が書き込んだ内容で、effectiveDml はクラスを適用した同じプロパティです(アイテム、次にクラス、そのクラスの基となるもの、さらに取得済みのほかのモジュールへとたどります)。このチェーンの解決が最後まで完了した場合に限り、アイテムは effectiveDml に記載されます。そのため、ここでの null は本当に Forms のデフォルトを意味します。それ以外のアイテムはすべて unresolvedItems に行として記載され、理由と、取得によって解決できる場合は取得するモジュールも示されます。propertyClasses と結果の hint にも、クラスごとの同じ説明が含まれ、fetch_module 呼び出しが指定されています。items: [...] を指定すると、質問の対象となるアイテムに絞り込めます。
get_block(columns: true) は、ブロックのデータソース列、どのアイテムからも提供されない列、およびデータベースで必須となる列(トリガーによる割り当てがないと挿入に失敗する列)を追加します。
ブロックのマスター詳細関係は、ブロックがどのような経路でクエリ可能かを決めるため、get_block はすべての詳細度でこれらを提供します。block.relations はそのブロックがマスターとなる関係(結合条件、deferred、autoQuery、deleteRecord、preventMasterlessOperations)で、detailOf はそのブロックを詳細として指定する関係と、それぞれが記述されているマスターを示します。preventMasterlessOperations が設定された詳細ブロックは、マスターを介さずにクエリできません。この制約はデータベースではなくフォームが適用するため、移植先で独自に適用する必要があります。
結果をたどる
インデックス化されたものはすべて、キャッシュが管理するファイル内の行範囲です。以前は、その範囲にファイルのパスが記載されていても、解決する方法がありませんでした。ファイルを参照する各結果に、source が含まれるようになりました。
"source": {
"uri": "oracleforms://ORDERS.fmb/plsql/triggers/ORDERS.ORDER_ID.WHEN-VALIDATE-ITEM.sql",
"file": "plsql/triggers/ORDERS.ORDER_ID.WHEN-VALIDATE-ITEM.sql",
"startLine": 1, "endLine": 3
}
read_source はどちらの形式も受け付けるため、検索結果を前後の文脈とともに読んだり、切り詰められた断片の続きを読んだりできます。同じファイルは MCP リソースとしても読み取れます。変換後のテキスト形式には oracleforms://{module}/converted、抽出された 1 つの PL/SQL ブロックには oracleforms://{module}/plsql/{category}/{name} を使います。また、これらの各結果には resource_link コンテンツブロックも含まれるため、クライアントは JSON を解析せずにそのリソースへアクセスできます。読み取りには行数と文字数の上限があり、途中で切られたリソースの読み取りでは、返されるテキスト内にその旨と続きを取得する呼び出しが示されます。
パスは引き続きキャッシュからの相対パスであり、URI は配置に依存しません。絶対ホストパスは、コンテナイメージや HTTP 経由で通信するクライアントには意味を持たないためです。
サブクラス化(継承)されたオブジェクト
Forms では、ブロック、アイテム、トリガー、またはプログラムユニットを別のモジュールからサブクラス化するか、オブジェクトグループ(通常は .olb から)を使ってコピーできます。どちらの場合も、子オブジェクトにはオーバーライドした内容だけが保存されるため、コードが親にある場合、子オブジェクト自身のファイルにある PL/SQL 本体は実際に空です。これをそのまま提供すると、単に不完全というだけでなく、誤った情報になります。空の本体は「このボタンは何もしない」と受け取られてしまいます。
そのため、PL/SQL を含むすべての結果には bodySource も含まれます。
bodySource | 意味 |
|---|---|
own | 本体はこのモジュールで定義されています(通常のケース) |
inherited | オブジェクトはサブクラス化されており、ここでの text は空です。コードの所在は inherited に示されます |
resolved | この呼び出しでは親の本体が表示されています(出所は resolvedFrom に示されます) |
empty | オブジェクトに本体が実際にありません |
inherited の参照は、親の用語とツールで使われる形式で示されるため、そのまま呼び出せます。このフォームの BAR_LIST.SELECT は、親側では name: SELECT、ownerPath: BAR となります。
{
"name": "WHEN-BUTTON-PRESSED", "block": "BAR_LIST", "item": "SELECT",
"text": "", "bodySource": "inherited",
"inherited": { "module": "TOOLBAR", "file": "toolbar.fmb", "name": "SELECT", "ownerPath": "BAR" },
"hint": "… Call fetch_module(module=\"TOOLBAR.fmb\"), then get_trigger(module=\"TOOLBAR.fmb\", name=\"WHEN-BUTTON-PRESSED\", ownerPath=\"BAR.SELECT\")."
}
オブジェクトグループのメンバーの場合、name はオブジェクト独自の名前で、objectGroup はそのオブジェクトを含むグループを示します。ライブラリファイルでは、そのグループを含むモジュールが記録されていないため、ownerPath は示されません。同じモジュール内の親はプロパティクラスです。定義ではなくプロパティを提供し、何も隠さないため、意図的に継承として報告されません。
get_trigger と get_program_unit では、resolve: true を指定すると、1 回の呼び出しでポインターをたどります。読み取るのはすでにキャッシュされているモジュールだけです。未キャッシュのモジュールに到達するには変換が必要になり、読み取り専用ツールではそれを行えないため、処理に失敗するのではなく、ポインターと fetch_module のヒントを返します。get_block もサブクラス化されたブロックとアイテムに同様のフラグを付けます。また、get_object_xml は、要求された階層までポインターを解決して返します(Forms は外側の所有者にある ParentModule 属性をそのまま書き込むため、断片だけでは判断できません)。
モジュール間のトレース
あるフォームについての質問が、そのフォーム内だけで完結することはほとんどありません。モーダルウィンドウを開くのはそのウィンドウを呼び出す側であり、受け渡される値は :GLOBAL を通じて移動し、ツールバーはまったく別の 3 つ目のフォームで定義されています。そのため、指定したモジュールを検索する search_source では、この 3 つのどれにも答えられません。
search_modules "PICKER" → which forms call it
search_modules ":GLOBAL.picked_ref" → every module that writes or reads it
search_modules 'ParentFilename="toolbar.fmb"' scope=xml → every module that subclasses it
各ヒットには、モジュール(他のツールで使われる NAME.ext 形式の文字列である moduleSpec)、キャッシュからの相対ファイルパス、行番号、抜粋、およびその箇所を開く uri が含まれます。Forms のコードベースでは、同じモジュール名が PICKER、picker、Call_Form('picker') のように記述されるため、既定では大文字と小文字を区別せずに照合します。正確な一致が必要な場合は ignoreCase: false を渡してください。
検索対象は取得済みのモジュールだけです。未取得のモジュールに到達するには変換が必要になり、読み取り専用ツールではそれを行えないためです。そのため、検索範囲は前提ではなく、結果の一部として示されます。
{
"query": ":GLOBAL.picked_ref",
"cachedModules": 12, "scannedModules": 12, "skippedNotCached": 3090, "skippedStale": 1,
"hint": "3090 matching module(s) are not cached and were not searched — list_modules(status=\"not_cached\") names them, and fetch_module adds one to the search. 1 cached module(s) changed on disk …"
}
ディレクトリ全体を検索可能にするには、セッション前にキャッシュを準備します。fetch_module を 1 つずつ実行する代わりに、--prefetch を使うと、サーバーがバッチジョブとして一度起動し、一致するすべてのモジュールを変換してインデックスを作成します。標準エラー出力にはモジュールごとに 1 行ずつ出力され、その後終了します(失敗したモジュールがある場合は 1 で終了します)。すでに最新のモジュールではフィンガープリントの確認だけで済むため、再実行すると中断した箇所から再開できます。
server --forms-dir /srv/forms --prefetch-all
server --forms-dir /srv/forms --prefetch ORDER --prefetch-type form
どちらの上限も適用され、再開できます。ページあたりのヒット数は maxResults で制限され、1 回の呼び出しで読み取るモジュール数にも上限があります。この上限がなければ、何も一致しないクエリでも、回答する前にキャッシュ内の変換済みファイルをすべて読み取ることになります。いずれかの上限に達すると truncated が設定され、同じ引数とともに渡す nextCursor が返されます。カーソルはその引数に紐付けられているため、別の検索を意図せず続行することはありません。
アノテーション
アシスタントが要素から読み取るのではなく、要素について記録するメタ情報です。
意味に関するメモ、タグ、分類、相互参照の関係が含まれます。永続ストアに保存され、派生インデックスとは別に管理されます(プロトコルの _meta フィールドには保存されません)。そのため、fetch_module による再インデックス後も保持され、後続のセッションに返されます。各エントリーには作成者が記録され、モジュールの現在のソースより古い場合は staleAgainstSource としてフラグ付けされるため、メモが黙って削除されることはありません。上記の読み取りツール(get_module_overview、get_block、get_trigger、get_program_unit、get_object_xml)では、要素のアノテーションがインラインで表示されます。
| ツール | 機能 |
|---|---|
annotate_element | 1 つの要素について、メモ/要約/タグ/分類を保存します |
relate_elements | 2 つの要素間の有向相互参照を記録します(例:トリガーがプログラムユニットを calls する) |
get_element_annotations | 1 つの要素について保存されたメモと関係を取得します |
search_annotations | テキスト、種類、タグを指定して、モジュールに保存されたメモ/タグ/関係を検索します |
remove_annotation | ID を指定して、保存されたアノテーションまたは関係を削除します |
さらに、キャッシュされたモジュールごとのリソース(oracleforms://ORDERS.fmb/index)、oracleforms://{module}/index および oracleforms://{module}/annotations のリソーステンプレート、そして explain_module プロンプトもあります。
クイックスタート
クライアントに合った方法を選んでください。Claude Code にはプラグイン、Claude Desktop には.mcpb バンドル、それ以外にはDocker イメージ、またはソースからのビルドを利用できます。サーバーは公式 MCP Registry に io.github.aoreshkov/oracle-forms-mcp として公開されています。
Claude Code(ワンコマンドプラグイン)
/plugin marketplace add aoreshkov/oracle-forms-mcp
/plugin install oracle-forms@oracle-forms-mcp
Claude Code では Forms ディレクトリの入力が求められます。初回使用時に、リリース済みサーバーがプラグインのデータディレクトリに取得され、チェックサムが検証されます。クローン、ビルド、JSON の編集は不要です。/reload-plugins を実行するとツールを使用できます。また、プラグインの trace-form スキルでは、上記のトレース方法について、質問する順序や誤解を招く読み取り結果を説明するため、トレースを grep から始めずに済みます。
PATHに JDK 25 以降が必要です(javac -version)。プラグインのブートストラップでは、Java の単一ファイルソースモードを使用するためです。JRE のみをお使いの場合は、以下の.mcpbバンドルまたはclaude mcp addを利用してください。
詳細、設定オプション、代替手段については、プラグインの README をご覧ください。
ソースからビルド
ビルドには JDK 25 以降が必要です(Gradle が未インストールの場合は自動的に用意します)。実行には JRE 25 以降が必要です。ビルドしてインストールします。
gradlew :server:installDist
Claude Code に登録します(stdio)。
claude mcp add oracle-forms -- server/build/install/server/bin/server --forms-dir C:\path\to\forms
Claude Desktop(ワンクリックバンドル)
最新リリースからoracle-forms-mcp-<version>.mcpbをダウンロードして開きます。Claude
Desktopはコネクターとしてインストールし、標準のフォルダーピッカーでフォームディレクトリの指定を求めます
。JSONの編集も、ローカルでのビルドも不要です。
PATH上にJRE 25+が必要です。 バンドルに含まれているのはサーバーであり、Javaランタイムではありません。 MCPBのマニフェスト形式で宣言できるランタイムはNodeと Pythonのみであるため、インストール時に確認できません。コネクターが起動しない場合は、ターミナルでjava -versionが動作することを確認してください。
マシンにOracle Formsのインストール(ORACLE_HOME)がない場合は、Dockerと同じコピー
モードの制約が適用されます。Oracle Formsのインストールがあれば、.fmb/.pllをライブ変換できます。
その他のMCPクライアント(Cursor、VS Code、バンドルなしのClaude Desktop)— Dockerイメージを使用
公開済みイメージは、ローカルでのビルドなしでサーバーをstdio経由で実行します。ボリュームマウントをフォーム
ディレクトリに指定してください(コピー モードの場合、変換済みの*_fmb.xml/*.pldファイルをモジュールと同じ
場所に置く必要があります。Dockerを参照)。
Claude Desktop(claude_desktop_config.json)とCursor(~/.cursor/mcp.json)では、同じ形式を使用します。
{
"mcpServers": {
"oracle-forms": {
"command": "docker",
"args": ["run", "-i", "--rm",
"-v", "ofmcp-cache:/home/mcp/.cache", "-v", "/path/to/forms:/forms",
"ghcr.io/aoreshkov/oracle-forms-mcp", "--forms-dir", "/forms"]
}
}
}
VS Code(.vscode/mcp.json)では、代わりにserversキーを使用します。
{
"servers": {
"oracle-forms": {
"command": "docker",
"args": ["run", "-i", "--rm",
"-v", "ofmcp-cache:/home/mcp/.cache", "-v", "${workspaceFolder}/forms:/forms",
"ghcr.io/aoreshkov/oracle-forms-mcp", "--forms-dir", "/forms"]
}
}
}
名前付きボリュームofmcp-cacheは、解析済みモジュールのキャッシュと、さらに重要な、永続的な注釈ストアを
コンテナーの再起動後も保持します。--rmで削除されるのはコンテナーであり、名前付きボリュームではありません。
このボリュームを削除すると、アシスタントが記録したメモ/タグ/関係は次回の実行時に引き継がれません。
バインドマウントの方法と一度だけ実行するchownについては、Dockerを参照してください。
ネイティブランチャーを使う場合は、"command": "docker", "args": [...]を
"command": "/abs/path/to/server/build/install/server/bin/server", "args": ["--forms-dir", "/abs/path/to/forms"]に置き換えます。
同梱のフィクスチャを使えば、Oracleをインストールせずに試せます。
server --forms-dir sample-forms
HTTPトランスポート:
server --forms-dir C:\forms --transport http --port 3000 # endpoint: http://127.0.0.1:3000/mcp
HTTPトランスポートは127.0.0.1のみで待ち受けます。独自の認証機能はないため、バインドアドレスによって、コンバーターを実行するfetch_moduleや注釈の書き込みを含むすべてのツールを呼び出せるユーザーが決まります。--host(またはOFMCP_HTTP_HOST)を指定すると、別のアドレスにバインドできます。そのアドレスがループバック以外の場合、サーバーは起動時に警告をログに記録します。Host/Originチェックは、DNSリバインディングを介してブラウザーからサーバーを利用することを防ぎます。ただし、ネットワーク上のクライアントがHost: 127.0.0.1を自ら送信することは防ぎません。
Docker(copy-mode のみ)
コンテナイメージは ghcr.io/aoreshkov/oracle-forms-mcp で公開されています。Oracle の frmf2xml /
frmcmp_batch バイナリはプロプライエタリであり、イメージには同梱されていないため、イメージは
copy-mode でのみ動作します。マウントするモジュールの隣に、変換済みのテキスト形式
(*_fmb.xml/*_mmb.xml/*_olb.xml/*.pld)がすでに置かれている必要があります。.fmb/.pll を
その場で変換する場合は、Oracle Forms がインストールされたホスト(ORACLE_HOME が設定済み)で
サーバーを実行してください。
docker run -i -v /path/to/forms:/forms ghcr.io/aoreshkov/oracle-forms-mcp --forms-dir /forms
キャッシュと注釈の永続化。 ボリュームがない場合、キャッシュと永続注釈ストアはコンテナの書き込み可能レイヤーに
保存され、コンテナの終了時に破棄されます。実行間で保持するには、/home/mcp/.cache にボリュームをマウントしてください。
docker run -i -v ofmcp-cache:/home/mcp/.cache -v /path/to/forms:/forms \
ghcr.io/aoreshkov/oracle-forms-mcp --forms-dir /forms
名前付きボリュームまたは匿名ボリュームは、イメージの非 root 所有権(uid 10001)を引き継ぐため、そのまま動作します。
ホストの バインドマウント では動作しません。Docker はマウント先の所有者を変更しないため、まずホスト上で
chown 10001 /host/cache を一度実行するか、--cache-dir/--annotations-dir で書き込み可能なパスに出力先を変更してください。
コンテナ内での HTTP トランスポートの実行。 コンテナ内ではループバックがコンテナ自身を指すため、
サーバーはそこで 0.0.0.0 をリッスンする必要があります。ネットワークから到達できない状態を保つため、
ポートは ホスト側 のループバックに公開してください。
docker run -p 127.0.0.1:3000:3000 -v /path/to/forms:/forms ghcr.io/aoreshkov/oracle-forms-mcp \
--forms-dir /forms --transport http --host 0.0.0.0 --port 3000 # http://127.0.0.1:3000/mcp
単純な -p 3000:3000 では、ホストのすべてのインターフェースでポートが公開され、到達可能なすべてのユーザーに
すべてのツールが公開されます。
オプション
--forms-dir <path> Directory containing the Forms modules (or pass it positionally)
--convert-command <cmd> Site-supplied converter command (with its arguments) instead of frmf2xml
--compile-command <cmd> Separate converter command for .pll libraries (0.11.0+)
--converted-dir <path> Directory the converted XML/.pld is written into (default: the cache)
--transport stdio|http Transport (default: stdio)
--host <address> HTTP bind address (default: 127.0.0.1; pass 0.0.0.0 inside a container)
--port <int> HTTP port (default: 3000)
--allowed-host / --allowed-origin Extra HTTP hosts/origins (localhost-only by default)
--cache-dir <path> Cache override (default: OS cache dir + /oracle-forms-mcp)
--annotations-dir <path> Durable annotation store (default: <cache dir>/annotations)
--conversion-timeout <sec> Kill a stuck conversion (default: 120)
--prefetch <pattern> Fetch every module whose name contains <pattern>, then exit
--prefetch-all Fetch every module in the forms directory, then exit
--prefetch-type <type> Limit --prefetch to form, menu, library or object_library
--json With a prefetch: the summary on stdout as a JSON object
--help / --version Print the full help page, or the version, and exit
server --help には完全なリファレンスが記載されています。すべてのオプション、その既定値と対応する変数、
およびそれらの間のルールを確認できます。--port、--allowed-host、--allowed-origin には
--transport http が必要で、--prefetch と --prefetch-all は併用できません。終了ステータスは、
成功が 0、モジュールの読み込みに失敗したプリフェッチが 1、コマンドラインの誤りが 2、予期しない失敗が 70 です。
予期しない失敗の場合、スタックトレースは stderr に出力されます。
変数を使ってサーバーを設定するクライアント(docker run -e、MCP 設定の env ブロックなど)向けに、
コンバーターのオプションと HTTP のバインドアドレスは環境変数でも設定できます。フラグが変数より優先されます。
| フラグ | 変数 |
|---|---|
--convert-command | OFMCP_CONVERT_COMMAND |
--compile-command | OFMCP_COMPILE_COMMAND |
--converted-dir | OFMCP_CONVERTED_DIR |
--host | OFMCP_HTTP_HOST |
--convert-command と --converted-dir は、Claude Code plugin(/plugin → Oracle Forms)の設定項目としても公開されており、.mcpb バンドル(Claude Desktop のコネクター設定)と Docker イメージのレジストリー掲載情報では、3 つすべてが設定項目として公開されています。そのため、どの経路からインストールしても、JSON を手作業で編集せずに、独自のコンバーターと出力ディレクトリーをサーバーに指定できます。いずれも未設定のままで問題ありません。空の値は「未設定」として扱われます。
変換後の XML を保持する
既定では、モジュールの変換後のテキスト形式はキャッシュエントリ内に保存されます。XML を確認、差分比較したり、ほかのツールに渡したりできるように、--converted-dir に任意のディレクトリを指定します。
server --forms-dir C:\forms --converted-dir C:\forms-xml
すべてのモジュールで、この単一のフラットなディレクトリを共有します。各ファイルには Oracle での名前(orders_fmb.xml、mainmenu_mmb.xml、utils.pld)が付けられるため、再取得時にはコピーが蓄積されず、モジュールのファイルが置き換えられます。ディレクトリが存在しない場合は作成されます。また、フォームのディレクトリ自体は指定できません(そこから読み込まれる変換前のモジュールと名前が重複するためです)。フォームのディレクトリ内のサブディレクトリであれば問題ありません。スキャンは再帰的に行われないためです。
これはコンバーターが書き込みを行うディレクトリであり、後からファイルを移動する先ではありません。コンバーターはこのディレクトリを作業ディレクトリとして実行されるため、固定の場所に書き込むラッパーは、単にその場所を指定するだけで使えます。出力は、まず Oracle の命名規則(orders_fmb.xml)に基づいてモジュールに割り当てられます。コンバーターが自由な名前を付ける場合は、「この実行で書き込まれた、名前が一致する最新のファイル」が使われます。実行開始時点ですでに存在していたファイル(別のモジュールのテキスト形式や、強制終了した実行の不完全な出力)は対象になりません。また、このディレクトリを共有する変換は、名前の変更も含めて直列化されるため、2 つのモジュールが混同されることはありません。コンバーターが付けた名前にかかわらず、ファイルは標準の名前に変更されます。また、インデックスにはいずれの場合も安定した converted/<name> パスで記録されるため、このオプションを設定しているかどうかに関係なく、インデックスは有効です。
独自のコンバーターを使用する
サイトで Forms ツールをラップする独自の環境設定、ログオン処理、パッチを適用した
frmf2xml、または別のコンバーターを使用する場合は、サーバーの実行先として指定します。
server --forms-dir C:\forms --convert-command C:\tools\fmb2xml.bat
値には実行ファイルだけでなく、コマンドライン全体を指定するため、インタープリター、コンテナー、互換レイヤーなど、独自の引数を必要とするラッパーもそのまま設定できます。
server --forms-dir /srv/forms --convert-command "/opt/forms/convert.sh --xml --quiet"
server --forms-dir C:\forms --convert-command "\"C:\tools\my tools\f2x.bat\" /nologon"
server --forms-dir /srv/forms --convert-command '["wine", "/opt/forms/frmf2xml.exe", "{}"]'
使用できる構文は 2 種類あり、どちらもシェルには渡されません。ここでコマンドを分割し、argv リストを使って起動します。
- 引用符で囲んだ文字列。 空白で分割し、
"…"または'…'で囲むことで、空白を含む部分をまとめます。バックスラッシュはそのまま扱われるため、Windows のパスを二重にする必要はありません。 - JSON 配列 —
["wine", "f2x.exe", "-xml"]— 引数ごとに 1 つの要素を指定する形式で、MCP クライアントがcommand/argsに使用するものと同じ構造です。引用符の扱いが複雑になる場合はこちらを推奨します。JSON ではバックスラッシュに\\が必要な点に注意してください。
既存のファイルを指す値は、空白も含めて全体が 1 つの値として扱われます。そのため、このオプションが引数を受け付ける前に設定された通常のパスは、引用符で囲まなくても引き続き使用できます。
モジュールの絶対パスは、{} を記述した位置に渡されます。コマンドに {} がない場合は最後の引数として追加されます。これは、以前の <command> <module> という規約と同じです。コマンドは作業ディレクトリを変換先ディレクトリに設定して実行されます。デフォルトでは、そのモジュールのキャッシュディレクトリです。変換後のテキスト形式はそこに出力する必要があります。作業ディレクトリを指定できないツールには、代わりに出力ファイルが渡されます。{out} は絶対パス(<converted dir>/orders_fmb.xml、<converted dir>/utils.pld)に置換されます。{out} は置換されるだけで、追加されることはありません。そのため、これを含まないコマンドは従来どおりに実行されます。この動作は frmf2xml の実行方法に合わせているため、すでにこれをラップしているスクリプトを変更する必要はありません。パーサーが読み取る形式と同じ形式を出力してください。.fmb/.mmb/.olb には XML、.pll には .pld ダンプを使用します。Oracle の <name>_fmb.xml という命名が推奨されますが、必須ではありません。作業ディレクトリに書き込まれた .xml(ライブラリの場合は .pld)なら、どのファイル名でも検出されます。
プログラムには、パス(絶対パスまたはサーバーの起動場所からの相対パス)か、PATH から検索される名前のみを指定できます。
--converted-dir を使用する場合、その作業ディレクトリがそのまま指定先になるため、起動場所ではなく独自の固定場所に書き込むラッパーは、--converted-dir でその場所を指定すれば動作します。作業ディレクトリに書き込むスクリプトは、どちらの場合も変更不要です。
優先順位は --convert-command → ORACLE_HOME → コピーモードです。そのため、Forms がインストールされているマシンでも、明示的に設定したコマンドが優先されます(.pll ライブラリはこの順序から外せます。以下を参照してください)。空の値は未設定として扱われます。ORACLE_HOME と同様に、コマンドの解析と検証は起動時ではなく最初の変換時に行われます。そのため、古い設定が残っていてもキャッシュ済みのモジュールは読み取り可能です。その場合、エラーには修正が必要なフラグ名が示されます。
出力は新しく書き込まれたものでなければなりません。 Forms 時代のツールは終了コードが信頼できないため、実行結果は出力ファイルで判定されます。また、実行開始より古いファイルは、以前の失敗した試行の残りとして扱われます。
copy、xcopy、cp -pで生成済みファイルをコピーするスクリプトは、元ファイルのタイムスタンプを保持するため、"produced no output file" というエラーで拒否されます。代わりにリダイレクトまたは再書き込みを行う(type src > out、cat src > out)か、結果にtouchを実行してください。
このコマンドはオペレーターのみが設定できます。ツールの引数で選択または拡張することはできません。ツールの呼び出し元が渡すのはモジュール名であり、コンバーターに渡される前にスキャン済みの Forms ディレクトリに対して解決されます。また、コマンドはシェルを介さず argv リストで直接起動されるため、モジュールのパスや引用符の指定内容が別のコマンドとして実行されることはありません。
独自のコマンドを使用してPL/SQLライブラリを変換する
0.11.0以降。 Oracleのツールで.pllをXMLに変換できるものはありません。frmf2xmlが受け付けるのはフォーム、メニュー、オブジェクトライブラリのみであり、ライブラリは代わりにfrmcmpで.pldテキストにダンプされます。そのため、frmf2xmlを使用する--convert-commandでは、すべてのライブラリで失敗します。--compile-commandを使うと、.pllモジュール専用のコマンドラインを、同じ構文とプレースホルダーで指定でき、その他のモジュールタイプには影響しません。
server --forms-dir /srv/forms --converted-dir /srv/forms-xml --convert-command "/opt/forms/f2xml.sh {}" --compile-command "frmcmp_batch Module={} Module_Type=LIBRARY Script=YES Batch=YES Logon=NO Output_File={out}"
または、JSON配列で指定します。
--compile-command '["frmcmp_batch", "Module={}", "Module_Type=LIBRARY", "Script=YES", "Batch=YES", "Logon=NO", "Output_File={out}"]'
1行で記述してください。この値はシェルを介さずに引数へ分割されるため、設定フィールドにコピーした\による行継続は、リテラル引数としてツールに渡されます。
frmcmpにOutput_File={out}を指定してください。 これがないと、frmcmpは.pldをモジュールと同じ場所、つまり作業ディレクトリにかかわらずフォームのディレクトリに書き込みます。その後、サーバーはそのファイルを変換済みファイルとして読み込みます。変換先ディレクトリに何も生成されず、モジュールの隣に新しいファイルが残っている場合、エラーにはそのファイル名が表示され、{out}を追加するよう案内されます。サーバーがそのファイルを削除することはありません。
Logon=NOも重要です。これがない場合(または実際のUserid=がない場合)、frmcmpは使用方法を表示して、何も書き込まずに終了コード0で終了します。
モジュールが使用するコンバーター:
--compile-command | --convert-command | ORACLE_HOME | .pll | .fmb / .mmb / .olb |
|---|---|---|---|---|
| — | — | — | コピーモード | コピーモード |
| — | — | 設定済み | frmcmp | frmf2xml |
| — | 設定済み | — / 設定済み | 変換コマンド | 変換コマンド |
| 設定済み | — | — | コンパイルコマンド | コピーモード |
| 設定済み | — | 設定済み | コンパイルコマンド | frmf2xml |
| 設定済み | 設定済み | — / 設定済み | コンパイルコマンド | 変換コマンド |
--compile-commandが未設定の場合、動作に変更はありません。ライブラリをすでに処理している--convert-commandには、引き続きライブラリが渡されます。あるタイプがコピーモードで処理される場合、キャッシュエントリーとの照合には変換前のテキスト形式が使われるため、_fmb.xmlを再エクスポートすると、そのフォームは引き続き古いものとして扱われますが、ライブラリはバイナリから変換されます。
キャッシュ
%LOCALAPPDATA%\oracle-forms-mcp(Windows)、~/Library/Caches/oracle-forms-mcp(macOS)、$XDG_CACHE_HOME/oracle-forms-mcp(Linux)。モジュールごとに1つのディレクトリが作成されます。
ORDERS.fmb/
converted/orders_fmb.xml converted (or copied) text form
plsql/triggers/*.sql decoded trigger bodies
plsql/program-units/*.sql decoded program units
plsql/menu-items/*.sql menu-item command bodies (menu modules)
index.json the structured index
いつでも安全に削除できます。モジュールは再取得されます。--converted-dirを使用すると、converted/ファイルは指定したディレクトリに書き込まれ、エントリーの残りの部分はここに保持されます。
注釈はこの派生キャッシュには含まれません。注釈は別のannotations/ストアに保存されます(モジュールごとにNAME.ext.jsonが1つあり、既定では<cache dir>/annotationsです。--annotations-dirで変更できます)。そのため、モジュールのキャッシュエントリーを削除したり、再取得したりしても、記録したメモ、タグ、関連付けはそのまま残ります。
Oracleツールについての注意事項
frmf2xmlはプロセスの作業ディレクトリに出力を書き込みます。サーバーは変換先ディレクトリ(--converted-dir、未指定の場合はモジュールのキャッシュディレクトリ)を作業ディレクトリとして実行し、OVERWRITE=YES USE_PROPERTY_IDS=NOを渡します。- ヘッドレス実行のため、
frmcmpよりもfrmcmp_batchが優先されます。サーバーはScript=YES Batch=YES Logon=NOと明示的なOutput_Fileを渡します。指定がない場合、frmcmpは.pldを作業ディレクトリではなくモジュールの隣に書き込みます。また、添付ライブラリを解決できるよう、サーバーはフォームのディレクトリをFORMS_PATHに追加します。 - フォームツールの終了コードは信頼できないため、出力ファイルが存在し、空でなく、実行開始時刻より新しいことを成功の判定基準とします。失敗時には、ツールの出力末尾が表示されます。
.pldファイルはクライアントのNLS文字セットで書き込まれる場合があります。パーサーはUTF-8で読み込み、失敗した場合はwindows-1252にフォールバックします。文字化けが発生する場合は、NLS_LANGを適切に設定してください。
プライバシーポリシー
Oracle Forms MCP はすべてお使いのマシン上で動作し、情報を収集しません。
- データ収集。 ありません。サーバーにはテレメトリ、分析、クラッシュレポート機能、アカウントはなく、サーバー自体がネットワークリクエストを送信することもありません。
- 読み取り、保存する内容。 指定した
--forms-dir内のモジュールを読み取り、変換済みのテキスト形式、デコード済み PL/SQL、index.jsonなどの生成ファイルをローカルのキャッシュ(または--converted-dir)に書き込みます。お使いの方またはアシスタントが記録した注釈は、ローカルの注釈ストアに書き込まれます。これらはすべてディスク上に保存され、いつでも削除できます。 - コンテンツの送信先。 モジュールのコンテンツがサーバーから送信されるのは、接続先の MCP クライアントへの応答としてのみです。モデルプロバイダーへの送信を含め、そのクライアントがコンテンツをどう扱うかは、こちらのポリシーではなく、クライアント独自のプライバシーポリシーに準拠します。
httpトランスポートは、--hostで別のホストにバインドしない限り、127.0.0.1のみで待ち受けます。 - 第三者との共有。 ありません。「ネットワークを使用しない」ことの唯一の例外は、Claude Code プラグインのランチャーです。ランチャーは 1 日に最大 1 回、GitHub API に最新リリースを問い合わせ、GitHub Releases からリリースの zip ファイルとそのチェックサムをダウンロードします。これらのリクエストは、GitHub のプライバシーに関する声明の適用を受ける GitHub 宛てに送信され、モジュールの情報は含まれません。これらのリクエストを避けるには、
server_versionを固定するか、OFMCP_SERVER_HOMEを使用してください。 - 保持期間。 キャッシュファイルと注釈ファイルは、削除するまで保持されます。ソフトウェアがほかの場所に情報を保存することはありません。
- お問い合わせ。 ご質問は GitHub issues までお寄せください。セキュリティに関する報告は SECURITY.md に従ってください。
開発
gradlew build # compile + all tests (no Oracle installation needed)
gradlew updateKotlinAbi # refresh the ABI dump (core/api/*.api) after public API changes
gradlew :server:run --args="--forms-dir sample-forms"
gradlew :server:packageMcpb # build the installable .mcpb bundle
コンバーターの動作は、模擬スクリプトを使った偽の ORACLE_HOME に対してテストされます。コピーモードのパイプライン全体は、バンドルされたフィクスチャを使い、FormsServiceIntegrationTest によるエンドツーエンドテストでカバーされています。
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。