brrragent 0.4.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- brrragent-0.4.2/.gitignore +15 -0
- brrragent-0.4.2/LICENSE +21 -0
- brrragent-0.4.2/PKG-INFO +224 -0
- brrragent-0.4.2/README.md +206 -0
- brrragent-0.4.2/SECURITY.md +13 -0
- brrragent-0.4.2/pyproject.toml +70 -0
- brrragent-0.4.2/src/brrragent/__init__.py +40 -0
- brrragent-0.4.2/src/brrragent/_errors.py +7 -0
- brrragent-0.4.2/src/brrragent/agent.py +385 -0
- brrragent-0.4.2/src/brrragent/auth_cli.py +335 -0
- brrragent-0.4.2/src/brrragent/codex_oauth.py +1031 -0
- brrragent-0.4.2/src/brrragent/gemini.py +240 -0
- brrragent-0.4.2/src/brrragent/keys.py +158 -0
- brrragent-0.4.2/src/brrragent/mcp_tools.py +686 -0
- brrragent-0.4.2/src/brrragent/openai_direct.py +623 -0
- brrragent-0.4.2/src/brrragent/openrouter.py +292 -0
- brrragent-0.4.2/src/brrragent/prompt_cache.py +78 -0
- brrragent-0.4.2/tests/conftest.py +39 -0
- brrragent-0.4.2/tests/test_agent.py +292 -0
- brrragent-0.4.2/tests/test_auth_cli.py +393 -0
- brrragent-0.4.2/tests/test_codex_oauth.py +975 -0
- brrragent-0.4.2/tests/test_errors.py +47 -0
- brrragent-0.4.2/tests/test_gemini.py +241 -0
- brrragent-0.4.2/tests/test_keys.py +47 -0
- brrragent-0.4.2/tests/test_mcp_tools.py +595 -0
- brrragent-0.4.2/tests/test_openai_direct.py +558 -0
- brrragent-0.4.2/tests/test_openrouter.py +299 -0
- brrragent-0.4.2/tests/test_prompt_cache.py +33 -0
brrragent-0.4.2/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Bruno Volpato
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
brrragent-0.4.2/PKG-INFO
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: brrragent
|
|
3
|
+
Version: 0.4.2
|
|
4
|
+
Summary: Reusable agent loop with multi-provider routing and MCP tool calling
|
|
5
|
+
Project-URL: Homepage, https://github.com/bvolpato/brrragent
|
|
6
|
+
Project-URL: Repository, https://github.com/bvolpato/brrragent
|
|
7
|
+
Project-URL: Issues, https://github.com/bvolpato/brrragent/issues
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Requires-Python: >=3.12
|
|
11
|
+
Requires-Dist: httpx2<3,>=2.5
|
|
12
|
+
Requires-Dist: httpx<1,>=0.27.1
|
|
13
|
+
Requires-Dist: mcp<3,>=2.0.0
|
|
14
|
+
Requires-Dist: openai<3,>=2.45.0
|
|
15
|
+
Provides-Extra: gemini
|
|
16
|
+
Requires-Dist: google-genai<3,>=1.0.0; extra == 'gemini'
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
|
|
19
|
+
# brrragent
|
|
20
|
+
|
|
21
|
+
`brrragent` is a Python package for running a synchronous model/tool loop. It
|
|
22
|
+
provides a common entry point for several model APIs and converts MCP tools into
|
|
23
|
+
the function-calling formats expected by those APIs.
|
|
24
|
+
|
|
25
|
+
## Overview
|
|
26
|
+
|
|
27
|
+
Main components:
|
|
28
|
+
|
|
29
|
+
- `run_agent`: executes model responses and tool calls until completion.
|
|
30
|
+
- `McpToolCaller`: discovers, filters, routes, and executes tools across one or
|
|
31
|
+
more MCP servers.
|
|
32
|
+
- `McpServerConfig`: configures Streamable HTTP, legacy HTTP+SSE, stdio, or raw
|
|
33
|
+
JSON-RPC compatibility connections.
|
|
34
|
+
- `KeyPool`: selects API keys and removes rate-limited keys from rotation.
|
|
35
|
+
|
|
36
|
+
Model adapters cover OpenAI, OpenAI-compatible APIs, Google Gemini, OpenRouter,
|
|
37
|
+
and the optional Codex OAuth backend. Structured outputs, retry handling, tool
|
|
38
|
+
result truncation, and provider routing are handled by the package.
|
|
39
|
+
|
|
40
|
+
## Installation
|
|
41
|
+
|
|
42
|
+
Install from PyPI:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
uv add brrragent
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Gemini support uses an optional dependency:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
uv add "brrragent[gemini]"
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Use `uv pip install` instead when installing into an existing environment.
|
|
55
|
+
|
|
56
|
+
## Basic use
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
from brrragent import McpToolCaller, run_agent
|
|
60
|
+
|
|
61
|
+
with McpToolCaller(endpoint="https://mcp.example.com/mcp") as mcp:
|
|
62
|
+
result = run_agent(
|
|
63
|
+
system_prompt="Follow the request and use available tools when needed.",
|
|
64
|
+
user_prompt="Return a short response.",
|
|
65
|
+
model="openai/gpt-5.5",
|
|
66
|
+
mcp=mcp,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
print(result)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Provider credentials are read from the caller's environment. Common variables
|
|
73
|
+
are `OPENAI_API_KEY`, `OPENROUTER_API_KEY`, and `GEMINI_API_KEY`.
|
|
74
|
+
|
|
75
|
+
## Codex OAuth
|
|
76
|
+
|
|
77
|
+
Create brrragent-owned Codex credentials with device login:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
uv run brrragent-auth login
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
This writes direct OAuth credentials to `~/.cache/brrragent/codex-auth.json`
|
|
84
|
+
and Codex CLI credentials to `~/.cache/brrragent/codex-home/auth.json`, both
|
|
85
|
+
with `0600` permissions. Default routing does not read shared OpenCode auth or
|
|
86
|
+
`~/.codex/auth.json`.
|
|
87
|
+
|
|
88
|
+
Override locations with `BRRRAGENT_AUTH_PATH`, `BRRRAGENT_CODEX_HOME`, or
|
|
89
|
+
command flags. Existing callers can still use `BRRRAGENT_OPENCODE_AUTH_PATH`
|
|
90
|
+
and explicit `CODEX_HOME` values.
|
|
91
|
+
|
|
92
|
+
Use one independently issued profile per application or worker group to avoid
|
|
93
|
+
refresh-token rotation conflicts:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
uv run brrragent-auth login --profile content-worker
|
|
97
|
+
BRRRAGENT_PROFILE=content-worker uv run your-application
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Prefer per-call selection when one process handles multiple use cases:
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
from brrragent import CodexAuthConfig, run_agent
|
|
104
|
+
|
|
105
|
+
result = run_agent(
|
|
106
|
+
system_prompt="Return a concise answer.",
|
|
107
|
+
user_prompt="Summarize this input.",
|
|
108
|
+
model="codex/gpt-5.6-luna:medium",
|
|
109
|
+
codex_auth=CodexAuthConfig(profile="content-worker"),
|
|
110
|
+
)
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Explicit paths are also supported with
|
|
114
|
+
`CodexAuthConfig(auth_path="...", codex_home="...")`. Per-call selection is
|
|
115
|
+
context-local, so concurrent use cases do not need to mutate process-wide
|
|
116
|
+
environment variables.
|
|
117
|
+
|
|
118
|
+
Named profiles live under `~/.cache/brrragent/profiles/<profile>/`. Run device
|
|
119
|
+
login separately for every profile; copying an auth file also copies its
|
|
120
|
+
rotating refresh token and does not provide isolation. `BRRRAGENT_PROFILE_ROOT`
|
|
121
|
+
can move the profile directory. A named profile also takes precedence over a
|
|
122
|
+
generic `CODEX_HOME` value.
|
|
123
|
+
|
|
124
|
+
## Prompt caching
|
|
125
|
+
|
|
126
|
+
Keep reusable tools and system instructions before request-specific content.
|
|
127
|
+
Use one versioned key per stable prompt without user IDs, timestamps, URLs, or
|
|
128
|
+
prompt text:
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
from brrragent import PromptCacheConfig
|
|
132
|
+
|
|
133
|
+
cache = PromptCacheConfig(key="workflow:v2", ttl="1h")
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Pass `prompt_cache=cache` to `run_agent`. brrragent maps it to supported cache
|
|
137
|
+
keys, explicit breakpoints, provider affinity, and Anthropic cache controls.
|
|
138
|
+
Gemini uses its stable system-first prefix for implicit caching. An optional
|
|
139
|
+
`on_usage` callback receives normalized token and cache usage for each request.
|
|
140
|
+
|
|
141
|
+
## MCP servers
|
|
142
|
+
|
|
143
|
+
One caller can combine multiple endpoints:
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
from brrragent import McpToolCaller
|
|
147
|
+
|
|
148
|
+
mcp = McpToolCaller(
|
|
149
|
+
endpoints=[
|
|
150
|
+
"https://mcp.example.com/one",
|
|
151
|
+
"https://mcp.example.com/two",
|
|
152
|
+
],
|
|
153
|
+
allowed_tools={"*"},
|
|
154
|
+
excluded_tools={"internal_*"},
|
|
155
|
+
)
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Use explicit server configuration to mix transports or prefix tool names:
|
|
159
|
+
|
|
160
|
+
```python
|
|
161
|
+
from brrragent import McpServerConfig, McpToolCaller
|
|
162
|
+
|
|
163
|
+
mcp = McpToolCaller(
|
|
164
|
+
servers=[
|
|
165
|
+
McpServerConfig(
|
|
166
|
+
name="remote",
|
|
167
|
+
url="https://mcp.example.com/mcp",
|
|
168
|
+
tool_prefix="remote_",
|
|
169
|
+
),
|
|
170
|
+
McpServerConfig(
|
|
171
|
+
name="local",
|
|
172
|
+
transport="stdio",
|
|
173
|
+
command="uvx",
|
|
174
|
+
args=("example-mcp-server",),
|
|
175
|
+
tool_prefix="local_",
|
|
176
|
+
),
|
|
177
|
+
]
|
|
178
|
+
)
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
`transport="auto"` tries Streamable HTTP, legacy HTTP+SSE, then raw JSON-RPC
|
|
182
|
+
POST compatibility. Standard transports use MCP initialization, version
|
|
183
|
+
negotiation, persistent sessions, and clean shutdown. Duplicate visible tool
|
|
184
|
+
names require `tool_prefix` configuration.
|
|
185
|
+
|
|
186
|
+
HTTP auth objects in `McpServerConfig` use `httpx2.Auth`, matching MCP SDK v2.
|
|
187
|
+
HTTP transports use the operating system trust store; set `SSL_CERT_FILE` or
|
|
188
|
+
`SSL_CERT_DIR` for custom certificate authorities.
|
|
189
|
+
|
|
190
|
+
`BRRRAGENT_MCP_URL` configures one default endpoint. `BRRRAGENT_MCP_URLS`
|
|
191
|
+
accepts comma-separated endpoints.
|
|
192
|
+
|
|
193
|
+
Tool filters accept exact names and case-sensitive shell patterns such as
|
|
194
|
+
`*_search`. Exclusions apply during discovery and execution.
|
|
195
|
+
|
|
196
|
+
## Public API
|
|
197
|
+
|
|
198
|
+
```python
|
|
199
|
+
from brrragent import (
|
|
200
|
+
AgentUsage,
|
|
201
|
+
KeyPool,
|
|
202
|
+
McpServerConfig,
|
|
203
|
+
McpToolCaller,
|
|
204
|
+
PromptCacheConfig,
|
|
205
|
+
RateLimitExhausted,
|
|
206
|
+
get_default_caller,
|
|
207
|
+
pick_env_key,
|
|
208
|
+
pick_key,
|
|
209
|
+
run_agent,
|
|
210
|
+
)
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## Requirements
|
|
214
|
+
|
|
215
|
+
- Python 3.12 or newer
|
|
216
|
+
- `httpx>=0.27.1,<1`
|
|
217
|
+
- `httpx2>=2.5,<3`
|
|
218
|
+
- `mcp>=2.0.0,<3`
|
|
219
|
+
- `openai>=2.45.0,<3`
|
|
220
|
+
- `google-genai>=1.0.0,<3` for the `gemini` extra
|
|
221
|
+
|
|
222
|
+
## License
|
|
223
|
+
|
|
224
|
+
MIT
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# brrragent
|
|
2
|
+
|
|
3
|
+
`brrragent` is a Python package for running a synchronous model/tool loop. It
|
|
4
|
+
provides a common entry point for several model APIs and converts MCP tools into
|
|
5
|
+
the function-calling formats expected by those APIs.
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
Main components:
|
|
10
|
+
|
|
11
|
+
- `run_agent`: executes model responses and tool calls until completion.
|
|
12
|
+
- `McpToolCaller`: discovers, filters, routes, and executes tools across one or
|
|
13
|
+
more MCP servers.
|
|
14
|
+
- `McpServerConfig`: configures Streamable HTTP, legacy HTTP+SSE, stdio, or raw
|
|
15
|
+
JSON-RPC compatibility connections.
|
|
16
|
+
- `KeyPool`: selects API keys and removes rate-limited keys from rotation.
|
|
17
|
+
|
|
18
|
+
Model adapters cover OpenAI, OpenAI-compatible APIs, Google Gemini, OpenRouter,
|
|
19
|
+
and the optional Codex OAuth backend. Structured outputs, retry handling, tool
|
|
20
|
+
result truncation, and provider routing are handled by the package.
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
Install from PyPI:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
uv add brrragent
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Gemini support uses an optional dependency:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
uv add "brrragent[gemini]"
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Use `uv pip install` instead when installing into an existing environment.
|
|
37
|
+
|
|
38
|
+
## Basic use
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
from brrragent import McpToolCaller, run_agent
|
|
42
|
+
|
|
43
|
+
with McpToolCaller(endpoint="https://mcp.example.com/mcp") as mcp:
|
|
44
|
+
result = run_agent(
|
|
45
|
+
system_prompt="Follow the request and use available tools when needed.",
|
|
46
|
+
user_prompt="Return a short response.",
|
|
47
|
+
model="openai/gpt-5.5",
|
|
48
|
+
mcp=mcp,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
print(result)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Provider credentials are read from the caller's environment. Common variables
|
|
55
|
+
are `OPENAI_API_KEY`, `OPENROUTER_API_KEY`, and `GEMINI_API_KEY`.
|
|
56
|
+
|
|
57
|
+
## Codex OAuth
|
|
58
|
+
|
|
59
|
+
Create brrragent-owned Codex credentials with device login:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
uv run brrragent-auth login
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
This writes direct OAuth credentials to `~/.cache/brrragent/codex-auth.json`
|
|
66
|
+
and Codex CLI credentials to `~/.cache/brrragent/codex-home/auth.json`, both
|
|
67
|
+
with `0600` permissions. Default routing does not read shared OpenCode auth or
|
|
68
|
+
`~/.codex/auth.json`.
|
|
69
|
+
|
|
70
|
+
Override locations with `BRRRAGENT_AUTH_PATH`, `BRRRAGENT_CODEX_HOME`, or
|
|
71
|
+
command flags. Existing callers can still use `BRRRAGENT_OPENCODE_AUTH_PATH`
|
|
72
|
+
and explicit `CODEX_HOME` values.
|
|
73
|
+
|
|
74
|
+
Use one independently issued profile per application or worker group to avoid
|
|
75
|
+
refresh-token rotation conflicts:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
uv run brrragent-auth login --profile content-worker
|
|
79
|
+
BRRRAGENT_PROFILE=content-worker uv run your-application
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Prefer per-call selection when one process handles multiple use cases:
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
from brrragent import CodexAuthConfig, run_agent
|
|
86
|
+
|
|
87
|
+
result = run_agent(
|
|
88
|
+
system_prompt="Return a concise answer.",
|
|
89
|
+
user_prompt="Summarize this input.",
|
|
90
|
+
model="codex/gpt-5.6-luna:medium",
|
|
91
|
+
codex_auth=CodexAuthConfig(profile="content-worker"),
|
|
92
|
+
)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Explicit paths are also supported with
|
|
96
|
+
`CodexAuthConfig(auth_path="...", codex_home="...")`. Per-call selection is
|
|
97
|
+
context-local, so concurrent use cases do not need to mutate process-wide
|
|
98
|
+
environment variables.
|
|
99
|
+
|
|
100
|
+
Named profiles live under `~/.cache/brrragent/profiles/<profile>/`. Run device
|
|
101
|
+
login separately for every profile; copying an auth file also copies its
|
|
102
|
+
rotating refresh token and does not provide isolation. `BRRRAGENT_PROFILE_ROOT`
|
|
103
|
+
can move the profile directory. A named profile also takes precedence over a
|
|
104
|
+
generic `CODEX_HOME` value.
|
|
105
|
+
|
|
106
|
+
## Prompt caching
|
|
107
|
+
|
|
108
|
+
Keep reusable tools and system instructions before request-specific content.
|
|
109
|
+
Use one versioned key per stable prompt without user IDs, timestamps, URLs, or
|
|
110
|
+
prompt text:
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
from brrragent import PromptCacheConfig
|
|
114
|
+
|
|
115
|
+
cache = PromptCacheConfig(key="workflow:v2", ttl="1h")
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Pass `prompt_cache=cache` to `run_agent`. brrragent maps it to supported cache
|
|
119
|
+
keys, explicit breakpoints, provider affinity, and Anthropic cache controls.
|
|
120
|
+
Gemini uses its stable system-first prefix for implicit caching. An optional
|
|
121
|
+
`on_usage` callback receives normalized token and cache usage for each request.
|
|
122
|
+
|
|
123
|
+
## MCP servers
|
|
124
|
+
|
|
125
|
+
One caller can combine multiple endpoints:
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
from brrragent import McpToolCaller
|
|
129
|
+
|
|
130
|
+
mcp = McpToolCaller(
|
|
131
|
+
endpoints=[
|
|
132
|
+
"https://mcp.example.com/one",
|
|
133
|
+
"https://mcp.example.com/two",
|
|
134
|
+
],
|
|
135
|
+
allowed_tools={"*"},
|
|
136
|
+
excluded_tools={"internal_*"},
|
|
137
|
+
)
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Use explicit server configuration to mix transports or prefix tool names:
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
from brrragent import McpServerConfig, McpToolCaller
|
|
144
|
+
|
|
145
|
+
mcp = McpToolCaller(
|
|
146
|
+
servers=[
|
|
147
|
+
McpServerConfig(
|
|
148
|
+
name="remote",
|
|
149
|
+
url="https://mcp.example.com/mcp",
|
|
150
|
+
tool_prefix="remote_",
|
|
151
|
+
),
|
|
152
|
+
McpServerConfig(
|
|
153
|
+
name="local",
|
|
154
|
+
transport="stdio",
|
|
155
|
+
command="uvx",
|
|
156
|
+
args=("example-mcp-server",),
|
|
157
|
+
tool_prefix="local_",
|
|
158
|
+
),
|
|
159
|
+
]
|
|
160
|
+
)
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
`transport="auto"` tries Streamable HTTP, legacy HTTP+SSE, then raw JSON-RPC
|
|
164
|
+
POST compatibility. Standard transports use MCP initialization, version
|
|
165
|
+
negotiation, persistent sessions, and clean shutdown. Duplicate visible tool
|
|
166
|
+
names require `tool_prefix` configuration.
|
|
167
|
+
|
|
168
|
+
HTTP auth objects in `McpServerConfig` use `httpx2.Auth`, matching MCP SDK v2.
|
|
169
|
+
HTTP transports use the operating system trust store; set `SSL_CERT_FILE` or
|
|
170
|
+
`SSL_CERT_DIR` for custom certificate authorities.
|
|
171
|
+
|
|
172
|
+
`BRRRAGENT_MCP_URL` configures one default endpoint. `BRRRAGENT_MCP_URLS`
|
|
173
|
+
accepts comma-separated endpoints.
|
|
174
|
+
|
|
175
|
+
Tool filters accept exact names and case-sensitive shell patterns such as
|
|
176
|
+
`*_search`. Exclusions apply during discovery and execution.
|
|
177
|
+
|
|
178
|
+
## Public API
|
|
179
|
+
|
|
180
|
+
```python
|
|
181
|
+
from brrragent import (
|
|
182
|
+
AgentUsage,
|
|
183
|
+
KeyPool,
|
|
184
|
+
McpServerConfig,
|
|
185
|
+
McpToolCaller,
|
|
186
|
+
PromptCacheConfig,
|
|
187
|
+
RateLimitExhausted,
|
|
188
|
+
get_default_caller,
|
|
189
|
+
pick_env_key,
|
|
190
|
+
pick_key,
|
|
191
|
+
run_agent,
|
|
192
|
+
)
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## Requirements
|
|
196
|
+
|
|
197
|
+
- Python 3.12 or newer
|
|
198
|
+
- `httpx>=0.27.1,<1`
|
|
199
|
+
- `httpx2>=2.5,<3`
|
|
200
|
+
- `mcp>=2.0.0,<3`
|
|
201
|
+
- `openai>=2.45.0,<3`
|
|
202
|
+
- `google-genai>=1.0.0,<3` for the `gemini` extra
|
|
203
|
+
|
|
204
|
+
## License
|
|
205
|
+
|
|
206
|
+
MIT
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Supported versions
|
|
4
|
+
|
|
5
|
+
Security fixes target latest published release.
|
|
6
|
+
|
|
7
|
+
## Reporting a vulnerability
|
|
8
|
+
|
|
9
|
+
Use [GitHub private vulnerability reporting](https://github.com/bvolpato/brrragent/security/advisories/new).
|
|
10
|
+
Do not open public issue for undisclosed vulnerabilities.
|
|
11
|
+
|
|
12
|
+
Include affected version, impact, reproduction steps, and any suggested fix.
|
|
13
|
+
Redact credentials, access tokens, private prompts, and customer data.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "brrragent"
|
|
3
|
+
version = "0.4.2"
|
|
4
|
+
description = "Reusable agent loop with multi-provider routing and MCP tool calling"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
license-files = ["LICENSE"]
|
|
8
|
+
requires-python = ">=3.12"
|
|
9
|
+
dependencies = [
|
|
10
|
+
"httpx>=0.27.1,<1",
|
|
11
|
+
"httpx2>=2.5,<3",
|
|
12
|
+
"mcp>=2.0.0,<3",
|
|
13
|
+
"openai>=2.45.0,<3",
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
[project.scripts]
|
|
17
|
+
brrragent-auth = "brrragent.auth_cli:main"
|
|
18
|
+
|
|
19
|
+
[project.optional-dependencies]
|
|
20
|
+
gemini = ["google-genai>=1.0.0,<3"]
|
|
21
|
+
|
|
22
|
+
[project.urls]
|
|
23
|
+
Homepage = "https://github.com/bvolpato/brrragent"
|
|
24
|
+
Repository = "https://github.com/bvolpato/brrragent"
|
|
25
|
+
Issues = "https://github.com/bvolpato/brrragent/issues"
|
|
26
|
+
|
|
27
|
+
[dependency-groups]
|
|
28
|
+
dev = [
|
|
29
|
+
"check-wheel-contents>=0.6,<0.7",
|
|
30
|
+
"pytest>=8.0.0",
|
|
31
|
+
"pytest-cov>=7,<8",
|
|
32
|
+
"ruff>=0.16.1,<0.17",
|
|
33
|
+
"twine>=7.0.0,<8",
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
[build-system]
|
|
37
|
+
requires = ["hatchling"]
|
|
38
|
+
build-backend = "hatchling.build"
|
|
39
|
+
|
|
40
|
+
[tool.hatch.build.targets.wheel]
|
|
41
|
+
packages = ["src/brrragent"]
|
|
42
|
+
|
|
43
|
+
[tool.hatch.build.targets.sdist]
|
|
44
|
+
exclude = ["/.gitignore"]
|
|
45
|
+
include = [
|
|
46
|
+
"/LICENSE",
|
|
47
|
+
"/README.md",
|
|
48
|
+
"/SECURITY.md",
|
|
49
|
+
"/pyproject.toml",
|
|
50
|
+
"/src",
|
|
51
|
+
"/tests",
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
[tool.pytest.ini_options]
|
|
55
|
+
addopts = ["--strict-config", "--strict-markers"]
|
|
56
|
+
|
|
57
|
+
[tool.ruff]
|
|
58
|
+
target-version = "py312"
|
|
59
|
+
|
|
60
|
+
[tool.ruff.lint]
|
|
61
|
+
select = ["E4", "E7", "E9", "F", "I", "UP", "B"]
|
|
62
|
+
|
|
63
|
+
[tool.coverage.run]
|
|
64
|
+
branch = true
|
|
65
|
+
source = ["brrragent"]
|
|
66
|
+
|
|
67
|
+
[tool.coverage.report]
|
|
68
|
+
fail_under = 75
|
|
69
|
+
show_missing = true
|
|
70
|
+
skip_covered = true
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""
|
|
2
|
+
brrragent — Reusable agentic react loop with MCP tool calling.
|
|
3
|
+
|
|
4
|
+
Provides:
|
|
5
|
+
- McpToolCaller: Multi-server MCP bridge (OpenAI + Gemini formats)
|
|
6
|
+
- McpServerConfig: Streamable HTTP, SSE, stdio, or legacy JSON configuration
|
|
7
|
+
- run_agent: Smart-routed agentic loop (Gemini direct, OpenAI direct, Codex OAuth, OpenRouter)
|
|
8
|
+
- get_default_caller: Singleton McpToolCaller instance
|
|
9
|
+
- pick_key / pick_env_key: Handle comma-separated API keys
|
|
10
|
+
- KeyPool: Thread-safe key pool with 429 eviction (works for any provider)
|
|
11
|
+
- RateLimitExhausted: Exception when all keys are rate-limited
|
|
12
|
+
|
|
13
|
+
Backends (importable individually):
|
|
14
|
+
- brrragent.openrouter: OpenRouter/OpenAI-compatible backend
|
|
15
|
+
- brrragent.openai_direct: OpenAI native backend
|
|
16
|
+
- brrragent.codex_oauth: ChatGPT/Codex OAuth backend via isolated auth
|
|
17
|
+
- brrragent.gemini: Google Gemini direct backend
|
|
18
|
+
- brrragent.keys: API key utilities
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from brrragent.agent import run_agent
|
|
22
|
+
from brrragent.codex_oauth import CodexAuthConfig, codex_auth_context
|
|
23
|
+
from brrragent.keys import KeyPool, RateLimitExhausted, pick_env_key, pick_key
|
|
24
|
+
from brrragent.mcp_tools import McpServerConfig, McpToolCaller, get_default_caller
|
|
25
|
+
from brrragent.prompt_cache import AgentUsage, PromptCacheConfig
|
|
26
|
+
|
|
27
|
+
__all__ = [
|
|
28
|
+
"AgentUsage",
|
|
29
|
+
"CodexAuthConfig",
|
|
30
|
+
"KeyPool",
|
|
31
|
+
"McpServerConfig",
|
|
32
|
+
"McpToolCaller",
|
|
33
|
+
"PromptCacheConfig",
|
|
34
|
+
"RateLimitExhausted",
|
|
35
|
+
"get_default_caller",
|
|
36
|
+
"codex_auth_context",
|
|
37
|
+
"pick_env_key",
|
|
38
|
+
"pick_key",
|
|
39
|
+
"run_agent",
|
|
40
|
+
]
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
def safe_error_summary(error: BaseException) -> str:
|
|
2
|
+
"""Describe an error without copying provider-controlled response text."""
|
|
3
|
+
name = type(error).__name__
|
|
4
|
+
status = getattr(error, "status_code", None)
|
|
5
|
+
if isinstance(status, int):
|
|
6
|
+
return f"{name} (HTTP {status})"
|
|
7
|
+
return name
|