open-edison 0.1.19__py3-none-any.whl → 0.1.29__py3-none-any.whl

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: open-edison
3
- Version: 0.1.19
3
+ Version: 0.1.29
4
4
  Summary: Open-source MCP security, aggregation, and monitoring. Single-user, self-hosted MCP proxy.
5
5
  Author-email: Hugo Berg <hugo@edison.watch>
6
6
  License-File: LICENSE
@@ -25,24 +25,45 @@ Requires-Dist: pytest>=8.3.3; extra == 'dev'
25
25
  Requires-Dist: ruff>=0.12.3; extra == 'dev'
26
26
  Description-Content-Type: text/markdown
27
27
 
28
- # OpenEdison
28
+ # OpenEdison 🔒⚡️
29
29
 
30
- Open-source single-user MCP security gateway that prevents data exfiltration—via direct access or tool chaining—with full monitoring for local single‑user deployments. Provides core functionality of <https://edison.watch> for local use.
30
+ > The secure MCP proxy gateway
31
+
32
+ Connect AI to your data/software securely without risk of data exfiltration. Gain visibility, block threats, and get alerts on the data your agent is reading/writing. No more "approve fatigue" with the MCP tool-call approvals.
33
+
34
+ OpenEdison solves the [lethal trifecta problem](https://simonwillison.net/2025/Jun/16/the-lethal-trifecta/), which can cause agent hijacking & data exfiltration by malicious actors.
35
+
36
+ <p align="center">
37
+ <img src="media/trifecta520p.gif" alt="Trifecta Security Risk Animation" width="520">
38
+ </p>
31
39
 
32
40
  <div align="center">
33
- <h2>📧 Interested in connecting AI to your business software with proper access controls? <a href="mailto:hello@edison.watch">Contact us</a> to discuss.</h2>
41
+ <h2>📧 To get visibility, control and exfiltration blocker into AI's interaction with your company software, systems of record, DBs, <a href="mailto:hello@edison.watch">Contact us</a> to discuss.</h2>
34
42
  </div>
35
43
 
36
- ## Features
44
+ <p align="center">
45
+ <img alt="Project Version" src="https://img.shields.io/pypi/v/open-edison?label=version&color=blue">
46
+ <img alt="Python Version" src="https://img.shields.io/badge/python-3.12-blue?logo=python">
47
+ <img src="https://img.shields.io/badge/License-GPLv3-blue" alt="License">
48
+
49
+ </p>
50
+
51
+ ---
52
+
53
+ ## Features ✨
54
+
55
+ - 🛑 **Data leak blocker** - Edison automatically blocks any data leaks, even if your AI gets jailbroken
56
+ - 🕰️ **Deterministic execution** - Deterministic execution. Guaranteed data exfiltration blocker.
57
+ - 🗂️ **Easily configurable** - Easy to configure and manage your MCP servers
58
+ - 📊 **Visibility into agent interactions** - Track and monitor your agents and their interactions with connected software/data via MCP calls
59
+ - 🔗 **Simple API** - REST API for managing MCP servers and proxying requests
60
+ - 🐳 **Docker support** - Run in a container for easy deployment
37
61
 
38
- - **Single-user MCP proxy** - No multi-user complexity, just a simple proxy for your MCP servers
39
- - **JSON configuration** - Easy to configure and manage your MCP servers
40
- - **Simple local frontend** - Track and monitor your MCP interactions, servers, and sessions.
41
- - **Session tracking** - Track and monitor your MCP interactions
42
- - **Simple API** - REST API for managing MCP servers and proxying requests
43
- - **Docker support** - Run in a container for easy deployment
62
+ ## About Edison.watch 🏢
44
63
 
45
- ## Quick Start
64
+ Edison helps you gain observability, control, and policy enforcement for all AI interactions with systems of records, existing company software and data. Prevent AI from causing data leakage, lightning-fast setup for cross-system governance.
65
+
66
+ ## Quick Start 🚀
46
67
 
47
68
  The fastest way to get started:
48
69
 
@@ -52,10 +73,10 @@ The fastest way to get started:
52
73
  curl -fsSL https://raw.githubusercontent.com/Edison-Watch/open-edison/main/curl_pipe_bash.sh | bash
53
74
  ```
54
75
 
55
- Run locally with uvx: `uvx open-edison --config-dir ~/edison-config`
76
+ Run locally with uvx: `uvx open-edison`
56
77
 
57
78
  <details>
58
- <summary>Install Node.js/npm (optional for MCP tools)</summary>
79
+ <summary>⬇️ Install Node.js/npm (optional for MCP tools)</summary>
59
80
 
60
81
  If you need `npx` (for Node-based MCP tools like `mcp-remote`), install Node.js as well:
61
82
 
@@ -75,6 +96,7 @@ If you need `npx` (for Node-based MCP tools like `mcp-remote`), install Node.js
75
96
  - Node/npx: `winget install -e --id OpenJS.NodeJS`
76
97
 
77
98
  After installation, ensure that `npx` is available on PATH.
99
+ </details>
78
100
 
79
101
  <details>
80
102
  <summary><img src="https://img.shields.io/badge/pypi-3775A9?style=for-the-badge&logo=pypi&logoColor=white" alt="PyPI"> Install from PyPI</summary>
@@ -85,11 +107,11 @@ After installation, ensure that `npx` is available on PATH.
85
107
 
86
108
  ```bash
87
109
  # Using uvx
88
- uvx open-edison --help
110
+ uvx open-edison
89
111
 
90
112
  # Using pipx
91
113
  pipx install open-edison
92
- open-edison --help
114
+ open-edison
93
115
  ```
94
116
 
95
117
  Run with a custom config directory:
@@ -109,18 +131,18 @@ There is a dockerfile for simple local setup.
109
131
 
110
132
  ```bash
111
133
  # Single-line:
112
- git clone https://github.com/GatlingX/open-edison.git && cd open-edison && make docker_run
134
+ git clone https://github.com/Edison-Watch/open-edison.git && cd open-edison && make docker_run
113
135
 
114
136
  # Or
115
137
  # Clone repo
116
- git clone https://github.com/GatlingX/open-edison.git
138
+ git clone https://github.com/Edison-Watch/open-edison.git
117
139
  # Enter repo
118
140
  cd open-edison
119
141
  # Build and run
120
142
  make docker_run
121
143
  ```
122
144
 
123
- The MCP server will be available at `http://localhost:3000` and the api + frontend at `http://localhost:3001`.
145
+ The MCP server will be available at `http://localhost:3000` and the api + frontend at `http://localhost:3001`. 🌐
124
146
 
125
147
  </details>
126
148
 
@@ -130,7 +152,7 @@ The MCP server will be available at `http://localhost:3000` and the api + fronte
130
152
  1. Clone the repository:
131
153
 
132
154
  ```bash
133
- git clone https://github.com/GatlingX/open-edison.git
155
+ git clone https://github.com/Edison-Watch/open-edison.git
134
156
  cd open-edison
135
157
  ```
136
158
 
@@ -161,12 +183,12 @@ make run
161
183
  open-edison run
162
184
  ```
163
185
 
164
- The server will be available at `http://localhost:3000`.
186
+ The server will be available at `http://localhost:3000`. 🌐
165
187
 
166
188
  </details>
167
189
 
168
190
  <details>
169
- <summary>MCP Connection</summary>
191
+ <summary>🔌 MCP Connection</summary>
170
192
 
171
193
  Connect any MCP client to Open Edison (requires Node.js/npm for `npx`):
172
194
 
@@ -190,20 +212,20 @@ Or add to your MCP client config:
190
212
  </details>
191
213
 
192
214
  <details>
193
- <summary>Usage</summary>
215
+ <summary>🧭 Usage</summary>
194
216
 
195
217
  ### API Endpoints
196
218
 
197
219
  See [API Reference](docs/quick-reference/api_reference.md) for full API documentation.
198
220
 
199
221
  <details>
200
- <summary>Development</summary>
222
+ <summary>🛠️ Development</summary>
201
223
 
202
- ### Setup
224
+ ### Setup 🧰
203
225
 
204
226
  Setup from source as above.
205
227
 
206
- ### Run
228
+ ### Run ▶️
207
229
 
208
230
  Server doesn't have any auto-reload at the moment, so you'll need to run & ctrl-c this during development.
209
231
 
@@ -211,7 +233,7 @@ Server doesn't have any auto-reload at the moment, so you'll need to run & ctrl-
211
233
  make run
212
234
  ```
213
235
 
214
- ### Tests/code quality
236
+ ### Tests/code quality
215
237
 
216
238
  We expect `make ci` to return cleanly.
217
239
 
@@ -224,7 +246,7 @@ make ci
224
246
  <details>
225
247
  <summary>⚙️ Configuration (config.json)</summary>
226
248
 
227
- ## Configuration
249
+ ## Configuration ⚙️
228
250
 
229
251
  The `config.json` file contains all configuration:
230
252
 
@@ -246,18 +268,20 @@ Each MCP server configuration includes:
246
268
 
247
269
  </details>
248
270
 
271
+ ## 🔐 How Edison prevents data leakages
272
+
249
273
  <details>
250
- <summary>Security & Permissions System</summary>
274
+ <summary>🔱 The lethal trifecta, agent lifecycle management</summary>
251
275
 
252
276
  Open Edison includes a comprehensive security monitoring system that tracks the "lethal trifecta" of AI agent risks, as described in [Simon Willison's blog post](https://simonwillison.net/2025/Jun/16/the-lethal-trifecta/):
253
277
 
254
- <img src="media/lethal-trifecta.png" alt="The lethal trifecta diagram showing the three key AI agent security risks" width="30%">
278
+ <img src="media/lethal-trifecta.png" alt="The lethal trifecta diagram showing the three key AI agent security risks" width="70%">
255
279
 
256
280
  1. **Private data access** - Access to sensitive local files/data
257
281
  2. **Untrusted content exposure** - Exposure to external/web content
258
282
  3. **External communication** - Ability to write/send data externally
259
283
 
260
- <img src="media/pam-diagram.png" alt="Privileged Access Management (PAM) example showing the lethal trifecta in action" width="60%">
284
+ <img src="media/pam-diagram.png" alt="Privileged Access Management (PAM) example showing the lethal trifecta in action" width="90%">
261
285
 
262
286
  The configuration allows you to classify these risks across **tools**, **resources**, and **prompts** using separate configuration files.
263
287
 
@@ -265,7 +289,7 @@ In addition to trifecta, we track Access Control Level (ACL) for each tool call,
265
289
  that is, each tool has an ACL level (one of PUBLIC, PRIVATE, or SECRET), and we track the highest ACL level for each session.
266
290
  If a write operation is attempted to a lower ACL level, it is blocked.
267
291
 
268
- ### Tool Permissions (`tool_permissions.json`)
292
+ ### 🧰 Tool Permissions (`tool_permissions.json`)
269
293
 
270
294
  Defines security classifications for MCP tools. See full file: [tool_permissions.json](tool_permissions.json), it looks like:
271
295
 
@@ -283,7 +307,7 @@ Defines security classifications for MCP tools. See full file: [tool_permissions
283
307
  ```
284
308
 
285
309
  <details>
286
- <summary>Resource Permissions (`resource_permissions.json`)</summary>
310
+ <summary>📁 Resource Permissions (`resource_permissions.json`)</summary>
287
311
 
288
312
  ### Resource Permissions (`resource_permissions.json`)
289
313
 
@@ -299,7 +323,7 @@ Defines security classifications for resource access patterns. See full file: [r
299
323
  </details>
300
324
 
301
325
  <details>
302
- <summary>Prompt Permissions (`prompt_permissions.json`)</summary>
326
+ <summary>💬 Prompt Permissions (`prompt_permissions.json`)</summary>
303
327
 
304
328
  ### Prompt Permissions (`prompt_permissions.json`)
305
329
 
@@ -314,7 +338,7 @@ Defines security classifications for prompt types. See full file: [prompt_permis
314
338
 
315
339
  </details>
316
340
 
317
- ### Wildcard Patterns
341
+ ### Wildcard Patterns
318
342
 
319
343
  All permission types support wildcard patterns:
320
344
 
@@ -322,7 +346,7 @@ All permission types support wildcard patterns:
322
346
  - **Resources**: `scheme:*` (e.g., `file:*` matches all file resources)
323
347
  - **Prompts**: `type:*` (e.g., `template:*` matches all template prompts)
324
348
 
325
- ### Security Monitoring
349
+ ### Security Monitoring 🕵️
326
350
 
327
351
  **All items must be explicitly configured** - unknown tools/resources/prompts will be rejected for security.
328
352
 
@@ -330,20 +354,17 @@ Use the `get_security_status` tool to monitor your session's current risk level
330
354
 
331
355
  </details>
332
356
 
333
- <details>
334
- <summary>Documentation</summary>
357
+ ## Documentation 📚
335
358
 
336
359
  📚 **Complete documentation available in [`docs/`](docs/)**
337
360
 
338
- - **[Getting Started](docs/quick-reference/config_quick_start.md)** - Quick setup guide
339
- - **[Configuration](docs/core/configuration.md)** - Complete configuration reference
340
- - **[API Reference](docs/quick-reference/api_reference.md)** - REST API documentation
341
- - **[Development Guide](docs/development/development_guide.md)** - Contributing and development
342
-
343
- </details>
361
+ - 🚀 **[Getting Started](docs/quick-reference/config_quick_start.md)** - Quick setup guide
362
+ - ⚙️ **[Configuration](docs/core/configuration.md)** - Complete configuration reference
363
+ - 📡 **[API Reference](docs/quick-reference/api_reference.md)** - REST API documentation
364
+ - 🧑‍💻 **[Development Guide](docs/development/development_guide.md)** - Contributing and development
344
365
 
345
366
  <details>
346
- <summary>License</summary>
367
+ <summary>📄 License</summary>
347
368
 
348
369
  GPL-3.0 License - see [LICENSE](LICENSE) for details.
349
370
 
@@ -0,0 +1,17 @@
1
+ src/__init__.py,sha256=QWeZdjAm2D2B0eWhd8m2-DPpWvIP26KcNJxwEoU1oEQ,254
2
+ src/__main__.py,sha256=kQsaVyzRa_ESC57JpKDSQJAHExuXme0rM5beJsYxFeA,161
3
+ src/cli.py,sha256=_F1xtUU2h4snWUHf1NptRWGQaD2OSIhEPGLh9Rzmtis,10032
4
+ src/config.py,sha256=LHLQWWWvPzdQfCEUCbCMqHpEOnXJhmPFmk9bSVGnUyY,9443
5
+ src/events.py,sha256=rBH7rnaSWZ7GIC8zyBTwpcvIKWmKYCki-DNGgJhxPow,5001
6
+ src/oauth_manager.py,sha256=qcQa5BDRZr4bjqiXNflCnrXOh9mo9JVjvP2Caseg2Uc,9943
7
+ src/permissions.py,sha256=NGAnlG_z59HEiVA-k3cYvwmmiuHzxuNb5Tbd5umbL00,10483
8
+ src/server.py,sha256=UjVBnDCghbP6TbrhWH5tMmoByvRNrr9LMpuHUDt7kno,44692
9
+ src/single_user_mcp.py,sha256=P-D6_B_l7fJ2OzMjVeg56OkzRkCsODqKpky4ecHRj6I,17356
10
+ src/telemetry.py,sha256=-RZPIjpI53zbsKmp-63REeZ1JirWHV5WvpSRa2nqZEk,11321
11
+ src/middleware/data_access_tracker.py,sha256=bArBffWgYmvxOx9z_pgXQhogvnWQcc1m6WvEblDD4gw,15039
12
+ src/middleware/session_tracking.py,sha256=5W1VH9HNqIZeX0HNxDEm41U4GY6SqKSXtApDEeZK2qo,23084
13
+ open_edison-0.1.29.dist-info/METADATA,sha256=TxOClcNfQNRR1RPxFswzJWlrmKKruN9_u_JjnfhECKA,12103
14
+ open_edison-0.1.29.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
15
+ open_edison-0.1.29.dist-info/entry_points.txt,sha256=qNAkJcnoTXRhj8J--3PDmXz_TQKdB8H_0C9wiCtDIyA,72
16
+ open_edison-0.1.29.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
17
+ open_edison-0.1.29.dist-info/RECORD,,
src/cli.py CHANGED
@@ -177,6 +177,7 @@ def _spawn_frontend_dev( # noqa: C901 - pragmatic complexity for env probing
177
177
 
178
178
 
179
179
  async def _run_server(args: Any) -> None:
180
+ # TODO check this works as we want it to
180
181
  # Resolve config dir and expose via env for the rest of the app
181
182
  config_dir_arg = getattr(args, "config_dir", None)
182
183
  if config_dir_arg is not None:
@@ -184,7 +185,7 @@ async def _run_server(args: Any) -> None:
184
185
  config_dir = get_config_dir()
185
186
 
186
187
  # Load config after setting env override
187
- cfg = Config.load()
188
+ cfg = Config(config_dir)
188
189
 
189
190
  host = getattr(args, "host", None) or cfg.server.host
190
191
  port = getattr(args, "port", None) or cfg.server.port
src/config.py CHANGED
@@ -10,6 +10,7 @@ import os
10
10
  import sys
11
11
  import tomllib
12
12
  from dataclasses import asdict, dataclass
13
+ from functools import cache
13
14
  from pathlib import Path
14
15
  from typing import Any, cast
15
16
 
@@ -37,8 +38,7 @@ def get_config_dir() -> Path:
37
38
  try:
38
39
  return Path(env_dir).expanduser().resolve()
39
40
  except Exception:
40
- # Fall through to defaults
41
- pass
41
+ log.warning(f"Failed to resolve OPEN_EDISON_CONFIG_DIR: {env_dir}")
42
42
 
43
43
  # Platform-specific defaults
44
44
  try:
@@ -58,39 +58,11 @@ def get_config_dir() -> Path:
58
58
  return (Path.home() / ".open-edison").resolve()
59
59
 
60
60
 
61
- # Back-compat private alias (internal modules may import this)
62
- def _get_config_dir() -> Path: # noqa: D401
63
- """Alias to public get_config_dir (maintained for internal imports)."""
64
- return get_config_dir()
65
-
66
-
67
- def _default_config_path() -> Path:
68
- """Determine default config.json path.
69
-
70
- In development (editable or source checkout), prefer repository root
71
- `config.json` when present. In an installed package (site-packages),
72
- use the resolved user config dir.
73
- """
74
- repo_pyproject = root_dir / "pyproject.toml"
75
- repo_config = root_dir / "config.json"
76
-
77
- # If pyproject.toml exists next to src/, we are likely in a repo checkout
78
- if repo_pyproject.exists():
79
- return repo_config
80
-
81
- # Otherwise, prefer user config directory
61
+ def get_config_json_path() -> Path:
62
+ """Get the path to the config.json file"""
82
63
  return get_config_dir() / "config.json"
83
64
 
84
65
 
85
- class ConfigError(Exception):
86
- """Exception raised for configuration-related errors"""
87
-
88
- def __init__(self, message: str, config_path: Path | None = None):
89
- self.message = message
90
- self.config_path = config_path
91
- super().__init__(self.message)
92
-
93
-
94
66
  @dataclass
95
67
  class ServerConfig:
96
68
  """Server configuration"""
@@ -119,10 +91,41 @@ class MCPServerConfig:
119
91
  enabled: bool = True
120
92
  roots: list[str] | None = None
121
93
 
94
+ oauth_scopes: list[str] | None = None
95
+ """OAuth scopes to request for this server."""
96
+
97
+ oauth_client_name: str | None = None
98
+ """Custom client name for OAuth registration."""
99
+
122
100
  def __post_init__(self):
123
101
  if self.env is None:
124
102
  self.env = {}
125
103
 
104
+ def is_remote_server(self) -> bool:
105
+ """
106
+ Check if this is a remote MCP server (connects to external HTTPS endpoint).
107
+
108
+ Remote servers use mcp-remote with HTTPS URLs and may require OAuth.
109
+ Local servers run as child processes and don't need OAuth.
110
+ """
111
+ return (
112
+ self.command == "npx"
113
+ and len(self.args) >= 3
114
+ and self.args[1] == "mcp-remote"
115
+ and self.args[2].startswith("https://")
116
+ )
117
+
118
+ def get_remote_url(self) -> str | None:
119
+ """
120
+ Get the remote URL for a remote MCP server.
121
+
122
+ Returns:
123
+ The HTTPS URL if this is a remote server, None otherwise
124
+ """
125
+ if self.is_remote_server():
126
+ return self.args[2]
127
+ return None
128
+
126
129
 
127
130
  @dataclass
128
131
  class TelemetryConfig:
@@ -135,6 +138,15 @@ class TelemetryConfig:
135
138
  export_interval_ms: int = 60000
136
139
 
137
140
 
141
+ @cache
142
+ def load_json_file(path: Path) -> dict[str, Any]:
143
+ """Load a JSON file from the given path.
144
+ Kept as a separate function because we want to manually clear cache sometimes (update in config)"""
145
+ log.info(f"Loading configuration from {path}")
146
+ with open(path) as f:
147
+ return json.load(f)
148
+
149
+
138
150
  @dataclass
139
151
  class Config:
140
152
  """Main configuration class"""
@@ -160,15 +172,14 @@ class Config:
160
172
  log.warning(f"Failed to read version from pyproject.toml: {e}")
161
173
  return "unknown"
162
174
 
163
- @classmethod
164
- def load(cls, config_path: Path | None = None) -> "Config":
175
+ def __init__(self, config_path: Path | None = None) -> None:
165
176
  """Load configuration from JSON file.
166
177
 
167
178
  If a directory path is provided, will look for `config.json` inside it.
168
179
  If no path is provided, uses OPEN_EDISON_CONFIG_DIR or project root.
169
180
  """
170
181
  if config_path is None:
171
- config_path = _default_config_path()
182
+ config_path = get_config_json_path()
172
183
  else:
173
184
  # If a directory was passed, use config.json inside it
174
185
  if config_path.is_dir():
@@ -176,12 +187,10 @@ class Config:
176
187
 
177
188
  if not config_path.exists():
178
189
  log.warning(f"Config file not found at {config_path}, creating default config")
179
- default_config = cls.create_default()
180
- default_config.save(config_path)
181
- return default_config
190
+ self.create_default()
191
+ self.save(config_path)
182
192
 
183
- with open(config_path) as f:
184
- data: dict[str, Any] = json.load(f)
193
+ data = load_json_file(config_path)
185
194
 
186
195
  mcp_servers_data = data.get("mcp_servers", []) # type: ignore
187
196
  server_data = data.get("server", {}) # type: ignore
@@ -216,20 +225,18 @@ class Config:
216
225
  export_interval_ms=export_interval_ms,
217
226
  )
218
227
 
219
- return cls(
220
- server=ServerConfig(**server_data), # type: ignore
221
- logging=LoggingConfig(**logging_data), # type: ignore
222
- mcp_servers=[
223
- MCPServerConfig(**server_item) # type: ignore
224
- for server_item in mcp_servers_data # type: ignore
225
- ],
226
- telemetry=telemetry_cfg,
227
- )
228
+ self.server = ServerConfig(**server_data) # type: ignore
229
+ self.logging = LoggingConfig(**logging_data) # type: ignore
230
+ self.mcp_servers = [
231
+ MCPServerConfig(**server_item) # type: ignore
232
+ for server_item in mcp_servers_data # type: ignore
233
+ ]
234
+ self.telemetry = telemetry_cfg
228
235
 
229
236
  def save(self, config_path: Path | None = None) -> None:
230
237
  """Save configuration to JSON file"""
231
238
  if config_path is None:
232
- config_path = _default_config_path()
239
+ config_path = get_config_json_path()
233
240
  else:
234
241
  # If a directory was passed, save to config.json inside it
235
242
  if config_path.is_dir():
@@ -251,26 +258,19 @@ class Config:
251
258
 
252
259
  log.info(f"Configuration saved to {config_path}")
253
260
 
254
- @classmethod
255
- def create_default(cls) -> "Config":
261
+ def create_default(self) -> None:
256
262
  """Create default configuration"""
257
- return cls(
258
- server=ServerConfig(),
259
- logging=LoggingConfig(),
260
- mcp_servers=[
261
- MCPServerConfig(
262
- name="filesystem",
263
- command="uvx",
264
- args=["mcp-server-filesystem", "/tmp"],
265
- enabled=False,
266
- )
267
- ],
268
- telemetry=TelemetryConfig(
269
- enabled=True,
270
- otlp_endpoint=DEFAULT_OTLP_METRICS_ENDPOINT,
271
- ),
263
+ self.server = ServerConfig()
264
+ self.logging = LoggingConfig()
265
+ self.mcp_servers = [
266
+ MCPServerConfig(
267
+ name="filesystem",
268
+ command="uvx",
269
+ args=["mcp-server-filesystem", "/tmp"],
270
+ enabled=False,
271
+ )
272
+ ]
273
+ self.telemetry = TelemetryConfig(
274
+ enabled=True,
275
+ otlp_endpoint=DEFAULT_OTLP_METRICS_ENDPOINT,
272
276
  )
273
-
274
-
275
- # Load global configuration
276
- config = Config.load()