Skip to main content

Govern MCP, tool calls, and LLM traffic for AI agents — across LangGraph, Claude Agent SDK, OpenAI Agents, and Google ADK.

Project description

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

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)

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/ for runnable agents, including MCP.

Other harnesses

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

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:

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):

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:

@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.

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/ — each governs the LLM and tool calls; the *_mcp variants add a Notion MCP server:

See examples/README.md for the env vars to run them.

Management client

Provision agents and connections from your backend:

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

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

firstops-0.3.0.tar.gz (71.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

firstops-0.3.0-py3-none-any.whl (38.1 kB view details)

Uploaded Python 3

File details

Details for the file firstops-0.3.0.tar.gz.

File metadata

  • Download URL: firstops-0.3.0.tar.gz
  • Upload date:
  • Size: 71.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for firstops-0.3.0.tar.gz
Algorithm Hash digest
SHA256 6b5271845ac7db4baf8d7c415947219c7dd07fcb18744220d6cd5cb073968339
MD5 875cc31c00177d1e0f0b022582473ec9
BLAKE2b-256 6ede4005bbefa3b4076d8de8cbdabbfacfb13ab07d393e83bc97e74c68c74b85

See more details on using hashes here.

Provenance

The following attestation bundles were made for firstops-0.3.0.tar.gz:

Publisher: publish.yml on firstops-dev/firstops-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file firstops-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: firstops-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 38.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for firstops-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 5967e6f3b8cbc5e2c40f4c6069ba3d2fefe22f0b68d35d7ab7c6d0716a50d74c
MD5 e5a66e13ea39ddb6fa9063262235250c
BLAKE2b-256 43a13586f9390a328c6e4243ed79d7d404c92e66a5286d075c43ffa23bcdab16

See more details on using hashes here.

Provenance

The following attestation bundles were made for firstops-0.3.0-py3-none-any.whl:

Publisher: publish.yml on firstops-dev/firstops-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page