2025年5月27日火曜日

Oracle REST Data ServicesのRESTサービスとしてStreamable HTTPを話すMCP Serverを実装してみる

Model Context Protocolの2025-03-26版より、MCPクライアントとMCPサーバー間の通信にStreamable HTTPが使えるようになりました。それまでのSSE - Server Side Eventを使った通信はOracle Databaseで扱えなかったため、リモートMCPサーバーを直接データベース上に実装できませんでした。Streamable HTTPは通常のHTTPによる通信なので、リモートMCPサーバーをOracle REST Data ServicesのRESTサービスとして実装できそうです。

実際にOracle REST Data ServicesのRESTサービスとして、簡単なリモートMCPサーバーを実装してみました。以下の記事でツール呼び出し向けに実装しているget_schemaとrun_sqlを、リモートMCPサーバーのtools/listで一覧し、tools/callで呼び出せるようにします。

Qwen3 30B A3B MLXをMacのLM Studioで実行しAPEXアプリケーションからツール呼び出しを行う

ツールとして使用するget_schemaとrun_sqlの実装や設定をワークスペースに作成するため、以下のAPEXアプリケーションをインポートします。表OPENAI_TOOLSとファンクションget_schema、run_sqlが作成され、属性などの定義情報が表に挿入されます。
https://github.com/ujnak/apexapps/blob/master/exports/chat-with-generative-ai-hc-242.zip

現時点でリモートMCPサーバーを直接呼ぶには、ClaudeのMaxプランに入るか、mcp-remoteを中間に入れる方法しか見つけられませんでした。mcp-remoteではなぜか上手く通信できず、また、ClaudeのMaxプランは高価です。

そういった理由で、とりあえず、MCP Inspectorで動作確認を行なっています。以下のGIF動画ように呼び出しています。


MCP Inspectorを起動します。

npx @modelcontextprotocol/inspector

% npx @modelcontextprotocol/inspector

Starting MCP inspector...

⚙️ Proxy server listening on port 6277

🔍 MCP Inspector is up and running at http://127.0.0.1:6274 🚀



MCP Inspector is up and running at ... に続いて表示されているURLに、ブラウザより接続します。

Transport TypeStreamable HTTPを選択し、URLにORDSのRESTサービスのURLを入力します。ORDSのRESTサービスをOAuth2で保護している場合、Header NameAuthorizationBearer TokenにORDSのトークンURLを呼び出して得られたトークンを設定します。

Connectをクリックすると、methodinitializeのリクエストが送信されます。そのレスポンスを受けて、接続が確立します。レスポンスに含まれるcapabilitiestoolsだけです。


Toolsタブを開き、List Toolsをクリックします。


methodがtools/listのリクエストが送信され、そのレスポンスとしてget_schemaとrun_sqlが呼び出し可能なツールとして返されます。


ツールからget_schemaを選択して、Run Toolをクリックします。methodtools/callのリクエストが送信され、そのレスポンスとしてget_shemaの出力が返されます。


同様にツールからrun_sqlを選択します。パラメータのsqlに以下を入力し、Run Toolをクリックします。

select * from eba_countries_v

run_sqlが呼び出され、上記のSELECT文の出力が返されます。


Disconnectをクリックします。以上で最初の状態に戻ります。


以下よりMCP Inspectorから呼び出している、リモートMCPサーバーについて説明します。

MCPサーバーはAlways FreeのAutonomous Databaseに実装しています。MCP Inspectorで確認する範囲であれば、パブリックIPやDNSに登録されたホスト名、HTTPSといった条件は不要かと思います。

主にJSONRPCを扱うパッケージとしてMCP_HTTP_SERVER_PKGを作成しています。プロシージャORDS_HANDLERを、ORDSのRESTサービスから呼び出します。

create or replace package mcp_http_server_pkg
as
/**
* JSON-RPC標準エラー・コード
*/
C_PARSE_ERROR constant number := -32700;
C_INVALID_REQUEST constant number := -32600;
C_METHOD_NOT_FOUND constant number := -32601;
C_INVALID_PARAMS constant number := -32602;
C_INTERNAL_ERROR constant number := -32603;
/**
* ORDSのPOSTハンドラに設定してMCP Serverの処理を呼び出す。
*
* @param p_script_name owa_util.get_cgi_env('SCRIPT_NAME')で取得されるスクリプト名。
* @param p_username ORDS RESTハンドラ内の:current_userで取得されるユーザ名。
* @param p_request :bodyで取得されるリクエストボディ。実態はJSONRPCのリクエスト。
* @param p_response レスポンスとなるBLOB。実態はJSONRPCのレスポンス。notificationの場合はnull。
* @param p_session_id apex_session.create_sessionで作成したセッションID。
* @param p_status_code レスポンスのHTTPステータスコード。通常は200。
*/
procedure ords_handler(
p_script_name in varchar2
,p_username in varchar2
,p_request in blob
,p_response out blob
,p_session_id out varchar2
,p_status_code out number
);
end mcp_http_server_pkg;

create or replace package body mcp_http_server_pkg as
C_MCP_SESSION_ID_HEADER constant varchar2(16) := 'Mcp-Session-Id';
/**
* 正常なレスポンスからメッセージを作成する。
*/
function create_success_response(
p_id in number
,p_result in clob
)
return blob
as
l_response_json json_object_t;
begin
l_response_json := json_object_t();
l_response_json.put('jsonrpc', '2.0');
l_response_json.put('id', p_id);
l_response_json.put('result', json_object_t(p_result));
return l_response_json.to_blob;
end create_success_response;
/**
* エラーからレスポンス・メッセージを作成する。
*/
function create_error_response(
p_id in number
,p_code in number
,p_message in varchar2
,p_data in clob default null
)
return blob
as
l_response_json json_object_t;
l_error_json json_object_t;
begin
l_response_json := json_object_t();
l_response_json.put('jsonrpc', '2.0');
l_response_json.put('id', p_id);
l_error_json := json_object_t();
l_error_json.put('code', p_code);
l_error_json.put('message', p_message);
if p_data is not null then
l_error_json.put('data', json_object_t(p_data));
end if;
l_response_json.put('error', l_error_json);
return l_response_json.to_blob;
end create_error_response;
/**
* 動的時に実行するSQLを生成する。
*/
function create_dynamic_sql(
p_package_name in varchar2
,p_method in varchar2
)
return varchar2
as
l_sql varchar2(4000);
begin
l_sql := 'begin ' || p_package_name || '.' || replace(p_method,'/','_') || '(';
l_sql := l_sql || ':username, :p_params, :p_context, :p_result, :p_error, :p_status_code); end;';
return l_sql;
end create_dynamic_sql;
/**
* ORDSのPOSTハンドラに設定してMCP Serverの処理を呼び出す。
*/
procedure ords_handler(
p_script_name in varchar2
,p_username in varchar2
,p_request in blob
,p_response out blob
,p_session_id out varchar2
,p_status_code out number
)
as
/*
* JSONRPCリクエスト。
*/
l_request_json json_object_t;
l_id number;
l_method varchar2(128);
l_params clob;
l_version varchar2(16);
l_username varchar2(128);
/*
* MCPセッションID。
*/
l_session_id varchar2(128);
/*
* MCPサーバーの実体であるハンドラの設定。
*/
r_mcp_http_server mcp_http_servers%rowtype;
/*
* MCPサーバーのmethodの動的な呼び出し。
*/
l_sql varchar2(4000);
l_result clob;
l_error clob;
l_status_code number;
begin
/*
* ユーザー名の取得。
*/
if p_username is null then
select sys_context('USERENV', 'SESSION_USER') into l_username from dual;
else
l_username := p_username;
end if;
/*
* Mcp-Session-Idヘッダを取得する。
*/
p_session_id := owa_util.get_cgi_env(C_MCP_SESSION_ID_HEADER);
/*
* リクエストの検証とパースを行う。id, method, paramsを取り出す。
*/
begin
l_request_json := json_object_t(p_request);
/*
* リクエストのidを取得する。ClaudeのMCP Inspectorでは数値。
* notificationの場合はnullなのでチェック不要。
*/
l_id := l_request_json.get_number('id');
/* JSON-RPCのバージョンを確認する。 */
l_version := l_request_json.get_string('jsonrpc');
if l_version is null or l_version != '2.0' then
p_status_code := 400;
p_response := create_error_response(
p_id => null,
p_code => C_INVALID_REQUEST,
p_message => 'Invalid JSON-RPC version. Expected "2.0".'
);
return;
end if;
/*
* methodを取得する。methodは必須。
*/
l_method := l_request_json.get_string('method');
if l_method is null then
p_status_code := 400;
p_response := create_error_response(
p_id => null,
p_code => C_INVALID_REQUEST,
p_message => 'Method is required in the request.'
);
return;
end if;
/*
* paramsが必須かどうかはmethodに依存。
*/
l_params := l_request_json.get_object('params').to_clob();
exception
when others then
p_status_code := 400;
p_response := create_error_response(
p_id => null,
p_code => C_PARSE_ERROR,
p_message => 'Invalid JSON format in request body. sqlerrm: ' || sqlerrm
);
return;
end;
/*
* ハンドラの設定を取得する。
*/
begin
select * into r_mcp_http_server from mcp_http_servers where ords_uri = p_script_name;
exception
when no_data_found then
p_status_code := 400;
p_response := create_error_response(
p_id => l_id,
p_code => C_INVALID_REQUEST,
p_message => 'No MCP Server registered: ' || p_script_name
);
return;
end;
/*
* メソッドがinitializeの場合は新規にセッションを開始する。
*/
if l_method = 'initialize' then
apex_session.create_session(
p_app_id => r_mcp_http_server.apex_app_id
,p_page_id => r_mcp_http_server.apex_page_id
,p_username => l_username
);
p_session_id := v('APP_SESSION');
else
/* セッションIDが取得できたらセッションをアタッチする。 */
if p_session_id is not null then
apex_session.attach(
p_app_id => r_mcp_http_server.apex_app_id
,p_page_id => r_mcp_http_server.apex_page_id
,p_session_id => p_session_id
);
else
p_status_code := 400;
p_response := create_error_response(
p_id => l_id,
p_code => C_INVALID_REQUEST,
p_message => 'Mcp-Session-Id is required for method: ' || l_method
);
return;
end if;
end if;
/*
* デバッグレベルを設定する。何故かこの処理は効いていない。
*/
if r_mcp_http_server.log_level is not null then
apex_debug.enable(r_mcp_http_server.log_level);
end if;
/*
* MCP Serverのハンドラを呼び出す。
*/
l_sql := create_dynamic_sql(r_mcp_http_server.package_name, l_method);
execute immediate l_sql
using in l_username, in l_params, in r_mcp_http_server.tool_set
,out l_result, out l_error, out l_status_code;
p_status_code := l_status_code;
if l_id is not null then
/*
* idがある場合は通常のリクエスト。
*/
if l_error is not null then
/*
* エラーが発生した場合はエラーレスポンスを返す。
*/
p_response := create_error_response(
p_id => l_id,
p_code => C_INTERNAL_ERROR,
p_message => 'Error in MCP Server: ' || l_error
);
else
/*
* 正常に処理が完了した場合はレスポンスを返す。
*/
p_response := create_success_response(
p_id => l_id
,p_result => l_result
);
end if;
end if;
exception
when others then
/*
* 例外が発生した場合はエラーレスポンスを返す。
*/
p_status_code := 500;
p_response := create_error_response(
p_id => l_id,
p_code => C_INTERNAL_ERROR,
p_message => 'Internal Server Error: ' || sqlerrm
);
end ords_handler;
end mcp_http_server_pkg;
MCPサーバーとして必要なinitializetools/listtools/callの処理は、パッケージMCP_SAMPLEに実装しています。

create or replace package mcp_sample as
procedure initialize(
p_username in varchar2
,p_params in clob
,p_context in varchar2
,p_result out clob
,p_error out clob
,p_status_code out number
);
procedure notifications_initialized(
p_username in varchar2
,p_params in clob
,p_context in varchar2
,p_result out clob
,p_error out clob
,p_status_code out number
);
procedure tools_list(
p_username in varchar2
,p_params in clob
,p_context in varchar2
,p_result out clob
,p_error out clob
,p_status_code out number
);
procedure tools_call(
p_username in varchar2
,p_params in clob
,p_context in varchar2
,p_result out clob
,p_error out clob
,p_status_code out number
);
end mcp_sample;
/
view raw mcp_sample.pkh hosted with ❤ by GitHub

create or replace package body mcp_sample as
procedure initialize(
p_username in varchar2
,p_params in clob
,p_context in varchar2
,p_result out clob
,p_error out clob
,p_status_code out number
)
as
l_request_json json_object_t;
l_result_json json_object_t;
l_response clob;
l_response_json json_object_t;
l_id varchar2(128);
l_si json_object_t;
begin
p_status_code := 200;
p_error := null;
/* 固定でcapabilitiesを返す。 */
l_result_json := json_object_t(q'~{
"protocolVersion": "2025-03-26",
"capabilities": {
"logging": {},
"prompts": {},
"resources": {},
"tools": {
"listChanged": true
}
},
"serverInfo": {
"name": "mcp_sample",
"version": "0.1.0"
}
}~');
/*
l_si := l_result_json.get_object('serverInfo');
l_si.put('username', p_username);
*/
p_result := l_result_json.to_clob();
end initialize;
procedure notifications_initialized(
p_username in varchar2
,p_params in clob
,p_context in varchar2
,p_result out clob
,p_error out clob
,p_status_code out number
)
as
begin
/* 通知なのでp_status_codeは204を返す。 */
p_status_code := 204;
p_error := null;
p_result := null;
end notifications_initialized;
procedure tools_list(
p_username in varchar2
,p_params in clob
,p_context in varchar2
,p_result out clob
,p_error out clob
,p_status_code out number
)
as
l_tool_json json_object_t;
l_tools_arr json_array_t;
l_input_schema_json json_object_t;
l_result_json json_object_t;
begin
/*
* 表OPENAI_TOOLSは、OpenAI Chat Completions APIのツール呼び出し向けに作成したもの。
* それを流用している。
* OpenAIがargumentsのところをAnthropicではinputSchemaになっている。
*/
l_tools_arr := json_array_t();
for r in (
select tool_name, description, parameters from openai_tools where tool_set = p_context and tool_type = 'function'
)
loop
l_tool_json := json_object_t();
l_tool_json.put('name', r.tool_name);
l_tool_json.put('description', r.description);
l_input_schema_json := json_object_t(r.parameters);
l_tool_json.put('inputSchema', l_input_schema_json);
l_tools_arr.append(l_tool_json);
end loop;
l_result_json := json_object_t();
l_result_json.put('tools', l_tools_arr);
p_result := l_result_json.to_clob();
p_error := null;
p_status_code := 200;
end tools_list;
procedure tools_call(
p_username in varchar2
,p_params in clob
,p_context in varchar2
,p_result out clob
,p_error out clob
,p_status_code out number
)
as
l_params json_object_t;
l_name varchar2(128);
l_args_obj json_object_t;
l_args clob;
l_sql varchar2(256);
l_out clob;
l_result_json json_object_t;
l_content_arr json_array_t;
l_out_obj json_object_t;
l_response_json json_object_t;
l_error_json json_object_t;
begin
/*
* nameは必ずなければならないので、p_paramsがnullは許容しない。
*/
if p_params is null then
l_error_json := json_object_t();
l_error_json.put('code', mcp_http_server_pkg.C_INVALID_PARAMS);
l_error_json.put('message', 'Invalid parameters: p_params is null');
p_error := l_error_json.to_clob();
p_result := null;
p_status_code := 400;
return;
end if;
/*
* パラメータをパースしてnameとargumentsを取得する。
*/
l_params := json_object_t.parse(p_params);
l_name := l_params.get_string('name');
/*
* nameは必須なので、nullの場合はエラーを返す。
*/
if l_name is null then
l_error_json := json_object_t();
l_error_json.put('code', mcp_http_server_pkg.C_INVALID_PARAMS);
l_error_json.put('message', 'Invalid parameters: name is required');
p_error := l_error_json.to_clob();
p_result := null;
p_status_code := 400;
return;
end if;
/*
* 引数argumentsはオプション。
*/
l_args_obj := l_params.get_object('arguments');
if l_args_obj is not null then
l_args := l_args_obj.to_clob();
end if;
/*
* ツールの実行。
*/
l_sql := 'begin :a := ' || l_name || '(:b); end;';
execute immediate l_sql using out l_out, in l_args;
/* 出力をフォーマットする。 */
l_result_json := json_object_t();
l_content_arr := json_array_t();
l_out_obj := json_object_t();
l_out_obj.put('type', 'text');
l_out_obj.put('text', l_out);
l_content_arr.append(l_out_obj);
l_result_json.put('content', l_content_arr);
l_result_json.put('isError', false);
p_result := l_result_json.to_clob();
p_error := null;
p_status_code := 200;
exception
when others then
l_error_json := json_object_t();
l_error_json.put('code', mcp_http_server_pkg.C_INTERNAL_ERROR);
l_error_json.put('message', 'Error in tools_call: ' || sqlerrm);
p_error := l_error_json.to_clob();
p_result := null;
p_status_code := 500;
end tools_call;
end mcp_sample;
/
view raw mcp_sample.pkb hosted with ❤ by GitHub
これらのパッケージやパッケージを登録する表MCP_HTTP_SERVERSの作成を、サポート・スクリプトとして含んだAPEXアプリケーションMCP Handlerのエクスポートを以下の置きました。
https://github.com/ujnak/apexapps/blob/master/exports/mcp_handler.zip

このアプリケーションをインストールして実行すると、以下のような画面が開きます。初期状態では、レポートは空です。


表MCP_HTTP_SERVERSに、RESTサービスとリモートMCPサーバーを紐づける情報を設定します。

ORDS URIにORDSのRESTサービスのURIを設定します。/ords/ORDS別名/モジュール名になります。リモートMCPサーバーのエンドポイントの末尾は/mcpになるので、テンプレート名は必ずmcpになります。ORDS URIからはmcpの部分は除きます。

methodのinitizliazeを受け付けたときに開始するセッションとして、APEXのセッションを流用しています。そのため、APEX_SESSION.CREATE_SESSIONに与えるアプリケーションIDとページIDを、Apex App IDApex Page IDに設定します。これはデフォルトで、このアプリ自体のアプリケーションIDとページIDを割り当てているので変更は不要です。

Package NameにリモートMCPサーバーの処理を実装しているパッケージ名を設定します。methodinitializeであれば、このパッケージに含まれるプロシージャINITIALIZEが呼び出されます。methodtools/listであれば、/を_に置き換えたプロシージャTOOLS_LISTを呼び出します。tools/callはプロシージャTOOLS_CALLが呼び出されます。MCPサーバーにpromptsやresourcesは実装していませんが、initializeのレスポンスのcapabilitiesに含めて、パッケージにpromptsやresourcesをプロシージャとして実装することで、フレームワークを変更することなく機能を追加できます。

Log LevelはAPEX_DEBUG.ENABLEに与える数値です。しかし、何故かAPEX_DEBUG.INFOの出力をビューAPEX_DEBUG_MESSAGESから確認できていません。


Tool Setの指定は、Chat with Generative AIのアプリケーションのToolsに登録した、Tool Setを指定します。今回のテストでは、ファンクションget_schemarun_sqlをまとめたCountriesを選択しています。


続いて、ORDSのRESTサービスの設定を行います。こちらについてはインストール・スクリプトを先ほどのmcp_handler.zipに含めているので、APEXアプリケーションを実行するとRESTサービスも作成されます。

-- Generated by ORDS REST Data Services 25.1.0.r1001652
-- Schema: WKSP_APEXDEV Date: 火 5月 27 08:00:03 2025
--
DECLARE
l_roles OWA.VC_ARR;
l_modules OWA.VC_ARR;
l_patterns OWA.VC_ARR;
BEGIN
/*
* ORDS.ENABLE_SCHEMAは実施済みが前提。
*
ORDS.ENABLE_SCHEMA(
p_enabled => TRUE,
p_schema => 'WKSP_APEXDEV',
p_url_mapping_type => 'BASE_PATH',
p_url_mapping_pattern => 'apexdev',
p_auto_rest_auth => FALSE);
*/
ORDS.DEFINE_MODULE(
p_module_name => 'sampleserver',
p_base_path => '/sampleserver/',
p_items_per_page => 25,
p_status => 'PUBLISHED',
p_comments => NULL);
ORDS.DEFINE_TEMPLATE(
p_module_name => 'sampleserver',
p_pattern => 'mcp',
p_priority => 0,
p_etag_type => 'HASH',
p_etag_query => NULL,
p_comments => NULL);
ORDS.DEFINE_HANDLER(
p_module_name => 'sampleserver',
p_pattern => 'mcp',
p_method => 'POST',
p_source_type => 'plsql/block',
p_mimes_allowed => 'application/json',
p_comments => NULL,
p_source =>
'declare
l_response blob;
l_session_id varchar2(128);
l_status_code number;
l_request blob;
begin
l_request := :body;
mcp_http_server_pkg.ords_handler(
p_script_name => owa_util.get_cgi_env(''SCRIPT_NAME'')
,p_username => :current_user
,p_request => l_request
,p_response => l_response
,p_session_id => l_session_id
,p_status_code => l_status_code
);
/*
* レスポンスを呼び出し元に返す。
*/
:status_code := l_status_code;
sys.htp.init;
sys.htp.p(''Content-Type: application/json'');
if l_session_id is not null then
sys.htp.p(''Mcp-Session-Id: '' || l_session_id);
end if;
if l_response is not null and dbms_lob.getlength(l_response) > 0 then
sys.htp.p(''Content-Length: '' || dbms_lob.getlength(l_response));
sys.owa_util.http_header_close;
sys.wpg_docload.download_file(l_response);
else
sys.owa_util.http_header_clo' || 'se;
end if;
commit;
end;');
ORDS.DEFINE_HANDLER(
p_module_name => 'sampleserver',
p_pattern => 'mcp',
p_method => 'DELETE',
p_source_type => 'plsql/block',
p_mimes_allowed => NULL,
p_comments => NULL,
p_source =>
'declare
l_session_id varchar2(128);
begin
l_session_id := owa_util.get_cgi_env(''Mcp-Session-Id'');
if l_session_id is not null then
apex_session.delete_session(l_session_id);
end if;
:status_code := 200;
end if;');
ORDS.CREATE_ROLE(p_role_name => 'MCP Server Role');
l_roles(1) := 'MCP Server Role';
l_modules(1) := 'sampleserver';
ORDS.DEFINE_PRIVILEGE(
p_privilege_name => 'oracle.example.mcp',
p_roles => l_roles,
p_patterns => l_patterns,
p_modules => l_modules,
p_label => 'MCP Server',
p_description => NULL,
p_comments => NULL);
l_roles.DELETE;
l_modules.DELETE;
l_patterns.DELETE;
COMMIT;
END;
RESTサービスとしてsampleserverが作成されますが、作成した時点でOAuth2で保護されています。そのため、RESTサービスにアクセスするためのOAuthクライアントを作成します。

ORDSのOAuth2による保護については、以前に書いたこちらの記事「ORDS REST APIのOAuth2による保護とAPEXからの呼び出し」が参考になります。

以下のスクリプトを実行し、クライアントとしてmcp_clientを作成し、ロールMCP Server Roleを割り当てます。

begin
-- OAuth2 clientとしてmcp_clientを作成します。
oauth.create_client(
p_name => 'mcp_client'
,p_grant_type => 'client_credentials'
,p_description => 'Sample MCP Client'
,p_support_email => 'yuXX@acme.com'
,p_privilege_names => NULL
);
-- mcp_clientにロールMCP Server Roleを割り当てる。
oauth.grant_client_role(
p_client_name => 'mcp_client'
,p_role_name => 'MCP Server Role'
);
end;


Bearerトークンを取得するために、作成したOAuthクライアントmcp_clientclient_idclient_secretを検索します。

以下のSELECT文を実行します。

select name, client_id, client_secret from user_ords_clients where name = 'mcp_client'


取り出したclient_idおよびclient_secretを使って、ORDSのトークンURLを呼び出します。ORDSのトークンURLは、ORDS別名以降に/oauth/tokenを加えたものになります。
curl -X POST https://[ホスト名]/ords/[ORDS別名]/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-u "[client_id]:[client_secret]" \
-d "grant_type=client_credentials"

% curl -X POST https://***********-apexdev.adb.us-ashburn-1.oraclecloudapps.com/ords/apexdev/oauth/token \

-H "Content-Type: application/x-www-form-urlencoded" \

-u "rJc************Qjg..:-FnhL***********eg.." \

-d "grant_type=client_credentials"

{"access_token":"KhDE2WU*********38oOg","token_type":"bearer","expires_in":3600}

% 


ベーシック認証の情報としてclient_idとclient_secretを与えてトークンURLを呼び出すと、access_tokenが返されます。access_tokenの値をコピーし、MCP InspectorのBearer Tokenに設定します。


リモートMCPサーバーの保護についてきちんと調べ切れていないのですが、この方法による保護はユーザーが固定になり、アプリケーションの利用者に紐づきません。おそらく、こちらの記事「ORDS 23.3で追加されたOAUTH.CREATE_JWT_PROFILEを使ってRESTサービスを保護する」で使用しているOAUTH.CREATE_JWT_PROFILEによる保護が必要なのではないかと思われます。

実際にはClaude DesktopやClaude.aiに統合した上でテストを行う必要はありますが、何とかなりそうな印象はあります。ただし、アプリケーションを利用しているユーザーに認証を紐づけるのは、難度が高そうだとは思います。

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

Oracle APEXのアプリケーション作成の参考になれば幸いです。