{"id":50539109,"url":"https://github.com/getclyro/clyro","last_synced_at":"2026-06-03T19:00:30.664Z","repository":{"id":345742467,"uuid":"1187082054","full_name":"getclyro/clyro","owner":"getclyro","description":"Clyro is a governance platform for AI agents. While most tools let you watch agents fail, Clyro stops failures before they happen — catching infinite loops, runaway costs, and policy violations in real time.","archived":false,"fork":false,"pushed_at":"2026-05-15T06:26:36.000Z","size":682,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-05-15T08:05:58.514Z","etag":null,"topics":["agents","ai","ai-governance","anthropic","claude","claude-code","claude-hooks","crewai","drift-detection","governance","guardrails","langchain","langgraph","mcp","python"],"latest_commit_sha":null,"homepage":"https://clyro.dev/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/getclyro.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":"AUTHORS","dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-20T10:16:55.000Z","updated_at":"2026-05-15T06:26:41.000Z","dependencies_parsed_at":"2026-05-15T08:02:51.317Z","dependency_job_id":null,"html_url":"https://github.com/getclyro/clyro","commit_stats":null,"previous_names":["getclyro/clyro"],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/getclyro/clyro","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/getclyro%2Fclyro","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/getclyro%2Fclyro/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/getclyro%2Fclyro/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/getclyro%2Fclyro/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/getclyro","download_url":"https://codeload.github.com/getclyro/clyro/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/getclyro%2Fclyro/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33876333,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-03T02:00:06.370Z","response_time":59,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agents","ai","ai-governance","anthropic","claude","claude-code","claude-hooks","crewai","drift-detection","governance","guardrails","langchain","langgraph","mcp","python"],"created_at":"2026-06-03T19:00:26.738Z","updated_at":"2026-06-03T19:00:30.656Z","avatar_url":"https://github.com/getclyro.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Clyro SDK\n\n[![PyPI version](https://img.shields.io/pypi/v/clyro.svg)](https://pypi.org/project/clyro/)\n[![Python Version](https://img.shields.io/badge/python-3.11%2B-blue.svg)](https://www.python.org/downloads/)\n[![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE)\n[![CI](https://github.com/getclyro/clyro/actions/workflows/ci.yml/badge.svg)](https://github.com/getclyro/clyro/actions/workflows/ci.yml)\n\n**Runtime governance for AI agents — prevent failures before they happen.**\n\nOne `pip install`, three tools:\n\n| Component | What it does | CLI |\n|-----------|-------------|-----|\n| **SDK** | Wrap any Python agent with tracing, cost limits, loop detection, and policy enforcement | `clyro-sdk` |\n| **MCP Wrapper** | Govern MCP tool calls in Claude Desktop, Cursor, and VS Code | `clyro-mcp` |\n| **Claude Code Hooks** | Block destructive commands (rm -rf, DROP TABLE) in Claude Code sessions | `clyro-hook` |\n\n## What is Clyro?\n\nClyro is a governance platform for AI agents. While most tools let you watch agents fail, Clyro stops failures before they happen, catching infinite loops, runaway costs, and policy violations in real time.\n\n**Works fully offline.** No API key required. Install, wrap, and get governance immediately with local YAML policies. Optionally connect to Clyro Cloud for team dashboards, shared policies, and session replay.\n\nThe SDK is the integration layer: add `clyro.wrap()` to any Python agent and you get execution tracing, cost tracking, step limits, loop detection, and policy enforcement — all with zero changes to your agent logic. If the SDK encounters an error, it fails open — your agent keeps running.\n\n## Features\n\n- **Works offline**: Local mode with YAML policies — no cloud dependency\n- **5 framework adapters**: LangGraph, CrewAI, Claude Agent SDK, Anthropic SDK, Generic\n- **Prevention Stack**: Step limits, cost limits, loop detection, business logic guardrails\n- **Policy enforcement**: 8 operators, block/allow/require_approval, per-rule fail-open\n- **Cost tracking**: Automatic LLM cost calculation for OpenAI and Anthropic models\n- **MCP governance**: JSON-RPC proxy for Claude Desktop, Cursor, VS Code\n- **Claude Code hooks**: PreToolUse/PostToolUse governance for Bash, Edit, Write\n- **Minimal dependencies**: 6 lightweight packages — no heavy ML frameworks, no vendor lock-in\n- **Fail-open design**: SDK failures never break your agent\n\n## Quick Start\n\n### Installation\n\n```bash\npip install clyro\n```\n\n### 1. SDK — Wrap any Python agent\n\n```python\nimport clyro\nfrom clyro import ClyroConfig, ExecutionControls\n\n# No API key needed — runs in local mode automatically\nwrapped = clyro.wrap(\n    your_agent,\n    config=ClyroConfig(\n        agent_name=\"my-agent\",\n        controls=ExecutionControls(\n            max_steps=50,\n            max_cost_usd=2.0,\n            enable_loop_detection=True,\n            enable_policy_enforcement=True,\n        ),\n    ),\n)\n\n# Run normally — governance enforced, session summary printed at end\nresult = wrapped.invoke({\"messages\": [{\"role\": \"user\", \"content\": \"Hello\"}]})\n```\n\n### 2. MCP Wrapper — Govern MCP tool calls\n\n```bash\n# Create config\ncat \u003e mcp_governance.yaml \u003c\u003c 'EOF'\npolicies:\n  - name: block-dangerous-commands\n    rules:\n      - tool_name: Bash\n        conditions:\n          - field: command\n            operator: contains\n            value: \"rm -rf\"\n        decision: block\n        message: \"Destructive command blocked\"\nEOF\n\n# Wrap any MCP server\nclyro-mcp wrap --config mcp_governance.yaml -- npx @modelcontextprotocol/server-filesystem /tmp\n```\n\n### 3. Claude Code Hooks — Govern Claude Code\n\n```json\n// In Claude Desktop settings.json\n{\n  \"hooks\": {\n    \"PreToolUse\": [{\n      \"type\": \"command\",\n      \"command\": \"clyro-hook evaluate\"\n    }]\n  }\n}\n```\n\n### Local YAML Policies\n\nCreate `~/.clyro/sdk/policies.yaml`:\n\n```yaml\nversion: 1\ndefault_action: allow            # required; decision when no rule matches\n\nactions:\n  llm_call:\n    policies:\n      - name: cost-cap\n        parameter: cost\n        operator: max_value      # matches when cost \u003e 5.0\n        value: 5.0\n        action: block            # matched → block (action is required)\n\n  tool_call:\n    policies:\n      - name: block-dangerous-tool\n        parameter: tool_name\n        operator: equals         # matches when tool_name == \"delete_database\"\n        value: \"delete_database\"\n        action: block\n```\n\nBoth `default_action` (root) and per-rule `action` are **required**. Each rule\nfires its `action` when its condition matches; `default_action` is the\nfallback when no rule matches.\n\n### Connect to Cloud (optional)\n\n```python\n# Add API key to enable cloud features: dashboards, team policies, session replay\nconfig = ClyroConfig(\n    api_key=os.environ.get(\"CLYRO_API_KEY\"),  # Get from clyro.dev\n    agent_name=\"my-agent\",\n    controls=ExecutionControls(max_steps=50, max_cost_usd=2.0),\n)\n```\n\n\u003e **Cloud mode: the dashboard's `default_action` always wins.** When you set\n\u003e an `api_key` (cloud mode), the cloud dashboard's `default_action` is\n\u003e authoritative — your local YAML's `default_action` is treated as a\n\u003e fallback that applies only when the agent has no cloud policies attached\n\u003e (or the policy fetch fails). This is **cloud-wins** precedence: a\n\u003e centrally-mandated default cannot be silently overridden by an\n\u003e out-of-date local config.\n\u003e\n\u003e This applies to the `default_action` fallback only. Explicit local rules\n\u003e (rules whose conditions match) still fast-fail pre-flight as before.\n\u003e To honor the local YAML's `default_action`, run without `api_key`\n\u003e (local-only mode).\n\n## Configuration\n\n### Environment Variables\n\n```bash\nexport CLYRO_API_KEY=\"your-clyro-api-key\"\nexport CLYRO_ENDPOINT=\"https://api.clyro.dev\"\nexport CLYRO_AGENT_NAME=\"my-agent\"\nexport CLYRO_MAX_STEPS=\"50\"\nexport CLYRO_MAX_COST_USD=\"10.0\"\n```\n\n```python\nfrom clyro import ClyroConfig\n\nconfig = ClyroConfig.from_env()\nclyro.configure(config)\n```\n\n### Programmatic Configuration\n\n```python\nfrom clyro import ClyroConfig, ExecutionControls\n\nconfig = ClyroConfig(\n    # Authentication\n    api_key=os.environ.get(\"CLYRO_API_KEY\"),\n    endpoint=\"https://api.clyro.dev\",\n\n    # Agent identification\n    agent_name=\"my-production-agent\",\n\n    # Execution controls\n    controls=ExecutionControls(\n        max_steps=50,\n        max_cost_usd=5.0,\n        loop_detection_threshold=3,\n        enable_step_limit=True,\n        enable_cost_limit=True,\n        enable_loop_detection=True,\n    ),\n\n    # Local storage\n    local_storage_path=\"~/.clyro/traces.db\",\n    local_storage_max_mb=100,\n\n    # Sync settings\n    sync_interval_seconds=5.0,\n    batch_size=100,\n    retry_max_attempts=3,\n\n    # Behavior\n    fail_open=True,\n    capture_inputs=True,\n    capture_outputs=True,\n    capture_state=True,\n)\n\nclyro.configure(config)\n```\n\n## Execution Controls\n\n### Step Limits\n\nPrevent runaway agent executions:\n\n```python\nfrom clyro import ClyroConfig, ExecutionControls, StepLimitExceededError\n\nconfig = ClyroConfig(\n    controls=ExecutionControls(max_steps=10)\n)\n\n@clyro.wrap(config=config)\ndef my_agent():\n    # Will raise StepLimitExceededError after 10 steps\n    pass\n\ntry:\n    my_agent()\nexcept StepLimitExceededError as e:\n    print(f\"Agent exceeded {e.limit} steps\")\n```\n\n### Cost Limits\n\nControl LLM spending:\n\n```python\nfrom clyro import ClyroConfig, ExecutionControls, CostLimitExceededError\n\nconfig = ClyroConfig(\n    controls=ExecutionControls(max_cost_usd=1.0)\n)\n\n@clyro.wrap(config=config)\ndef my_agent():\n    # Will raise CostLimitExceededError if cost exceeds $1.00\n    pass\n\ntry:\n    my_agent()\nexcept CostLimitExceededError as e:\n    print(f\"Cost ${e.current_cost_usd:.4f} exceeded limit ${e.limit_usd:.2f}\")\n```\n\n### Loop Detection\n\nDetect infinite loops automatically:\n\n```python\nfrom clyro import ClyroConfig, ExecutionControls, LoopDetectedError\n\nconfig = ClyroConfig(\n    controls=ExecutionControls(\n        loop_detection_threshold=3,  # Detect after 3 iterations\n        enable_loop_detection=True\n    )\n)\n\n@clyro.wrap(config=config)\ndef my_agent():\n    # Will raise LoopDetectedError if same state repeats 3 times\n    pass\n\ntry:\n    my_agent()\nexcept LoopDetectedError as e:\n    print(f\"Loop detected: {e.iterations} iterations\")\n    print(f\"State hash: {e.state_hash}\")\n```\n\n## Cost Tracking\n\nAutomatic cost calculation for LLM calls:\n\n```python\nfrom clyro import calculate_cost\n\n# OpenAI response\nresponse = client.chat.completions.create(\n    model=\"gpt-4o\",\n    messages=[{\"role\": \"user\", \"content\": \"Hello\"}]\n)\ncost = calculate_cost(response)\nprint(f\"Cost: ${cost:.4f}\")\n\n# Anthropic response\nresponse = anthropic.messages.create(\n    model=\"claude-3-sonnet-20240229\",\n    messages=[{\"role\": \"user\", \"content\": \"Hello\"}]\n)\ncost = calculate_cost(response)\nprint(f\"Cost: ${cost:.4f}\")\n```\n\n## Model Selection\n\nGet cost-optimal model recommendations:\n\n```python\nfrom clyro import ModelSelector\n\nselector = ModelSelector()\n\n# Get recommendation for classification task\nrecommendation = selector.recommend(\n    task_type=\"classification\",\n    max_cost_usd=0.001\n)\n\nprint(f\"Recommended model: {recommendation['model']}\")\nprint(f\"Expected cost: ${recommendation['expected_cost_usd']:.4f}\")\nprint(f\"Parameters: {recommendation['params']}\")\n```\n\n## Session Access\n\nAccess session information during execution:\n\n```python\nimport clyro\n\n@clyro.wrap\ndef my_agent(query: str) -\u003e str:\n    session = clyro.get_session()\n    if session:\n        print(f\"Step: {session.step_number}\")\n        print(f\"Cost: ${session.cumulative_cost:.4f}\")\n        print(f\"Duration: {session.duration_ms}ms\")\n\n    return f\"Response: {query}\"\n```\n\n## Local-Only Mode\n\nRun without backend connection:\n\n```python\nconfig = ClyroConfig(\n    api_key=None,  # No API key = local-only mode\n    local_storage_path=\"~/.clyro/traces.db\"\n)\n\nclyro.configure(config)\n\n@clyro.wrap\ndef my_agent(query: str) -\u003e str:\n    return f\"Response: {query}\"\n\n# Traces stored locally, not synced to backend\nresult = my_agent(\"Hello\")\n```\n\n## Error Handling\n\nThe SDK uses fail-open design - errors are logged but don't break your agent:\n\n```python\nimport clyro\nfrom clyro import ClyroError, TraceError, TransportError\n\n@clyro.wrap\ndef my_agent():\n    # Even if tracing fails, your agent continues\n    return \"Success\"\n\ntry:\n    result = my_agent()\nexcept ClyroError as e:\n    # SDK errors are caught internally with fail_open=True\n    # But you can catch them if needed\n    print(f\"SDK error: {e}\")\n```\n\n## Troubleshooting\n\n| Symptom | Cause | Fix |\n|---------|-------|-----|\n| `StepLimitExceededError` raised unexpectedly | `max_steps` set too low for your agent's workload | Increase `max_steps` in `ExecutionControls` or set `enable_step_limit=False` to disable |\n| `CostLimitExceededError` on first run | Default cost limit too low for the model you're using | Increase `max_cost_usd` — check `session.cumulative_cost` after a test run to calibrate |\n| `LoopDetectedError` false positive | Agent legitimately revisits similar states | Raise `loop_detection_threshold` (default: 3) or disable with `enable_loop_detection=False` |\n| Traces not appearing in dashboard | Sync worker hasn't flushed yet, or API key is invalid | Check `CLYRO_API_KEY` is set; traces flush every `sync_interval_seconds` (default: 5s). Inspect `~/.clyro/traces.db` for local buffered traces |\n| `TransportError` on startup | Backend unreachable (network issue or wrong endpoint) | Verify `CLYRO_ENDPOINT`; SDK fails open so your agent still runs — traces buffer locally |\n| Import error: `ModuleNotFoundError: clyro` | SDK not installed in active environment | Run `pip install clyro` in your virtualenv |\n| Agent runs but no traces captured | `@clyro.wrap` decorator missing or `clyro.configure()` not called | Ensure `clyro.configure(config)` runs before any wrapped function is called |\n| High memory usage | Large `local_storage_max_mb` or many un-synced traces | Lower `local_storage_max_mb` or check that background sync is running (backend reachable) |\n\n## Architecture\n\n```\n┌─────────────────────────────────────────────────────────────┐\n│                       Your Agent                             │\n│                    (any Python callable)                     │\n└───────────────────────┬─────────────────────────────────────┘\n                        │\n                        │ @clyro.wrap\n                        ▼\n┌─────────────────────────────────────────────────────────────┐\n│                    Clyro SDK Wrapper                         │\n│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐     │\n│  │   Session    │  │  Transport   │  │    Config    │     │\n│  │ Management   │  │    Layer     │  │   Manager    │     │\n│  └──────┬───────┘  └──────┬───────┘  └──────────────┘     │\n│         │                  │                                 │\n│         ▼                  ▼                                 │\n│  ┌──────────────┐  ┌──────────────┐                        │\n│  │ TraceEvent   │  │  Background  │                        │\n│  │  Creation    │  │  Sync Worker │                        │\n│  └──────┬───────┘  └──────┬───────┘                        │\n│         │                  │                                 │\n│         └──────────┬───────┘                                 │\n│                    ▼                                         │\n│         ┌──────────────────────┐                            │\n│         │  SQLite Local Store  │                            │\n│         │  ~/.clyro/traces.db  │                            │\n│         └──────────┬───────────┘                            │\n│                    │                                         │\n└────────────────────┼─────────────────────────────────────────┘\n                     │\n                     │ HTTPS (background sync)\n                     ▼\n          ┌──────────────────────┐\n          │   Clyro Backend API  │\n          │   (PostgreSQL +      │\n          │    ClickHouse)       │\n          └──────────────────────┘\n```\n\n## Framework Adapters\n\n| Framework | Adapter | How it works |\n|-----------|---------|-------------|\n| **LangGraph** | `LangGraphCallbackHandler` | Node/edge capture, LLM + tool tracing |\n| **CrewAI** | `CrewAICallbackHandler` | Task tracing, delegation, inter-agent comms |\n| **Claude Agent SDK** | `HookRegistrar` | Hook-based instrumentation, subagent tracking |\n| **Anthropic SDK** | Proxy wrapper | Transparent tracing for `messages.create/stream` |\n| **Any Python callable** | `@clyro.wrap` | Generic adapter, works with sync/async |\n\n## Documentation\n\n### Usage Guides\n\n| Guide | Description |\n|-------|-------------|\n| [LangGraph](docs/sdk/langgraph.md) | Wrap LangGraph agents with governance |\n| [CrewAI](docs/sdk/crewai.md) | Wrap CrewAI agents with governance |\n| [Claude Agent SDK](docs/sdk/claude_agent_sdk.md) | Wrap Claude Agent SDK with governance |\n| [Anthropic SDK](docs/sdk/anthropic.md) | Wrap Anthropic SDK calls with governance |\n| [MCP Wrapper](docs/mcp/mcp_wrapper.md) | Govern MCP tool calls in Claude Desktop, Cursor, VS Code |\n| [Claude Code Hooks](docs/hooks/claude_code_hooks.md) | Block destructive commands in Claude Code |\n| [OpenTelemetry](docs/otel/opentelemetry.md) | Export traces to OTLP-compatible backends |\n| [CX Policy](docs/policy/cx_policy.md) | Configure customer experience policies |\n\n### Reference\n\n- [API Reference](https://docs.clyro.dev/sdk) — Full API documentation\n- [CHANGELOG](CHANGELOG.md) — Version history\n- [CONTRIBUTING](CONTRIBUTING.md) — Development setup and guidelines\n\n## Development\n\n```bash\n# Clone and install\ngit clone https://github.com/getclyro/clyro.git\ncd clyro\npip install -e \".[dev]\"\n\n# Run tests\npytest\n\n# Run tests with coverage\npytest --cov=clyro --cov-report=term-missing\n\n# Lint and format\nruff check clyro/\nruff format clyro/\n```\n\n### Project Structure\n\n```\nclyro/\n├── adapters/           # Framework adapters (LangGraph, CrewAI, Anthropic, Claude Agent SDK)\n├── mcp/                # MCP governance wrapper (JSON-RPC proxy, YAML policies)\n├── hooks/              # Claude Code hooks (PreToolUse/PostToolUse governance)\n├── backend/            # Cloud backend communication (HTTP client, sync, circuit breaker)\n├── storage/            # Local SQLite storage + migrations\n├── workers/            # Background sync workers\n├── config.py           # Configuration models (ClyroConfig, ExecutionControls)\n├── wrapper.py          # Core wrap() function\n├── local_policy.py     # Local YAML policy evaluator\n├── local_logger.py     # Terminal logger for local mode\n├── cli.py              # CLI (clyro-sdk feedback, help)\n├── exceptions.py       # Exception hierarchy\n├── cost.py             # LLM cost calculation\n└── redaction.py        # PII/secret redaction\ntests/\n├── sdk/                # SDK unit tests\n├── mcp/                # MCP wrapper tests\n├── hooks/              # Claude Code hooks tests\n└── integration/        # End-to-end tests\n```\n\n## Requirements\n\n- Python 3.11+\n- httpx, pydantic, structlog, tenacity, aiosqlite, pyyaml\n\n## License\n\n[Apache License 2.0](LICENSE)\n\n## Support Links\n\n- Documentation: https://docs.clyro.dev\n- Issues: https://github.com/getclyro/clyro/issues\n- Community: https://discord.gg/clyro\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgetclyro%2Fclyro","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgetclyro%2Fclyro","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgetclyro%2Fclyro/lists"}