Metadata-Version: 2.5
Name: ory-openai-agents
Version: 1.9.0
Summary: Ory Agent Security for the OpenAI Agents SDK — per-tool authorization, activity logging, and identity propagation via a tool-input guardrail. Built on ory-argus.
Author: Ory
License-Expression: Apache-2.0
Keywords: agent,agents,ai,authorization,openai,ory,permissions
Requires-Python: >=3.10
Requires-Dist: openai-agents>=0.7
Requires-Dist: ory-argus<2,>=1
Provides-Extra: dev
Requires-Dist: pytest>=8; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Description-Content-Type: text/markdown

# ory-openai-agents

Ory Agent Security for the [OpenAI Agents SDK](https://openai.github.io/openai-agents-python/).

Authorizes every tool call against Ory Permissions via a **tool-input guardrail**, records
invocations, and propagates user → agent identity — built on
[`ory-argus`](https://pypi.org/project/ory-argus/).

```bash
pip install ory-openai-agents
```

```python
from uuid import uuid4

from agents import Agent, Runner, function_tool
from ory_argus import OryAgentClient, with_hook_context
from ory_openai_agents import ory_tool_input_guardrail, ory_run_hooks

ory = OryAgentClient.from_env("openai-agents")
ory_guardrail = ory_tool_input_guardrail(client=ory)

@function_tool(tool_input_guardrails=[ory_guardrail])
def search(query: str) -> str:
    """Search the application's approved data source."""
    return search_backend(query)

agent = Agent(
    name="assistant",
    tools=[search],
)
with with_hook_context(ory, session_id=uuid4().hex):
    status = "error"
    try:
        result = Runner.run_sync(agent, "…", hooks=ory_run_hooks(client=ory))
        status = "ok"
    finally:
        ory.logger.activity(
            "session.end",
            status,
            attributes={"harness": "openai-agents"},
        )
        ory.flush_events(timeout=3)
```

Attach the guardrail to every side-effecting `FunctionTool`; installing the package alone
does not gate tools. Hosted and built-in tools do not pass through this function-tool
guardrail. Reuse one `OryAgentClient` for guardrails and run hooks.
Scope each Runner invocation with `with_hook_context` so session, tool, and completion events
share one Agent Security session ID. Prefer a stable job or conversation ID supplied by the
deployment controller; generate a new opaque ID when each invocation is a distinct session.

### MCP servers

Tools listed from an `MCPServer` are checked as `McpServer:<server>` and
`McpTool:<server>:<tool>` instead of plain tools when the server carries the MCP guardrail:

```python
from agents.mcp import MCPServerStdio, MCPServerStreamableHttp
from ory_openai_agents import guard_mcp_server

github = guard_mcp_server(
    MCPServerStreamableHttp(params={"url": "https://api.githubcopilot.com/mcp/"}, name="github"),
    client=ory,
)
playwright = guard_mcp_server(
    MCPServerStdio(params={"command": "npx", "args": ["-y", "@playwright/mcp"]}),
    client=ory,
)
agent = Agent(name="assistant", mcp_servers=[github, playwright])
```

`guard_mcp_server` appends `ory_mcp_tool_input_guardrail(server)` to the server's
`tool_input_guardrails`, which the SDK copies onto every tool it lists from that server; do
this before the run lists tools. Each call reports the name you gave the server, the server's raw tool name (the
`mcp_<server>__` prefix that `include_server_in_tool_names` adds is removed), and hashed
fingerprints of the URL host or launched package, so Agent Security matches the server to its
catalog whatever it is named. The SDK's default names (`stdio: <command>`,
`streamable_http: <url>`) embed the launch command or URL, so an unnamed server is reported as
`unknown` and identified only by its fingerprints; name servers explicitly (`github`) to see
them by name in Observe. Fingerprints are hashes; no URL, command, or argument is sent. A plain
`ory_tool_input_guardrail()` attached to an MCP server still gates its tools, but as plain
tools.

- **observe** (default): denied tools still run; a `permission.observe_deny` activity event is recorded.
- **enforce**: denied tools are rejected — the model receives
  the denial and the tool never executes.

Credentials are read from the shared `~/.config/ory-agent-plugins/config.json`, so a login
from any Ory harness or `ory-argus login` is reused automatically.

For a headless Agent Security deployment, inject `ORY_PROJECT_URL`,
`ORY_AGENT_SECURITY_URL`, and `ORY_AGENT_API_KEY` into the Agent process. Startup resolves
the administrator-provisioned subject and opaque root from Agent Security, skips browser
login, and keeps that context only in memory. `ORY_AGENT_SUBJECT_ID` and
`ORY_AGENT_ROOT_DELEGATION` are compatibility overrides for older packages. The
application still supplies the OpenAI Agents type; do not inject `ORY_USER_OAUTH2_TOKEN`.
