Provenza について
Provenzaは、AIエージェントの操作を統制するPython SDKとゲートウェイで、権限とポリシーを確認し、署名付きの監査記録を作成します。ツール利用の承認と追跡が必要なチーム向けで、MCPクライアントはゲートウェイ経由で管理者承認済みのツールにアクセスできます。
主な機能
- 01エージェントの権限とポリシーに基づく呼び出し確認
- 02検証可能な署名付き監査記録
- 03人の承認を待つ呼び出しの保留
- 04スタンドアロン時の許可ツールへの制限
- 05HTTPとAIプロバイダーのリクエスト統制
- 06MCP経由での承認済みツール公開
活用シーン
- 01エージェントの機密操作に事前審査を要求
- 02エージェントのツール呼び出し記録を追跡・検証
- 03組織が承認したツールだけにエージェントを制限
- 04ポリシー確認を経るアクセス経路にMCPクライアントを接続
MCP 対応
MCPクライアントはProvenzaのゲートウェイに接続し、管理者承認済みで呼び出しがポリシー確認・記録されるツールを利用できます。`provenza-mcp`コマンドは、ローカルプロセスを起動するクライアント向けにstdio経由でゲートウェイを提供します。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
uvx provenzaPROVENZA_URLYour self-hosted Provenza, e.g. https://provenza.example.comPROVENZA_TOKENシークレットOne-time enrollment token (Agents -> New agent); needed on the first start onlyPROVENZA_AGENT_NAMEAgent name on this machine (default: mcp)ローカルサーバーにはマシン上の実行環境(Node.js 18 以上など)が必要です。設定ファイルの場所はクライアントごとに異なります。
ツール
ローカルサーバーのツールはマシン上で起動して初めて取得できます。メンテナーはまだツール一覧を提供していません。
詳細
Provenza Python SDK
AI エージェントのための、ガバナンスが適用され、署名され、監査可能なアクションです。
pip install provenza
from provenza import Agent
agent = Agent("support-bot") # reads PROVENZA_URL and PROVENZA_TOKEN
@agent.tool(name="zendesk.read")
def open_tickets(status: str = "open") -> list:
return zendesk.tickets.list(status=status)
open_tickets() # checked by Provenza, run, recorded and signed
呼び出し時の動作
接続モード(PROVENZA_URL が設定されている場合):
-
SDK は、この呼び出しを実行してよいかサーバーに問い合わせます。サーバーは、 エージェントの権限、組織のポリシー、Tool Registry、 動作、およびサーキットブレーカーを確認します。
-
呼び出しが許可された場合にのみ、ツールが実行されます。許可されない場合:
- 拒否された呼び出しは
ProvenzaDenied(PermissionError)を発生させます。 - 人による判断が必要な呼び出しは
ProvenzaApprovalRequiredを発生させます。
どちらの場合も、試行は記録されます。保留中の呼び出しを担当者が承認したら、 まったく同じ呼び出しをもう一度実行してください。1 時間以内に 1 回実行されます。
try: send_report(to="[email protected]") except ProvenzaApprovalRequired as held: if agent.wait_for_approval(held.action_id)["decision"] == "approved": send_report(to="[email protected]") # the same call: runs once - 拒否された呼び出しは
-
結果は記録され、エージェント自身の鍵で署名されます。Provenza は 署名付き監査ログに封印するため、オフラインで検証できる レシートをダウンロードできます。
SDK はフェイルクローズ方式です。サーバーに接続できない場合、ツールは実行されず、
SDK は ProvenzaUnavailable を発生させます。fail_open=True を渡すと、
ツールを実行し、未チェックの呼び出しとしてローカルに記録します。
スタンドアロンモード(サーバーなし)では、呼び出しが実行され、
署名付きのハッシュチェーン監査ログにローカルで書き込まれます。
allowed_tools=[...] を使って実行を制限できます。
ローカル監査ログに書き込まれるのは、引数と結果のハッシュのみであり、 内容そのものは記録されません。
ID の取得
管理者が Provenza(Agents → New agent)でワンタイム登録トークンを発行します。エージェントの鍵は自身のマシン上で 生成され、公開鍵のみがサーバーに送信されます。
export PROVENZA_URL=https://provenza.example.com
export PROVENZA_TOKEN=pvz_enr_... # used once; afterwards the identity file is enough
ID は ~/.config/provenza/agents/<name>.json(モード
600)に保存されます。他のユーザーが読み取り可能な場合、SDK はその読み込みを拒否します。これは
tools/provenza_sign.py enroll が書き込むファイルと同じなので、CLI で登録して
SDK で実行できます。
秘密鍵を OS のキーチェーンに保存するには、次のようにします。
pip install 'provenza[keychain]'
Agent("bot", key_store="keychain")
耐量子ハイブリッド鍵(Ed25519 + ML-DSA-65)を使うには、次のようにします。
pip install 'provenza[pq]'
Agent("bot", hybrid=True)
agent.fingerprint は、エージェントの署名を検証する人に伝える値です。
その他
with agent.chain(chain_id): # act inside a delegation chain
open_tickets()
agent.delegate(other_agent_id, "summarize Q3", ["read"], tools=["db.read"], expires_in=600)
chk = agent.check("files.delete", {"path": p}) # ask for something that is not a decorated tool
if chk.allowed:
delete(p)
agent.record(chk, {"deleted": True})
非同期ツール(async def)も同じように動作します。呼び出しが実行されたものの
サーバーに記録できなかった場合(呼び出し中にサーバーが停止した場合)、SDK は結果を
隠しません。ローカル監査ログに署名付きのギャップエントリを書き込み、
ProvenzaWarning を発行します。
レイテンシー
ガバナンスが適用された呼び出しにかかる時間は、SDK 自体の処理時間とサーバーへの 往復通信時間です。SDK 自体の処理時間は、2026-10-11 にインプロセスサーバー(ネットワークなし)を 使って測定しました。
record_mode | ツールの戻り値が返るまでの往復通信回数 | 中央値 | p95 |
|---|---|---|---|
"sync"(デフォルト) | 2(チェック、記録) | 0.42 ms | 0.59 ms |
"background" | 1(チェック) | 0.15 ms | 0.33 ms |
これにネットワークの往復時間(×2 または ×1)と、サーバーのチェック処理時間が加わります。
"sync" では、ツールが戻る時点で記録がサーバーに保存されています。
"background" では、キューから順番に記録が送信されます。送信に失敗すると、
同期モードと同様に、ローカル監査ログに署名付きのギャップが記録されます。
flush() / aflush() はキューの処理を待ち、close() とインタープリターの
終了時にはキューを空にします。プロセスが強制終了されると、キューに残っているものは失われます。
待機しないことの代償です。
拒否は例外が発生する前に必ず記録されるため、
ProvenzaDenied.action_id はすぐに確認できます。
自動インストルメンテーション
import provenza.auto
provenza.auto.instrument(agent) # httpx (sync and async) and requests
このプロセスから AI プロバイダーへの直接の呼び出しはすべて、ガバナンス対象のアクションになります。対象となるのは、OpenAI、Anthropic、Mistral、Cohere、Gemini の SDK、LangChain のプロバイダークライアント、または通常の HTTP です。プロバイダーホストは、組織の AI ドメインカタログ(Shadow AI)から取得され、各呼び出しをサーバーが判定します。
| カタログのステータス | 結果 |
|---|---|
blocked | 拒否されます。インシデント ai_domain_blocked が記録されます |
allowed | 送信されます。ただし、ポリシーによって拒否される場合を除きます |
| 未分類 | 送信されます。ただし、ポリシーによって拒否される場合を除きます。管理者が分類できるよう記録されます |
記録されるのはホスト、メソッド、パス(クエリ文字列を除く)、および本文のハッシュとサイズのみです。プロンプトは記録されません。
拒否は HTTP の応答として返されます。ステータスコードは 403 で、OpenAI 形式のエラー本文と x-should-retry: false が含まれます。そのため、プロバイダー SDK は通常の権限エラーを Provenza の理由とともに発生させ、再試行しません。サーバーに接続できない場合、何も送信されず(503)、その他のホストと Provenza 自体への呼び出しには影響しません。provenza.auto.uninstrument() を実行すると、すべて元に戻ります。
エグレスプロキシ
Provenza のエグレスプロキシ(リポジトリ内の proxy/)を介する場合、エージェントには 2 つの選択肢があります。キーを送信してプロキシにすべてのリクエストを検査させるか、エージェント自身でリクエストを検査して検査 ID を送信できます。
body = json.dumps({"amount": 1000}).encode()
chk = agent.proxy_check("stripe", "POST", "/v1/charges", body=body)
if chk.allowed:
httpx.post("http://127.0.0.1:8443/stripe/v1/charges", content=body, headers=chk.proxy_headers)
path、query、body は、実際に送信する内容と完全に一致している必要があります。プロキシは、この検査に関連付けられていないリクエストを拒否します。プロキシがリクエストを記録するため、record() には渡しません。
agent.proxy_request_headers(...)(同じ引数)は、送信するヘッダー(検査用ヘッダー、または Provenza に接続できない間はエージェントの署名付きリクエストのみ)を返します。署名付きポリシーを適用するプロキシは、ローカルのキルスイッチで停止されるまで、署名付きリリースのみを基準にリクエストを判定します。Provenza は復旧後にリクエストを記録します(docs/pvp/edge.md のセクション 6)。
Provenza 経由の HTTP ツール
Provenza に登録された API(Gateways → HTTP tools)は Provenza 経由で呼び出されるため、エージェントがキーを保持することはありません。呼び出しは検査、実行、記録が一度に行われます。認証情報を必要とするツールはエグレスプロキシ経由で呼び出され、認証情報はプロキシが保持します。Provenza 自体は認証情報を保持しません。
agent = Agent("billing-bot", egress_proxy="http://127.0.0.1:8443") # or PROVENZA_EGRESS_PROXY
out = agent.invoke("crm.set_tier", {"id": "42", "tier": "gold"})
out["status"], out["body"] # a refusal raises ProvenzaDenied
agent.http_tools() # names, descriptions and input schemas, for the model
プロキシ経由では HTTP リクエスト自体にも署名が付与され(X-Provenza-Request)、強制モードのプロキシはすべてのパスで署名を必須とします。標準の OpenAI クライアントや MCP クライアントなど、署名できないクライアントの場合は、この ID ファイルを使ってエージェントのそばでプロキシのエージェントモードを実行してください(provenza-proxy -agent-identity …、proxy/README.md)。
エグレスの自己検査
このホストは Provenza のエグレスプロキシに制限され、唯一の外部接続手段になっていますか? 起動時と、その後も定期的に検査を実行してください。Require confined egress が有効な場合、エージェントは最新の検査結果が confined のときにのみ動作します。docs/egress-lockdown.md を参照してください。
agent.egress_selfcheck() # {"verdict": "confined" | "open", ...}
ツール用許可証
許可された検査には、組織が署名した許可証が含まれます。エージェントは呼び出しとともにこれを送信します。
chk = agent.check("crm.update", payload)
if chk.allowed:
httpx.post("https://crm-tool.internal/update", json=payload, headers=chk.permit_headers)
agent.record(chk, output)
ツールは、この呼び出しに正確に対応する有効な許可証がなければ、何も実行しません。
from provenza.permit import PermitMiddleware, PermitVerifier, require_permit
verifier = PermitVerifier("https://provenza.example.com", org="acme")
app = PermitMiddleware(app, verifier, tool="crm.update") # any ASGI app: FastAPI, Starlette, ...
@require_permit(verifier, "db.read") # or a plain function
def read(query: str): ...
検証器は、組織の JWKS に対する署名、ツール、JSON 本文のハッシュ(検査済みの入力である必要があります)、有効期限を確認します。続いて Provenza で許可証を引き換えるため、ツールのすべてのレプリカを通じて一度だけ使用されます。Provenza に接続できない場合は拒否されます。
統合
各フレームワークはオプションの追加機能です。SDK がフレームワークを必要とすることはありません。crewai
とautogen-coreでさえ、互換性のないバージョンのprotobufを必要とします。
Provenza のゲートウェイ経由。 呼び出しはサーバー経由で行われ、サーバーがプロバイダーの認証情報を保持するため、エージェントのコードの動作にかかわらず適用されます。
# pip install 'provenza[openai]'
from provenza.integrations.openai import gateway_client, wrap
client = gateway_client(agent) # an openai.OpenAI pointed at the LLM gateway, with the agent's key
client = wrap(existing_client, agent) # or a copy of one you already configured
# MCP: no extra needed. Point any MCP client at Provenza instead of at the servers.
from provenza.integrations.mcp import connection, client_config
conn = connection(agent) # {"url": ".../api/v1/mcp", "headers": {...}}
ローカルコマンドを起動する MCP クライアント(Claude Desktop、Claude Code、
Cursor、Windsurf、Gemini CLI、VS Code):provenza-mcpは stdio 経由で同じゲートウェイを利用します。
Provenza で登録トークンを発行し(Agents → New agent)、次を追加します。
{"mcpServers": {"provenza": {
"command": "uvx", "args": ["provenza-mcp"],
"env": {"PROVENZA_URL": "https://provenza.example.com", "PROVENZA_TOKEN": "pvz_enr_..."}}}}
初回起動時にエージェントが登録されます。キーはそのマシン上で生成されるため、その後トークンを削除できます。クライアントに表示されるのは管理者が承認したツールのみで、すべての呼び出しは組織のポリシーに照らして確認され、記録されます。1 台のマシンで複数のエージェントを実行するには、PROVENZA_AGENT_NAMEを設定します。
LLM ゲートウェイ経由では、サーバーが許可されたモデルとプロバイダー、ポリシー、制限を確認します。プロンプトや回答は保存しません。組織がルーティングしたすべてのモデル(OpenAI、Anthropic、Azure、…)に、この OpenAI 互換の単一エンドポイントを通じてアクセスできます。ストリーミングはまだサポートされていません。
ツールラッパー。 これらはツール名、説明、引数スキーマを維持するため、モデルから見た違いはありません。
# pip install 'provenza[langchain]'
from provenza.integrations.langchain import govern_tools
tools = govern_tools(agent, [search, read_file]) # a refusal reaches the model as the tool's answer
# pip install 'provenza[autogen]'
from provenza.integrations.autogen import govern_tools
tools = govern_tools(agent, [FunctionTool(lookup, description="...")])
# pip install 'provenza[crewai]'
from provenza.integrations.crewai import govern_tools
researcher = crewai.Agent(role="...", goal="...", tools=govern_tools(agent, [search_tool]))
prefix=はポリシーで認識される名前に名前空間を付与するため、prefix="kb."を指定すると、searchはkb.searchになります。それ以外の場合は、agent.call(name, arguments, lambda: ...)またはawait agent.acall(...)を使って単一の呼び出しを管理できます。
これは何であり、何ではないか
SDK はエージェントのプロセス内で実行されます。これはエージェントの正確な記録であり、協調的な適用手段です。同じプロセス内のコードは SDK を介さずにツールを呼び出すことも、エージェントのキーを読み取ることもできます。エージェントのコードに依存しない適用は、Provenza のゲートウェイ(LLM、MCP、A2A)によって行われます。ゲートウェイが認証情報を保持するため、エージェントがそれらを持つことはありません。
provenza.audit_log.verify(path, public_key)でローカルの監査ログを検証します。
tools/provenza_audit.py receiptでサーバーのレシートを検証します。
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。