Skip to content

Commit 86ddf9f

Browse files
authored
chore: add readme (#65)
* chore: add readme Signed-off-by: Danny Kopping <danny@coder.com> * chore: fix diagram Signed-off-by: Danny Kopping <danny@coder.com> * chore: make fmt Signed-off-by: Danny Kopping <danny@coder.com> * chore: improvements Signed-off-by: Danny Kopping <danny@coder.com> * chore: make fmt Signed-off-by: Danny Kopping <danny@coder.com> * chore: review feedback Signed-off-by: Danny Kopping <danny@coder.com> --------- Signed-off-by: Danny Kopping <danny@coder.com>
1 parent 9e9491b commit 86ddf9f

File tree

8 files changed

+746
-1
lines changed

8 files changed

+746
-1
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
example/aibridge.db

README.md

Lines changed: 131 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,131 @@
1-
aibridge
1+
# aibridge
2+
3+
aibridge is an HTTP gateway that sits between AI clients and upstream AI providers (Anthropic, OpenAI). It intercepts requests to record token usage, prompts, and tool invocations per user. Optionally supports centralized [MCP](https://modelcontextprotocol.io/) tool injection with allowlist/denylist filtering.
4+
5+
## Architecture
6+
7+
```
8+
┌─────────────────┐ ┌───────────────────────────────────────────┐
9+
│ AI Client │ │ aibridge │
10+
│ (Claude Code, │────▶│ ┌─────────────────┐ ┌─────────────┐ │
11+
│ Cursor, etc.) │ │ │ RequestBridge │───▶│ Providers │ │
12+
└─────────────────┘ │ │ (http.Handler) │ │ (Anthropic │ │
13+
│ └─────────────────┘ │ OpenAI) │ │
14+
│ └──────┬──────┘ │
15+
│ │ │
16+
│ ▼ │ ┌─────────────┐
17+
│ ┌─────────────────┐ ┌─────────────┐ │ │ Upstream │
18+
│ │ Recorder │◀───│ Interceptor │─── ───▶│ API │
19+
│ │ (tokens, tools, │ │ (streaming/ │ │ │ (Anthropic │
20+
│ │ prompts) │ │ blocking) │ │ │ OpenAI) │
21+
│ └────────┬────────┘ └──────┬──────┘ │ └─────────────┘
22+
│ │ │ │
23+
│ ▼ ┌──────▼──────┐ │
24+
│ ┌ ─ ─ ─ ─ ─ ─ ─ ┐ │ MCP Proxy │ │
25+
│ │ Database │ │ (tools) │ │
26+
│ └ ─ ─ ─ ─ ─ ─ ─ ┘ └─────────────┘ │
27+
└───────────────────────────────────────────┘
28+
```
29+
30+
### Components
31+
32+
- **RequestBridge**: The main `http.Handler` that routes requests to providers
33+
- **Provider**: Defines bridged routes (intercepted) and passthrough routes (proxied)
34+
- **Interceptor**: Handles request/response processing and streaming
35+
- **Recorder**: Interface for capturing usage data (tokens, prompts, tools)
36+
- **MCP Proxy** (optional): Connects to MCP servers to list tool, inject them into requests, and invoke them in an inner agentic loop
37+
38+
## Request Flow
39+
40+
1. Client sends request to `/anthropic/v1/messages` or `/openai/v1/chat/completions`
41+
2. **Actor extraction**: Request must have an actor in context (via `AsActor()`).
42+
3. **Upstream call**: Request forwarded to the AI provider
43+
4. **Response relay**: Response streamed/sent to client
44+
5. **Recording**: Token usage, prompts, and tool invocations recorded
45+
46+
**With MCP enabled**: Tools from configured MCP servers are centrally defined and injected into requests (prefixed `bmcp_`). Allowlist/denylist regex patterns control which tools are available. When the model selects an injected tool, the gateway invokes it in an inner agentic loop, and continues the conversation loop until complete.
47+
48+
Passthrough routes (`/v1/models`, `/v1/messages/count_tokens`) are reverse-proxied directly.
49+
50+
## Observability
51+
52+
### Prometheus Metrics
53+
54+
Create metrics with `NewMetrics(prometheus.Registerer)`:
55+
56+
| Metric | Type | Description |
57+
|--------|------|-------------|
58+
| `interceptions_total` | Counter | Intercepted request count |
59+
| `interceptions_inflight` | Gauge | Currently processing requests |
60+
| `interceptions_duration_seconds` | Histogram | Request duration |
61+
| `tokens_total` | Counter | Token usage (input/output) |
62+
| `prompts_total` | Counter | User prompt count |
63+
| `injected_tool_invocations_total` | Counter | MCP tool invocations |
64+
| `passthrough_total` | Counter | Non-intercepted requests |
65+
66+
### Recorder Interface
67+
68+
Implement `Recorder` to persist usage data to your database. The example uses SQLite ([example/recorder.go](example/recorder.go)):
69+
70+
- `aibridge_interceptions` - request metadata (provider, model, initiator, timestamps)
71+
- `aibridge_token_usages` - input/output token counts per response
72+
- `aibridge_user_prompts` - user prompts
73+
- `aibridge_tool_usages` - tool invocations (injected and client-defined)
74+
75+
```go
76+
type Recorder interface {
77+
RecordInterception(ctx context.Context, req *InterceptionRecord) error
78+
RecordInterceptionEnded(ctx context.Context, req *InterceptionRecordEnded) error
79+
RecordTokenUsage(ctx context.Context, req *TokenUsageRecord) error
80+
RecordPromptUsage(ctx context.Context, req *PromptUsageRecord) error
81+
RecordToolUsage(ctx context.Context, req *ToolUsageRecord) error
82+
}
83+
```
84+
85+
## Example
86+
87+
See [example/](example/) for a complete runnable example with SQLite persistence and [DeepWiki](https://mcp.deepwiki.com) MCP integration.
88+
89+
### Setup
90+
91+
1. **Get API keys** from the provider consoles:
92+
- Anthropic: https://console.anthropic.com/settings/keys
93+
- OpenAI: https://platform.openai.com/api-keys
94+
95+
2. **Set environment variables**:
96+
```bash
97+
export ANTHROPIC_API_KEY="sk-ant-..."
98+
export OPENAI_API_KEY="sk-..."
99+
```
100+
101+
3. **Run the example**:
102+
```bash
103+
cd example && go run .
104+
```
105+
106+
4. **Test with curl**:
107+
```bash
108+
curl -X POST http://localhost:8080/anthropic/v1/messages \
109+
-H "Content-Type: application/json" \
110+
-d '{
111+
"model": "claude-sonnet-4-20250514",
112+
"max_tokens": 1024,
113+
"messages": [{"role": "user", "content": "Hello!"}],
114+
"stream": true
115+
}'
116+
```
117+
118+
5. **Test with Claude Code**:
119+
Claude Code allows a base URL override via `ANTHROPIC_BASE_URL`.
120+
121+
![image with cloude code example](example/claude-code.png)
122+
123+
## Supported Routes
124+
125+
| Provider | Route | Type |
126+
|----------|-------|------|
127+
| Anthropic | `/anthropic/v1/messages` | Bridged (intercepted) |
128+
| Anthropic | `/anthropic/v1/models` | Passthrough |
129+
| Anthropic | `/anthropic/v1/messages/count_tokens` | Passthrough |
130+
| OpenAI | `/openai/v1/chat/completions` | Bridged (intercepted) |
131+
| OpenAI | `/openai/v1/models` | Passthrough |

example/claude-code.png

27.6 KB
Loading

example/go.mod

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
module github.com/coder/aibridge/example
2+
3+
go 1.24.6
4+
5+
toolchain go1.24.10
6+
7+
require (
8+
cdr.dev/slog v1.6.2-0.20250703074222-9df5e0a6c145
9+
github.com/coder/aibridge v0.0.0
10+
github.com/google/uuid v1.6.0
11+
github.com/prometheus/client_golang v1.23.2
12+
modernc.org/sqlite v1.40.1
13+
)
14+
15+
require (
16+
github.com/anthropics/anthropic-sdk-go v1.13.0 // indirect
17+
github.com/aws/aws-sdk-go-v2 v1.30.3 // indirect
18+
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 // indirect
19+
github.com/aws/aws-sdk-go-v2/config v1.27.27 // indirect
20+
github.com/aws/aws-sdk-go-v2/credentials v1.17.27 // indirect
21+
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 // indirect
22+
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 // indirect
23+
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 // indirect
24+
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect
25+
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 // indirect
26+
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 // indirect
27+
github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 // indirect
28+
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 // indirect
29+
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 // indirect
30+
github.com/aws/smithy-go v1.20.3 // indirect
31+
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
32+
github.com/bahlo/generic-list-go v0.2.0 // indirect
33+
github.com/beorn7/perks v1.0.1 // indirect
34+
github.com/buger/jsonparser v1.1.1 // indirect
35+
github.com/cespare/xxhash/v2 v2.3.0 // indirect
36+
github.com/charmbracelet/lipgloss v0.7.1 // indirect
37+
github.com/dustin/go-humanize v1.0.1 // indirect
38+
github.com/hashicorp/errwrap v1.0.0 // indirect
39+
github.com/hashicorp/go-multierror v1.1.1 // indirect
40+
github.com/invopop/jsonschema v0.13.0 // indirect
41+
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
42+
github.com/mailru/easyjson v0.7.7 // indirect
43+
github.com/mark3labs/mcp-go v0.38.0 // indirect
44+
github.com/mattn/go-isatty v0.0.20 // indirect
45+
github.com/mattn/go-runewidth v0.0.15 // indirect
46+
github.com/muesli/reflow v0.3.0 // indirect
47+
github.com/muesli/termenv v0.15.2 // indirect
48+
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
49+
github.com/ncruces/go-strftime v0.1.9 // indirect
50+
github.com/openai/openai-go/v2 v2.7.0 // indirect
51+
github.com/prometheus/client_model v0.6.2 // indirect
52+
github.com/prometheus/common v0.66.1 // indirect
53+
github.com/prometheus/procfs v0.16.1 // indirect
54+
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
55+
github.com/rivo/uniseg v0.4.4 // indirect
56+
github.com/spf13/cast v1.7.1 // indirect
57+
github.com/tidwall/gjson v1.18.0 // indirect
58+
github.com/tidwall/match v1.2.0 // indirect
59+
github.com/tidwall/pretty v1.2.1 // indirect
60+
github.com/tidwall/sjson v1.2.5 // indirect
61+
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
62+
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
63+
go.opentelemetry.io/otel v1.33.0 // indirect
64+
go.opentelemetry.io/otel/trace v1.33.0 // indirect
65+
go.yaml.in/yaml/v2 v2.4.2 // indirect
66+
golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b // indirect
67+
golang.org/x/sys v0.36.0 // indirect
68+
golang.org/x/term v0.34.0 // indirect
69+
golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9 // indirect
70+
google.golang.org/protobuf v1.36.8 // indirect
71+
gopkg.in/yaml.v3 v3.0.1 // indirect
72+
modernc.org/libc v1.66.10 // indirect
73+
modernc.org/mathutil v1.7.1 // indirect
74+
modernc.org/memory v1.11.0 // indirect
75+
)
76+
77+
replace github.com/coder/aibridge => ../

0 commit comments

Comments
 (0)