RPC アクション: 関数のコンパイル
Kozou はスキーマの 名詞 — テーブルとビュー — を忠実な
REST / OpenAPI / MCP / Admin UI サーフェスにコンパイルします。v1.4 以降は
動詞 もコンパイルできます。COMMENT でタグ付けされた Postgres 関数が、
同じ 4 つのサーフェスすべてで呼び出し可能な アクション になります。
これが重要なのは、Kozou が推奨するセキュリティモデル — JWT →
SET LOCAL ROLE → 行レベルセキュリティ + 列 GRANT — が、意味のある書き込み
(注文を承認、入金を記録、公開) を関数に集約し、テーブルレベルの書き込み権限を
revoke するためです。これらの動詞は、本来であればすべての生成サーフェスの
外側に落ちてしまいます。RPC アクションはそれらを 同じ 認可機構の上に取り戻し
ます。新しい認証モデルは不要で、どの role が各関数を実行できるかはデータベースが
施行します。
Kozou の出力の中で RPC がどこに位置するかは @kozou/api REST レイヤー と COMMENT 規約 を参照してください。
関数を露出する
Section titled “関数を露出する”デフォルトでは何も露出されません。関数は、その COMMENT が @expose: rpc
タグを持つときにのみアクションへコンパイルされます。
COMMENT ON FUNCTION approve_order(order_id uuid) IS '注文を承認し在庫を確保する。 @ai: 冪等ではない — 再呼び出し前に注文ステータスを確認すること。 @policy: 承認できるのはマネージャーのみ。 @expose: rpc';このタグ 1 つで、関数は 4 つのサーフェスすべて にコンパイルされます。
- REST —
POST /rpc/<schema>.<fn>。名前付き引数の JSON ボディを取ります (例:POST /rpc/public.approve_order{ "order_id": "…" })。 - OpenAPI —
POST /rpc/<schema>.<fn>operation (operationId: rpc.<schema>.<fn>)。@ai/@policyの advisory はx-kozou-ai/x-kozou-policyとして付与されます。 - MCP —
describe_functionsツールが各アクションのシグネチャと advisory を 返すので、AI エージェントはどんなアクションが存在しどう呼ぶかを把握できます。 - Admin UI — “Actions” ページが引数フォーム (テーブルフォームと同じ widget 推論 — enum-select、relation-select など) を描画し、結果を表示します。
引数は名前付き (スカラ / enum / FK 型) で、DEFAULT を持つ引数は省略できます。
戻り値の wire マッピング:
| Postgres の戻り | Wire |
|---|---|
スカラ (integer、text、uuid など) | その値 |
| 単一 composite / 表の 1 行 | オブジェクト |
SETOF / RETURNS TABLE(...) | 配列 — 行型なら オブジェクトの配列、スカラ集合なら 値の配列 |
void | 204 No Content (REST)、MCP call 経由では { ok: true, note: … } |
露出は権限ではない
Section titled “露出は権限ではない”@expose: rpc タグは「この動詞をサーフェスに出す」という意味であり、誰が
実行できるか を決めるものではありません。それを施行するのは、リクエストの
role (JWT クレーム由来、SET LOCAL ROLE 経由) における PostgreSQL の EXECUTE
権限です。EXECUTE を持たない呼び出し元は 42501 となり、Kozou はこれを汎用的な
ボディとともに 403 Forbidden にマッピングします。関数は role の
トランザクション内で実行されるので、関数自身のチェックと RLS が効きます。
そのため、露出にはガードレールがあります。いずれかを満たさないタグ付き関数は 露出されず、ビルドがそれを大きく報告します (黙って消えることはありません)。
PUBLIC EXECUTE は hard skip
Section titled “PUBLIC EXECUTE は hard skip”CREATE FUNCTION はデフォルトで EXECUTE を PUBLIC に付与します。この付与が
残ったままのタグ付き関数は、誰でも — 匿名 role を含め — エンドポイントを
呼べてしまうため、skip されます。まず締めてください。
REVOKE EXECUTE ON FUNCTION approve_order(uuid) FROM PUBLIC;GRANT EXECUTE ON FUNCTION approve_order(uuid) TO app_manager;意図的に public 呼び出し可能にしたい関数 (例: 匿名検索) は、明示的に宣言します
— COMMENT に @expose: rpc public、または
api.rpc.allowPublicExecute に列挙します。
SECURITY DEFINER は二重 opt-in が必要
Section titled “SECURITY DEFINER は二重 opt-in が必要”SECURITY DEFINER 関数は 所有者 権限で実行され、呼び出し元の RLS を
バイパスしうる — 最も強力で footgun になりやすいケースです。次の 両方 を
満たすときにのみ露出されます。
-
api.rpc.allowDefinerに列挙されて いること (migration 作成者のタグとは別の、operator による deploy 時の opt-in)。かつ -
owner-safe な
search_pathを宣言していること — 各要素がその所有者しか オブジェクトを作成できないスキーマ (例:pg_catalog) で、末尾にpg_tempを 置くこと。CREATE FUNCTION settle_invoice(invoice_id uuid) RETURNS voidLANGUAGE sql SECURITY DEFINERSET search_path = pg_catalog, pg_tempAS $$ … $$;書き込み可能なスキーマを含む
search_path(別の role が、所有者が未修飾で 解決するオブジェクトを仕込めてしまう) は拒否されます。
その他の loud skip
Section titled “その他の loud skip”オーバーロード名 (同一 schema.name が複数)、VARIADIC / polymorphic /
無名引数、マッピング不能な戻り型はビルド issue とともに skip されるので、
タグ付けしたのに現れない関数は必ず理由がわかります。
MCP エージェントからアクションを実行する
Section titled “MCP エージェントからアクションを実行する”describe_functions はエージェントにアクションを 学ばせ、call ツールは
それを 実行 させます — describe → act の「act」側です。これは
opt-in でデフォルト OFF: オペレーターが実行を有効化するまで、MCP サーバーは
describe 専用です。
kozou.config.yaml の
server.mcp.execution で有効化します。
server: mcp: execution: enabled: true role: kozou_mcp_agent # call の実行 role (server.mcp.http.auth なしの場合は必須) # claims: { tenant_id: acme } # 任意。RLS 向けに publish される # allow: [public.approve_order] # 任意の allowlist。省略 = 露出アクション全部実行を有効化すると、bundled kozou mcp サーバーは
{ "function": "<schema>.<fn>", "args": { … } } を取る call ツールを列挙します。
関数は REST サーフェスと同じ施行エンベロープ (SET LOCAL ROLE + publish した
claims をトランザクション内で) で実行されるので、関数の EXECUTE 権限とそれ自身の
RLS が適用されます。call が到達できるのは describe_functions が既に広告している
関数 (および任意の allow リストとの積集合) だけで、それ以外は「unknown function」
として扱われます (露出されていない関数は存在しない関数と区別できません)。
デフォルトは単一サービス role — OAuth で per-user に
Section titled “デフォルトは単一サービス role — OAuth で per-user に”REST サーフェスは リクエストごと の JWT を運ぶので、各呼び出しはそのユーザーの
role で実行されます。server.mcp.http.auth なしでは MCP にエンドユーザーの
トークンがありません: call はすべてのリクエストで 1 つの オペレーター設定
role として実行され、エージェントは role や claims を 選べません (選べれば
self-elevate できてしまうため)。これは意図的に粗い設計です。
least-privilege role を使う
Section titled “least-privilege role を使う”実行 role の EXECUTE 付与と関数の RLS が境界のすべてなので、call には
専用の least-privilege role を与えてください — DB owner や superuser は避け、
エージェントが実行してよい関数にだけ GRANT EXECUTE します。
CREATE ROLE kozou_mcp_agent NOLOGIN;GRANT kozou_mcp_agent TO app_login; -- 接続のログイン roleGRANT EXECUTE ON FUNCTION approve_order(uuid) TO kozou_mcp_agent;SECURITY DEFINER が最も鋭い縁です: definer 関数は 所有者 として実行され、
実行 role の RLS をバイパスするので、call の有効化と definer アクションが揃うと
エージェントは所有者権限の動詞を起動できます。definer 関数はそもそも露出に
allowDefiner の二重 opt-in を要します — 実行を有効化する前にレビューしてください。
エラーとトランスポート
Section titled “エラーとトランスポート”失敗した呼び出しは 汎用的な メッセージの MCP エラー結果を返します — permission
denied、制約カテゴリ、RAISE の場合は “the function call failed”。生の DB エラー文は
エージェントに返しません (サーバーログにのみ出します)。
MCP の HTTP トランスポートはデフォルトでは無認証です: call 有効時、ポートに到達
できる誰もが実行 role としてアクションを実行できるため、サーバーは非 loopback bind 時に
より強い起動警告を出します。127.0.0.1 (デフォルト) のままにするか、前段に
auth/proxy を置くか、OAuth resource-server モード (server.mcp.http.auth) を有効に
してください — 有効時は毎リクエストに検証済み bearer トークンが必要になり、call は
固定 role ではなくトークンごとの role として実行されます (リモート MCP と
OAuth)。call の wire 形は RPC サーフェスの他と同様、
Kozou v1.6 時点で安定した契約です。env-only の standalone @kozou/mcp CLI は
describe 専用のままで、実行は bundled kozou mcp の機能です。
2 つの RPC allowlist は kozou.config.yaml の
api.rpc 配下にあり、デフォルトは空です
(これらが無くても、PUBLIC EXECUTE を revoke した invoker 関数は露出されます)。
api: rpc: allowDefiner: - public.approve_order # schema 修飾 allowPublicExecute: - public.search関数を提供するのは内製の @kozou/api バックエンド (デフォルト) だけです。
外部 PostgREST opt-out では Admin UI は Actions サーフェスを隠し、PostgREST 自身の
/rpc/ を使います。