bonito-mcp 1.0.0__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.
@@ -0,0 +1,8 @@
1
+ # Bonito MCP Server Configuration
2
+ # Copy this file to .env and fill in your values.
3
+
4
+ # Required: Your Bonito API / gateway key
5
+ BONITO_API_KEY=YOUR_KEY_HERE
6
+
7
+ # Optional: Override the API base URL (defaults to https://api.getbonito.com)
8
+ # BONITO_API_URL=https://api.getbonito.com
@@ -0,0 +1,12 @@
1
+ node_modules/
2
+ .next/
3
+ __pycache__/
4
+ *.pyc
5
+ .env
6
+ .venv/
7
+ dist/
8
+ *.egg-info/
9
+ .env.secrets
10
+ secrets/age-key.txt
11
+ secrets/*.yaml
12
+ !secrets/*.enc.yaml
@@ -0,0 +1,6 @@
1
+ FROM python:3.11-slim
2
+ WORKDIR /app
3
+ COPY . .
4
+ RUN pip install --no-cache-dir -e .
5
+ EXPOSE 8080
6
+ CMD ["bonito-mcp", "--transport", "sse", "--port", "8080"]
@@ -0,0 +1,120 @@
1
+ Metadata-Version: 2.4
2
+ Name: bonito-mcp
3
+ Version: 1.0.0
4
+ Summary: MCP server for the Bonito AI Platform – manage providers, models, agents, and gateway routing.
5
+ Project-URL: Homepage, https://getbonito.com
6
+ Project-URL: Documentation, https://getbonito.com/docs
7
+ Project-URL: Repository, https://github.com/ShabariRepo/bonito
8
+ Author-email: Bonito AI <hello@getbonito.com>
9
+ License-Expression: MIT
10
+ Keywords: ai,bonito,gateway,llm,mcp
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Topic :: Software Development :: Libraries
18
+ Requires-Python: >=3.10
19
+ Requires-Dist: httpx>=0.25.0
20
+ Requires-Dist: mcp>=1.0.0
21
+ Description-Content-Type: text/markdown
22
+
23
+ # Bonito MCP Server
24
+
25
+ A [Model Context Protocol (MCP)](https://modelcontextprotocol.io) server that wraps the **Bonito AI Platform** REST API. Use it from Claude Desktop, Cursor, or any MCP-compatible client to manage your multi-provider AI infrastructure.
26
+
27
+ ## Features
28
+
29
+ - **Provider Management** – Connect and verify AWS Bedrock, Azure OpenAI, GCP Vertex AI, OpenAI, Anthropic, and Groq
30
+ - **Model Management** – List, sync, and activate models across providers
31
+ - **Unified Gateway** – Send chat completions through Bonito's OpenAI-compatible gateway
32
+ - **Agent Management** – Create and execute BonBon and Bonobot agents
33
+ - **Knowledge Bases** – Create and manage knowledge bases
34
+ - **Cost & Observability** – Track costs per provider and view gateway logs
35
+
36
+ ## Quick Start
37
+
38
+ ### Install from source
39
+
40
+ ```bash
41
+ git clone https://github.com/ShabariRepo/bonito.git
42
+ cd bonito/mcp-server
43
+ pip install -e .
44
+ ```
45
+
46
+ ### Set your API key
47
+
48
+ ```bash
49
+ export BONITO_API_KEY="your-bonito-api-key"
50
+ ```
51
+
52
+ ### Run (stdio – for Claude Desktop)
53
+
54
+ ```bash
55
+ bonito-mcp
56
+ ```
57
+
58
+ ### Run (SSE – for network clients)
59
+
60
+ ```bash
61
+ bonito-mcp --transport sse --port 8080
62
+ ```
63
+
64
+ ## Claude Desktop Configuration
65
+
66
+ Add to your `claude_desktop_config.json`:
67
+
68
+ ```json
69
+ {
70
+ "mcpServers": {
71
+ "bonito": {
72
+ "command": "bonito-mcp",
73
+ "env": {
74
+ "BONITO_API_KEY": "your-bonito-api-key"
75
+ }
76
+ }
77
+ }
78
+ }
79
+ ```
80
+
81
+ ## Docker
82
+
83
+ ```bash
84
+ docker build -t bonito-mcp .
85
+ docker run -e BONITO_API_KEY="your-key" -p 8080:8080 bonito-mcp
86
+ ```
87
+
88
+ ## Available Tools
89
+
90
+ | Tool | Description |
91
+ |------|-------------|
92
+ | `list_providers` | List connected AI providers |
93
+ | `connect_provider` | Connect a new provider |
94
+ | `verify_provider` | Verify provider credentials |
95
+ | `list_models` | List models (with filters) |
96
+ | `sync_models` | Sync models from providers |
97
+ | `activate_model` | Activate a model for the gateway |
98
+ | `chat_completion` | Send a chat completion request |
99
+ | `list_gateway_keys` | List gateway API keys |
100
+ | `create_gateway_key` | Create a new gateway key |
101
+ | `gateway_usage` | View usage statistics |
102
+ | `list_agents` | List agents in a project |
103
+ | `create_agent` | Create a BonBon or Bonobot agent |
104
+ | `execute_agent` | Execute an agent |
105
+ | `get_agent` | Get agent details |
106
+ | `list_knowledge_bases` | List knowledge bases |
107
+ | `create_knowledge_base` | Create a knowledge base |
108
+ | `get_costs` | Get cost breakdown by provider |
109
+ | `get_gateway_logs` | View gateway request logs |
110
+
111
+ ## Environment Variables
112
+
113
+ | Variable | Required | Default | Description |
114
+ |----------|----------|---------|-------------|
115
+ | `BONITO_API_KEY` | Yes | – | Your Bonito API key |
116
+ | `BONITO_API_URL` | No | `https://api.getbonito.com` | API base URL |
117
+
118
+ ## License
119
+
120
+ MIT
@@ -0,0 +1,98 @@
1
+ # Bonito MCP Server
2
+
3
+ A [Model Context Protocol (MCP)](https://modelcontextprotocol.io) server that wraps the **Bonito AI Platform** REST API. Use it from Claude Desktop, Cursor, or any MCP-compatible client to manage your multi-provider AI infrastructure.
4
+
5
+ ## Features
6
+
7
+ - **Provider Management** – Connect and verify AWS Bedrock, Azure OpenAI, GCP Vertex AI, OpenAI, Anthropic, and Groq
8
+ - **Model Management** – List, sync, and activate models across providers
9
+ - **Unified Gateway** – Send chat completions through Bonito's OpenAI-compatible gateway
10
+ - **Agent Management** – Create and execute BonBon and Bonobot agents
11
+ - **Knowledge Bases** – Create and manage knowledge bases
12
+ - **Cost & Observability** – Track costs per provider and view gateway logs
13
+
14
+ ## Quick Start
15
+
16
+ ### Install from source
17
+
18
+ ```bash
19
+ git clone https://github.com/ShabariRepo/bonito.git
20
+ cd bonito/mcp-server
21
+ pip install -e .
22
+ ```
23
+
24
+ ### Set your API key
25
+
26
+ ```bash
27
+ export BONITO_API_KEY="your-bonito-api-key"
28
+ ```
29
+
30
+ ### Run (stdio – for Claude Desktop)
31
+
32
+ ```bash
33
+ bonito-mcp
34
+ ```
35
+
36
+ ### Run (SSE – for network clients)
37
+
38
+ ```bash
39
+ bonito-mcp --transport sse --port 8080
40
+ ```
41
+
42
+ ## Claude Desktop Configuration
43
+
44
+ Add to your `claude_desktop_config.json`:
45
+
46
+ ```json
47
+ {
48
+ "mcpServers": {
49
+ "bonito": {
50
+ "command": "bonito-mcp",
51
+ "env": {
52
+ "BONITO_API_KEY": "your-bonito-api-key"
53
+ }
54
+ }
55
+ }
56
+ }
57
+ ```
58
+
59
+ ## Docker
60
+
61
+ ```bash
62
+ docker build -t bonito-mcp .
63
+ docker run -e BONITO_API_KEY="your-key" -p 8080:8080 bonito-mcp
64
+ ```
65
+
66
+ ## Available Tools
67
+
68
+ | Tool | Description |
69
+ |------|-------------|
70
+ | `list_providers` | List connected AI providers |
71
+ | `connect_provider` | Connect a new provider |
72
+ | `verify_provider` | Verify provider credentials |
73
+ | `list_models` | List models (with filters) |
74
+ | `sync_models` | Sync models from providers |
75
+ | `activate_model` | Activate a model for the gateway |
76
+ | `chat_completion` | Send a chat completion request |
77
+ | `list_gateway_keys` | List gateway API keys |
78
+ | `create_gateway_key` | Create a new gateway key |
79
+ | `gateway_usage` | View usage statistics |
80
+ | `list_agents` | List agents in a project |
81
+ | `create_agent` | Create a BonBon or Bonobot agent |
82
+ | `execute_agent` | Execute an agent |
83
+ | `get_agent` | Get agent details |
84
+ | `list_knowledge_bases` | List knowledge bases |
85
+ | `create_knowledge_base` | Create a knowledge base |
86
+ | `get_costs` | Get cost breakdown by provider |
87
+ | `get_gateway_logs` | View gateway request logs |
88
+
89
+ ## Environment Variables
90
+
91
+ | Variable | Required | Default | Description |
92
+ |----------|----------|---------|-------------|
93
+ | `BONITO_API_KEY` | Yes | – | Your Bonito API key |
94
+ | `BONITO_API_URL` | No | `https://api.getbonito.com` | API base URL |
95
+
96
+ ## License
97
+
98
+ MIT
@@ -0,0 +1,3 @@
1
+ """Bonito MCP Server - Model Context Protocol server for the Bonito AI Platform."""
2
+
3
+ __version__ = "1.0.0"
@@ -0,0 +1,173 @@
1
+ """HTTP client wrapper for the Bonito REST API."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ from typing import Any
7
+
8
+ import httpx
9
+
10
+
11
+ class BonitoClient:
12
+ """Async HTTP client for Bonito API calls."""
13
+
14
+ def __init__(
15
+ self,
16
+ base_url: str | None = None,
17
+ api_key: str | None = None,
18
+ ) -> None:
19
+ self.base_url = (base_url or os.environ.get("BONITO_API_URL", "https://api.getbonito.com")).rstrip("/")
20
+ self.api_key = api_key or os.environ.get("BONITO_API_KEY", "")
21
+ if not self.api_key:
22
+ raise ValueError(
23
+ "BONITO_API_KEY is required. Set it as an environment variable or pass it directly."
24
+ )
25
+ self._client: httpx.AsyncClient | None = None
26
+
27
+ async def _get_client(self) -> httpx.AsyncClient:
28
+ if self._client is None or self._client.is_closed:
29
+ self._client = httpx.AsyncClient(
30
+ base_url=self.base_url,
31
+ headers={
32
+ "Authorization": f"Bearer {self.api_key}",
33
+ "Content-Type": "application/json",
34
+ },
35
+ timeout=httpx.Timeout(30.0),
36
+ )
37
+ return self._client
38
+
39
+ async def close(self) -> None:
40
+ if self._client and not self._client.is_closed:
41
+ await self._client.aclose()
42
+
43
+ # ── Generic request helpers ──────────────────────────────────────
44
+
45
+ async def get(self, path: str, params: dict[str, Any] | None = None) -> Any:
46
+ client = await self._get_client()
47
+ resp = await client.get(path, params=params)
48
+ resp.raise_for_status()
49
+ return resp.json()
50
+
51
+ async def post(self, path: str, json: dict[str, Any] | None = None) -> Any:
52
+ client = await self._get_client()
53
+ resp = await client.post(path, json=json)
54
+ resp.raise_for_status()
55
+ return resp.json()
56
+
57
+ async def put(self, path: str, json: dict[str, Any] | None = None) -> Any:
58
+ client = await self._get_client()
59
+ resp = await client.put(path, json=json)
60
+ resp.raise_for_status()
61
+ return resp.json()
62
+
63
+ async def delete(self, path: str) -> Any:
64
+ client = await self._get_client()
65
+ resp = await client.delete(path)
66
+ resp.raise_for_status()
67
+ return resp.json()
68
+
69
+ # ── Provider Management ──────────────────────────────────────────
70
+
71
+ async def list_providers(self) -> Any:
72
+ return await self.get("/api/providers")
73
+
74
+ async def connect_provider(self, provider_type: str, credentials: dict[str, Any]) -> Any:
75
+ return await self.post("/api/providers/connect", json={
76
+ "provider_type": provider_type,
77
+ "credentials": credentials,
78
+ })
79
+
80
+ async def verify_provider(self, provider_id: str) -> Any:
81
+ return await self.post(f"/api/providers/{provider_id}/verify")
82
+
83
+ # ── Model Management ─────────────────────────────────────────────
84
+
85
+ async def list_models(
86
+ self,
87
+ provider: str | None = None,
88
+ capability: str | None = None,
89
+ active: bool | None = None,
90
+ ) -> Any:
91
+ params: dict[str, Any] = {}
92
+ if provider:
93
+ params["provider"] = provider
94
+ if capability:
95
+ params["capability"] = capability
96
+ if active is not None:
97
+ params["active"] = str(active).lower()
98
+ return await self.get("/api/models", params=params or None)
99
+
100
+ async def sync_models(self) -> Any:
101
+ return await self.post("/api/models/sync")
102
+
103
+ async def activate_model(self, model_id: str) -> Any:
104
+ return await self.post(f"/api/models/{model_id}/activate")
105
+
106
+ # ── Gateway ──────────────────────────────────────────────────────
107
+
108
+ async def chat_completion(self, model: str, messages: list[dict[str, str]], **kwargs: Any) -> Any:
109
+ payload: dict[str, Any] = {"model": model, "messages": messages, **kwargs}
110
+ return await self.post("/v1/chat/completions", json=payload)
111
+
112
+ async def list_gateway_keys(self) -> Any:
113
+ return await self.get("/api/gateway/keys")
114
+
115
+ async def create_gateway_key(self, name: str | None = None) -> Any:
116
+ payload: dict[str, Any] = {}
117
+ if name:
118
+ payload["name"] = name
119
+ return await self.post("/api/gateway/keys", json=payload)
120
+
121
+ async def gateway_usage(self, period: str | None = None) -> Any:
122
+ params: dict[str, Any] = {}
123
+ if period:
124
+ params["period"] = period
125
+ return await self.get("/api/gateway/usage", params=params or None)
126
+
127
+ # ── Agent Management ─────────────────────────────────────────────
128
+
129
+ async def list_agents(self, project_id: str) -> Any:
130
+ return await self.get(f"/api/projects/{project_id}/agents")
131
+
132
+ async def create_agent(self, project_id: str, agent_config: dict[str, Any]) -> Any:
133
+ return await self.post(f"/api/projects/{project_id}/agents", json=agent_config)
134
+
135
+ async def execute_agent(self, agent_id: str, message: str) -> Any:
136
+ return await self.post(f"/api/agents/{agent_id}/execute", json={"message": message})
137
+
138
+ async def get_agent(self, agent_id: str) -> Any:
139
+ return await self.get(f"/api/agents/{agent_id}")
140
+
141
+ # ── Knowledge Bases ──────────────────────────────────────────────
142
+
143
+ async def list_knowledge_bases(self) -> Any:
144
+ return await self.get("/api/knowledge-bases")
145
+
146
+ async def create_knowledge_base(self, name: str, description: str | None = None) -> Any:
147
+ payload: dict[str, Any] = {"name": name}
148
+ if description:
149
+ payload["description"] = description
150
+ return await self.post("/api/knowledge-bases", json=payload)
151
+
152
+ # ── Cost & Observability ─────────────────────────────────────────
153
+
154
+ async def get_costs(self, provider_id: str, period: str | None = None) -> Any:
155
+ params: dict[str, Any] = {}
156
+ if period:
157
+ params["period"] = period
158
+ return await self.get(f"/api/providers/{provider_id}/costs", params=params or None)
159
+
160
+ async def get_gateway_logs(
161
+ self,
162
+ limit: int | None = None,
163
+ offset: int | None = None,
164
+ model: str | None = None,
165
+ ) -> Any:
166
+ params: dict[str, Any] = {}
167
+ if limit is not None:
168
+ params["limit"] = limit
169
+ if offset is not None:
170
+ params["offset"] = offset
171
+ if model:
172
+ params["model"] = model
173
+ return await self.get("/api/gateway/logs", params=params or None)
@@ -0,0 +1,356 @@
1
+ """Bonito MCP Server – exposes the Bonito AI Platform REST API as MCP tools."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import sys
7
+ from contextlib import asynccontextmanager
8
+ from typing import Any
9
+
10
+ from mcp.server.fastmcp import FastMCP
11
+
12
+ from bonito_mcp.client import BonitoClient
13
+
14
+ # ── Lifespan: initialise / tear-down the HTTP client ─────────────────
15
+
16
+
17
+ @asynccontextmanager
18
+ async def lifespan(server: FastMCP):
19
+ """Create a shared BonitoClient for the server's lifetime."""
20
+ client = BonitoClient()
21
+ try:
22
+ yield {"client": client}
23
+ finally:
24
+ await client.close()
25
+
26
+
27
+ # ── Server instance ──────────────────────────────────────────────────
28
+
29
+ app = FastMCP(
30
+ "Bonito",
31
+ instructions=(
32
+ "Bonito MCP Server – manage multi-provider AI infrastructure. "
33
+ "Connect cloud AI providers, deploy agents, route requests through "
34
+ "the unified gateway, and monitor costs."
35
+ ),
36
+ lifespan=lifespan,
37
+ )
38
+
39
+
40
+ def _client(ctx) -> BonitoClient:
41
+ """Retrieve the BonitoClient from the lifespan context."""
42
+ return ctx.request_context.lifespan_context["client"]
43
+
44
+
45
+ def _json(data: Any) -> str:
46
+ """Pretty-print JSON for tool responses."""
47
+ return json.dumps(data, indent=2, default=str)
48
+
49
+
50
+ # ═══════════════════════════════════════════════════════════════════════
51
+ # Provider Management
52
+ # ═══════════════════════════════════════════════════════════════════════
53
+
54
+
55
+ @app.tool()
56
+ async def list_providers(ctx) -> str:
57
+ """List all connected AI providers (AWS Bedrock, Azure OpenAI, GCP Vertex, OpenAI, Anthropic, Groq)."""
58
+ result = await _client(ctx).list_providers()
59
+ return _json(result)
60
+
61
+
62
+ @app.tool()
63
+ async def connect_provider(
64
+ provider_type: str,
65
+ credentials: str,
66
+ ctx=None,
67
+ ) -> str:
68
+ """Connect a new AI provider.
69
+
70
+ Args:
71
+ provider_type: Provider type – one of: aws_bedrock, azure_openai, gcp_vertex, openai, anthropic, groq
72
+ credentials: JSON string with provider-specific credentials (e.g. API keys, region, project ID)
73
+ """
74
+ creds = json.loads(credentials) if isinstance(credentials, str) else credentials
75
+ result = await _client(ctx).connect_provider(provider_type, creds)
76
+ return _json(result)
77
+
78
+
79
+ @app.tool()
80
+ async def verify_provider(provider_id: str, ctx=None) -> str:
81
+ """Verify connectivity and credentials for a connected provider.
82
+
83
+ Args:
84
+ provider_id: The provider ID to verify
85
+ """
86
+ result = await _client(ctx).verify_provider(provider_id)
87
+ return _json(result)
88
+
89
+
90
+ # ═══════════════════════════════════════════════════════════════════════
91
+ # Model Management
92
+ # ═══════════════════════════════════════════════════════════════════════
93
+
94
+
95
+ @app.tool()
96
+ async def list_models(
97
+ provider: str | None = None,
98
+ capability: str | None = None,
99
+ active: bool | None = None,
100
+ ctx=None,
101
+ ) -> str:
102
+ """List available models across all providers with optional filters.
103
+
104
+ Args:
105
+ provider: Filter by provider (e.g. openai, anthropic, aws_bedrock)
106
+ capability: Filter by capability (e.g. chat, embedding, image)
107
+ active: Filter by activation status
108
+ """
109
+ result = await _client(ctx).list_models(provider=provider, capability=capability, active=active)
110
+ return _json(result)
111
+
112
+
113
+ @app.tool()
114
+ async def sync_models(ctx) -> str:
115
+ """Sync available models from all connected providers. Discovers newly available models."""
116
+ result = await _client(ctx).sync_models()
117
+ return _json(result)
118
+
119
+
120
+ @app.tool()
121
+ async def activate_model(model_id: str, ctx=None) -> str:
122
+ """Activate a model to make it available through the gateway.
123
+
124
+ Args:
125
+ model_id: The model ID to activate
126
+ """
127
+ result = await _client(ctx).activate_model(model_id)
128
+ return _json(result)
129
+
130
+
131
+ # ═══════════════════════════════════════════════════════════════════════
132
+ # Gateway
133
+ # ═══════════════════════════════════════════════════════════════════════
134
+
135
+
136
+ @app.tool()
137
+ async def chat_completion(
138
+ model: str,
139
+ messages: str,
140
+ temperature: float | None = None,
141
+ max_tokens: int | None = None,
142
+ ctx=None,
143
+ ) -> str:
144
+ """Send a chat completion request through the Bonito gateway (OpenAI-compatible).
145
+
146
+ Args:
147
+ model: Model identifier (e.g. gpt-4o, claude-sonnet-4-20250514, or a Bonito model alias)
148
+ messages: JSON array of message objects, e.g. [{"role":"user","content":"Hello"}]
149
+ temperature: Sampling temperature (0-2)
150
+ max_tokens: Maximum tokens to generate
151
+ """
152
+ msgs = json.loads(messages) if isinstance(messages, str) else messages
153
+ kwargs: dict[str, Any] = {}
154
+ if temperature is not None:
155
+ kwargs["temperature"] = temperature
156
+ if max_tokens is not None:
157
+ kwargs["max_tokens"] = max_tokens
158
+ result = await _client(ctx).chat_completion(model, msgs, **kwargs)
159
+ return _json(result)
160
+
161
+
162
+ @app.tool()
163
+ async def list_gateway_keys(ctx) -> str:
164
+ """List all API keys for the Bonito gateway."""
165
+ result = await _client(ctx).list_gateway_keys()
166
+ return _json(result)
167
+
168
+
169
+ @app.tool()
170
+ async def create_gateway_key(name: str | None = None, ctx=None) -> str:
171
+ """Create a new gateway API key.
172
+
173
+ Args:
174
+ name: Optional friendly name for the key
175
+ """
176
+ result = await _client(ctx).create_gateway_key(name=name)
177
+ return _json(result)
178
+
179
+
180
+ @app.tool()
181
+ async def gateway_usage(period: str | None = None, ctx=None) -> str:
182
+ """Get gateway usage statistics.
183
+
184
+ Args:
185
+ period: Time period – e.g. 24h, 7d, 30d
186
+ """
187
+ result = await _client(ctx).gateway_usage(period=period)
188
+ return _json(result)
189
+
190
+
191
+ # ═══════════════════════════════════════════════════════════════════════
192
+ # Agent Management
193
+ # ═══════════════════════════════════════════════════════════════════════
194
+
195
+
196
+ @app.tool()
197
+ async def list_agents(project_id: str, ctx=None) -> str:
198
+ """List all agents in a project.
199
+
200
+ Args:
201
+ project_id: The project ID
202
+ """
203
+ result = await _client(ctx).list_agents(project_id)
204
+ return _json(result)
205
+
206
+
207
+ @app.tool()
208
+ async def create_agent(
209
+ project_id: str,
210
+ name: str,
211
+ model: str,
212
+ system_prompt: str | None = None,
213
+ agent_type: str = "bonbon",
214
+ ctx=None,
215
+ ) -> str:
216
+ """Create a new agent (BonBon or Bonobot).
217
+
218
+ Args:
219
+ project_id: The project ID to create the agent in
220
+ name: Agent name
221
+ model: Model to use (e.g. gpt-4o, claude-sonnet-4-20250514)
222
+ system_prompt: Optional system prompt for the agent
223
+ agent_type: Agent type – bonbon (single model) or bonobot (multi-model orchestrator)
224
+ """
225
+ config: dict[str, Any] = {
226
+ "name": name,
227
+ "model": model,
228
+ "type": agent_type,
229
+ }
230
+ if system_prompt:
231
+ config["system_prompt"] = system_prompt
232
+ result = await _client(ctx).create_agent(project_id, config)
233
+ return _json(result)
234
+
235
+
236
+ @app.tool()
237
+ async def execute_agent(agent_id: str, message: str, ctx=None) -> str:
238
+ """Execute an agent with a message.
239
+
240
+ Args:
241
+ agent_id: The agent ID to execute
242
+ message: The message to send to the agent
243
+ """
244
+ result = await _client(ctx).execute_agent(agent_id, message)
245
+ return _json(result)
246
+
247
+
248
+ @app.tool()
249
+ async def get_agent(agent_id: str, ctx=None) -> str:
250
+ """Get details of a specific agent.
251
+
252
+ Args:
253
+ agent_id: The agent ID
254
+ """
255
+ result = await _client(ctx).get_agent(agent_id)
256
+ return _json(result)
257
+
258
+
259
+ # ═══════════════════════════════════════════════════════════════════════
260
+ # Knowledge Bases
261
+ # ═══════════════════════════════════════════════════════════════════════
262
+
263
+
264
+ @app.tool()
265
+ async def list_knowledge_bases(ctx) -> str:
266
+ """List all knowledge bases."""
267
+ result = await _client(ctx).list_knowledge_bases()
268
+ return _json(result)
269
+
270
+
271
+ @app.tool()
272
+ async def create_knowledge_base(name: str, description: str | None = None, ctx=None) -> str:
273
+ """Create a new knowledge base.
274
+
275
+ Args:
276
+ name: Name for the knowledge base
277
+ description: Optional description
278
+ """
279
+ result = await _client(ctx).create_knowledge_base(name, description=description)
280
+ return _json(result)
281
+
282
+
283
+ # ═══════════════════════════════════════════════════════════════════════
284
+ # Cost & Observability
285
+ # ═══════════════════════════════════════════════════════════════════════
286
+
287
+
288
+ @app.tool()
289
+ async def get_costs(provider_id: str, period: str | None = None, ctx=None) -> str:
290
+ """Get cost breakdown for a specific provider.
291
+
292
+ Args:
293
+ provider_id: The provider ID
294
+ period: Time period – e.g. 24h, 7d, 30d
295
+ """
296
+ result = await _client(ctx).get_costs(provider_id, period=period)
297
+ return _json(result)
298
+
299
+
300
+ @app.tool()
301
+ async def get_gateway_logs(
302
+ limit: int | None = None,
303
+ offset: int | None = None,
304
+ model: str | None = None,
305
+ ctx=None,
306
+ ) -> str:
307
+ """Get gateway request logs for observability.
308
+
309
+ Args:
310
+ limit: Maximum number of log entries to return
311
+ offset: Offset for pagination
312
+ model: Filter logs by model name
313
+ """
314
+ result = await _client(ctx).get_gateway_logs(limit=limit, offset=offset, model=model)
315
+ return _json(result)
316
+
317
+
318
+ # ═══════════════════════════════════════════════════════════════════════
319
+ # CLI entry point
320
+ # ═══════════════════════════════════════════════════════════════════════
321
+
322
+
323
+ def main() -> None:
324
+ """Run the Bonito MCP server."""
325
+ import argparse
326
+
327
+ parser = argparse.ArgumentParser(description="Bonito MCP Server")
328
+ parser.add_argument(
329
+ "--transport",
330
+ choices=["stdio", "sse"],
331
+ default="stdio",
332
+ help="Transport type (default: stdio)",
333
+ )
334
+ parser.add_argument(
335
+ "--host",
336
+ default="127.0.0.1",
337
+ help="Host for SSE transport (default: 127.0.0.1)",
338
+ )
339
+ parser.add_argument(
340
+ "--port",
341
+ type=int,
342
+ default=8080,
343
+ help="Port for SSE transport (default: 8080)",
344
+ )
345
+ args = parser.parse_args()
346
+
347
+ if args.transport == "sse":
348
+ app.settings.host = args.host
349
+ app.settings.port = args.port
350
+ app.run(transport="sse")
351
+ else:
352
+ app.run(transport="stdio")
353
+
354
+
355
+ if __name__ == "__main__":
356
+ main()
@@ -0,0 +1,36 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "bonito-mcp"
7
+ version = "1.0.0"
8
+ description = "MCP server for the Bonito AI Platform – manage providers, models, agents, and gateway routing."
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ requires-python = ">=3.10"
12
+ authors = [
13
+ { name = "Bonito AI", email = "hello@getbonito.com" },
14
+ ]
15
+ keywords = ["mcp", "bonito", "ai", "llm", "gateway"]
16
+ classifiers = [
17
+ "Development Status :: 4 - Beta",
18
+ "Intended Audience :: Developers",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3.10",
21
+ "Programming Language :: Python :: 3.11",
22
+ "Programming Language :: Python :: 3.12",
23
+ "Topic :: Software Development :: Libraries",
24
+ ]
25
+ dependencies = [
26
+ "mcp>=1.0.0",
27
+ "httpx>=0.25.0",
28
+ ]
29
+
30
+ [project.scripts]
31
+ bonito-mcp = "bonito_mcp.server:main"
32
+
33
+ [project.urls]
34
+ Homepage = "https://getbonito.com"
35
+ Documentation = "https://getbonito.com/docs"
36
+ Repository = "https://github.com/ShabariRepo/bonito"