Metadata-Version: 2.4
Name: firstops
Version: 0.3.0
Summary: Govern MCP, tool calls, and LLM traffic for AI agents — across LangGraph, Claude Agent SDK, OpenAI Agents, and Google ADK.
Project-URL: Homepage, https://firstops.dev
Project-URL: Documentation, https://github.com/firstops-dev/firstops-python
Project-URL: Repository, https://github.com/firstops-dev/firstops-python
Project-URL: Issues, https://github.com/firstops-dev/firstops-python/issues
Author-email: FirstOps <dev@firstops.dev>
License-Expression: MIT
License-File: LICENSE
Keywords: agent,claude,dpop,google-adk,governance,guardrails,langchain,langgraph,llm,mcp,openai-agents,security
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Security
Classifier: Topic :: Software Development :: Libraries
Requires-Python: >=3.10
Requires-Dist: cryptography>=42.0
Requires-Dist: httpx>=0.27
Provides-Extra: adk
Requires-Dist: google-adk[extensions]; extra == 'adk'
Provides-Extra: all
Requires-Dist: claude-agent-sdk; extra == 'all'
Requires-Dist: google-adk[extensions]; extra == 'all'
Requires-Dist: langchain-mcp-adapters; extra == 'all'
Requires-Dist: langchain-openai; extra == 'all'
Requires-Dist: langchain>=1.0; extra == 'all'
Requires-Dist: langgraph; extra == 'all'
Requires-Dist: openai-agents; extra == 'all'
Provides-Extra: claude
Requires-Dist: claude-agent-sdk; extra == 'claude'
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Provides-Extra: langgraph
Requires-Dist: langchain-mcp-adapters; extra == 'langgraph'
Requires-Dist: langchain-openai; extra == 'langgraph'
Requires-Dist: langchain>=1.0; extra == 'langgraph'
Requires-Dist: langgraph; extra == 'langgraph'
Provides-Extra: openai
Requires-Dist: openai-agents; extra == 'openai'
Description-Content-Type: text/markdown

# FirstOps Python SDK

Govern what your AI agents do. FirstOps applies identity, policy enforcement, credential brokering, and audit to every **LLM call**, **tool call**, and **MCP call** your agent makes — across LangGraph, the Claude Agent SDK, the OpenAI Agents SDK, and Google ADK, or any custom loop.

## Install

```bash
pip install "firstops[langgraph]"   # LangGraph + LangChain
pip install "firstops[claude]"      # Claude Agent SDK
pip install "firstops[openai]"      # OpenAI Agents SDK
pip install "firstops[adk]"         # Google ADK
pip install "firstops[all]"         # all of the above
pip install firstops                # core only (management client / custom loops)
```

Python 3.10+. Core deps are just `cryptography` and `httpx` — your agent framework comes in via the extra you pick.

---

## What FirstOps governs

| Surface | How it's wired | What you get |
|---|---|---|
| **LLM calls** | point the model `base_url` at the local sidecar | inspect prompts/responses, scrub PII, block, audit |
| **Tool calls** | one adapter (or `@firstops.tool`) | block / rewrite args / audit — including framework built-ins |
| **MCP servers** | point the MCP client at the local proxy | server-side policy + **credential brokering** (the agent never holds the upstream token) |

Every action is evaluated by FirstOps and returns `allow` / `deny` / `modify` — your agent logic doesn't change.

## Quick start (LangGraph)

```python
import firstops
from firstops.integrations.langgraph import FirstOpsMiddleware
from langchain.agents import create_agent
from langchain_openai import ChatOpenAI

fo = firstops.init(
    agent_id="<agent-uuid>",                      # from the FirstOps dashboard
    private_key_pem=open("agent-key.pem").read(),
)

# Route the LLM through FirstOps; wire one middleware to govern every tool call.
llm = ChatOpenAI(model="gpt-4o-mini", base_url=firstops.llm_base_url("openai"), api_key="sk-...")
agent = create_agent(model=llm, tools=[...], middleware=[FirstOpsMiddleware(fo)])

agent.invoke({"messages": [{"role": "user", "content": "..."}]})
```

The whole integration is `init()` + a `base_url` swap + one middleware. See [`examples/`](examples/) for runnable agents, including MCP.

## Other harnesses

**Claude Agent SDK** — one `PreToolUse` hook governs every tool (built-ins, MCP, custom):

```python
from claude_agent_sdk import query, ClaudeAgentOptions
from firstops.integrations.claude import firstops_hooks

options = ClaudeAgentOptions(hooks=firstops_hooks(fo), permission_mode="bypassPermissions")
async for _ in query(prompt="...", options=options):
    pass
```

**OpenAI Agents SDK** — a guardrail per tool + the model routed through the sidecar:

```python
from agents import Agent, function_tool, set_default_openai_client
from firstops.integrations.openai_agents import firstops_tool_input_guardrail
from openai import AsyncOpenAI

set_default_openai_client(AsyncOpenAI(base_url=firstops.llm_base_url("openai"), api_key="sk-..."))
guard = firstops_tool_input_guardrail(fo)

@function_tool(tool_input_guardrails=[guard])
def send_email(to: str, body: str) -> str: ...
```

**Google ADK** — one `before_tool_callback` governs every tool (block + rewrite args):

```python
from google.adk.agents import LlmAgent
from firstops.integrations.google_adk import firstops_before_tool_callback

agent = LlmAgent(name="assistant", model=..., tools=[...],
                 before_tool_callback=firstops_before_tool_callback(fo))
```

**Any framework / custom loop** — the base API:

```python
@firstops.tool          # govern any callable: block / scrub args / audit
def send_email(to: str, body: str): ...
```

## MCP servers

Point your MCP client at the local proxy; FirstOps brokers the upstream credentials.

```python
from langchain_mcp_adapters.client import MultiServerMCPClient

mcp = MultiServerMCPClient({"notion": {"url": firstops.mcp_url("<connection-id>"), "transport": "streamable_http"}})
tools = await mcp.get_tools()
```

## Examples

Runnable agents in [`examples/`](examples/) — each governs the LLM and tool calls; the `*_mcp` variants add a Notion MCP server:

- LangGraph — [`langgraph_basic.py`](examples/langgraph_basic.py), [`langgraph_notion_mcp.py`](examples/langgraph_notion_mcp.py)
- Claude Agent SDK — [`claude_sdk_basic.py`](examples/claude_sdk_basic.py), [`claude_sdk_mcp.py`](examples/claude_sdk_mcp.py)
- OpenAI Agents SDK — [`openai_agents_basic.py`](examples/openai_agents_basic.py), [`openai_agents_mcp.py`](examples/openai_agents_mcp.py)
- Google ADK — [`google_adk_basic.py`](examples/google_adk_basic.py)

See [`examples/README.md`](examples/README.md) for the env vars to run them.

## Management client

Provision agents and connections from your backend:

```python
from firstops import FirstOps

admin = FirstOps(api_key="fo_key_...")
agent = admin.agents.create(name="research-bot")      # -> id + private_key (shown once)
admin.connections.register(principal_id=agent.id, name="slack", upstream_url="https://mcp.slack.com/sse")
```

## How it works

`firstops.init()` starts a local sidecar and establishes the agent's identity (a DPoP-bound principal — RFC 9449). Tool and LLM actions are forwarded to the FirstOps gateway, which evaluates them against your policies and returns the verdict; MCP and LLM traffic flow through the sidecar with credentials brokered. Enforcement fails open on infrastructure errors; authentication fails closed.

## Documentation

- [Docs home](https://firstops.dev/docs)
- Guides: [LangChain / LangGraph](https://firstops.dev/docs/guides/langchain) · [Claude Agent SDK](https://firstops.dev/docs/guides/claude-sdk) · [OpenAI Agents SDK](https://firstops.dev/docs/guides/openai-agents) · [Google ADK](https://firstops.dev/docs/guides/google-adk)
- Concepts: [Identity](https://firstops.dev/docs/concepts/identity) · [Enforcement](https://firstops.dev/docs/concepts/enforcement) · [Connections](https://firstops.dev/docs/concepts/connections)
- [Repository](https://github.com/firstops-dev/firstops-python)

## License

MIT
