open-edison 0.1.64__py3-none-any.whl → 0.1.75rc1__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.
- {open_edison-0.1.64.dist-info → open_edison-0.1.75rc1.dist-info}/METADATA +1 -1
- open_edison-0.1.75rc1.dist-info/RECORD +41 -0
- src/cli.py +5 -4
- src/config.py +31 -27
- src/events.py +5 -2
- src/frontend_dist/assets/index-D05VN_1l.css +1 -0
- src/frontend_dist/assets/index-D6ziuTsl.js +51 -0
- src/frontend_dist/index.html +2 -2
- src/frontend_dist/sw.js +22 -2
- src/mcp_importer/exporters.py +1 -1
- src/mcp_stdio_capture.py +144 -0
- src/middleware/data_access_tracker.py +49 -4
- src/middleware/session_tracking.py +123 -34
- src/oauth_manager.py +2 -2
- src/permissions.py +86 -9
- src/server.py +27 -6
- src/setup_tui/main.py +6 -4
- src/single_user_mcp.py +246 -109
- open_edison-0.1.64.dist-info/RECORD +0 -40
- src/frontend_dist/assets/index-BUUcUfTt.js +0 -51
- src/frontend_dist/assets/index-o6_8mdM8.css +0 -1
- {open_edison-0.1.64.dist-info → open_edison-0.1.75rc1.dist-info}/WHEEL +0 -0
- {open_edison-0.1.64.dist-info → open_edison-0.1.75rc1.dist-info}/entry_points.txt +0 -0
- {open_edison-0.1.64.dist-info → open_edison-0.1.75rc1.dist-info}/licenses/LICENSE +0 -0
src/single_user_mcp.py
CHANGED
@@ -5,12 +5,21 @@ FastMCP instance for the single-user Open Edison setup.
|
|
5
5
|
Handles MCP protocol communication with running servers using a unified composite proxy.
|
6
6
|
"""
|
7
7
|
|
8
|
+
import asyncio
|
9
|
+
import time
|
8
10
|
from typing import Any, TypedDict
|
9
11
|
|
10
12
|
from fastmcp import Client as FastMCPClient
|
11
13
|
from fastmcp import Context, FastMCP
|
12
|
-
from fastmcp.server.
|
14
|
+
from fastmcp.server.server import add_resource_prefix, has_resource_prefix
|
15
|
+
|
16
|
+
# Low level FastMCP imports
|
17
|
+
from fastmcp.tools.tool import Tool
|
18
|
+
from fastmcp.tools.tool_transform import (
|
19
|
+
apply_transformations_to_tools,
|
20
|
+
)
|
13
21
|
from loguru import logger as log
|
22
|
+
from mcp.server.lowlevel.server import LifespanResultT
|
14
23
|
|
15
24
|
from src.config import Config, MCPServerConfig
|
16
25
|
from src.middleware.session_tracking import (
|
@@ -61,6 +70,114 @@ class SingleUserMCP(FastMCP[Any]):
|
|
61
70
|
self._setup_demo_resources()
|
62
71
|
self._setup_demo_prompts()
|
63
72
|
|
73
|
+
async def import_server(
|
74
|
+
self,
|
75
|
+
server: FastMCP[LifespanResultT],
|
76
|
+
prefix: str | None = None,
|
77
|
+
tool_separator: str | None = None,
|
78
|
+
resource_separator: str | None = None,
|
79
|
+
prompt_separator: str | None = None,
|
80
|
+
) -> None:
|
81
|
+
"""
|
82
|
+
Import the MCP objects from another FastMCP server into this one with a given prefix.
|
83
|
+
Overloads FastMCP's import_server method to improve performance.
|
84
|
+
|
85
|
+
Args:
|
86
|
+
server: The FastMCP server to import
|
87
|
+
prefix: prefix to use for the imported server's objects. If None,
|
88
|
+
objects are imported with their original names.
|
89
|
+
tool_separator: Deprecated. Required to be None.
|
90
|
+
resource_separator: Deprecated. Required to be None.
|
91
|
+
prompt_separator: Deprecated. Required to be None.
|
92
|
+
"""
|
93
|
+
|
94
|
+
if prefix is None:
|
95
|
+
raise ValueError("Prefix is required")
|
96
|
+
|
97
|
+
if tool_separator is not None:
|
98
|
+
raise ValueError("Tool separator is deprecated and not supported")
|
99
|
+
|
100
|
+
if resource_separator is not None:
|
101
|
+
raise ValueError("Resource separator is deprecated and not supported")
|
102
|
+
|
103
|
+
if prompt_separator is not None:
|
104
|
+
raise ValueError("Prompt separator is deprecated and not supported")
|
105
|
+
|
106
|
+
log.debug(f"🔧 Importing server {prefix} ({server.name}) into single user MCP'")
|
107
|
+
|
108
|
+
# Fetch all server objects in parallel
|
109
|
+
tools, resources, templates, prompts = await asyncio.gather(
|
110
|
+
server.get_tools(),
|
111
|
+
server.get_resources(),
|
112
|
+
server.get_resource_templates(),
|
113
|
+
server.get_prompts(),
|
114
|
+
return_exceptions=True,
|
115
|
+
)
|
116
|
+
|
117
|
+
# Validate and normalize all results
|
118
|
+
tools = self._validate_server_result(tools, "tools", server.name)
|
119
|
+
resources = self._validate_server_result(resources, "resources", server.name)
|
120
|
+
templates = self._validate_server_result(templates, "templates", server.name)
|
121
|
+
prompts = self._validate_server_result(prompts, "prompts", server.name)
|
122
|
+
|
123
|
+
# Import all components
|
124
|
+
self._import_tools(tools, prefix)
|
125
|
+
self._import_resources(resources, prefix)
|
126
|
+
self._import_templates(templates, prefix)
|
127
|
+
self._import_prompts(prompts, prefix)
|
128
|
+
|
129
|
+
log.debug(f"Imported server {server.name} with prefix '{prefix}'")
|
130
|
+
|
131
|
+
def _validate_server_result(
|
132
|
+
self, result: Any, result_type: str, server_name: str
|
133
|
+
) -> dict[str, Any]:
|
134
|
+
"""Validate and normalize server result from asyncio.gather with return_exceptions=True."""
|
135
|
+
if isinstance(result, Exception):
|
136
|
+
log.warning(f'Server {server_name} does not appear to contain "{result_type}"')
|
137
|
+
log.debug(f"Server {server_name} _validate_server_result exception result: {result}")
|
138
|
+
return {}
|
139
|
+
if not isinstance(result, dict):
|
140
|
+
log.warning(f"Server {server_name} returned an unexpected response")
|
141
|
+
log.debug(
|
142
|
+
f"Server {server_name} _validate_server_result unexpected type {type(result)} with value: {result}"
|
143
|
+
)
|
144
|
+
return {}
|
145
|
+
return result # type: ignore[return-value]
|
146
|
+
|
147
|
+
def _import_tools(self, tools: dict[str, Any], prefix: str) -> None:
|
148
|
+
"""Import tools from server"""
|
149
|
+
for key, tool in tools.items():
|
150
|
+
if prefix:
|
151
|
+
tool = tool.model_copy(key=f"{prefix}_{key}")
|
152
|
+
self._tool_manager.add_tool(tool)
|
153
|
+
|
154
|
+
def _import_resources(self, resources: dict[str, Any], prefix: str) -> None:
|
155
|
+
"""Import resources from server"""
|
156
|
+
for key, resource in resources.items():
|
157
|
+
if prefix:
|
158
|
+
resource_key = add_resource_prefix(key, prefix, self.resource_prefix_format)
|
159
|
+
resource = resource.model_copy(
|
160
|
+
update={"name": f"{prefix}_{resource.name}"}, key=resource_key
|
161
|
+
)
|
162
|
+
self._resource_manager.add_resource(resource)
|
163
|
+
|
164
|
+
def _import_templates(self, templates: dict[str, Any], prefix: str) -> None:
|
165
|
+
"""Import templates from server"""
|
166
|
+
for key, template in templates.items():
|
167
|
+
if prefix:
|
168
|
+
template_key = add_resource_prefix(key, prefix, self.resource_prefix_format)
|
169
|
+
template = template.model_copy(
|
170
|
+
update={"name": f"{prefix}_{template.name}"}, key=template_key
|
171
|
+
)
|
172
|
+
self._resource_manager.add_template(template)
|
173
|
+
|
174
|
+
def _import_prompts(self, prompts: dict[str, Any], prefix: str) -> None:
|
175
|
+
"""Import prompts from server"""
|
176
|
+
for key, prompt in prompts.items():
|
177
|
+
if prefix:
|
178
|
+
prompt = prompt.model_copy(key=f"{prefix}_{key}")
|
179
|
+
self._prompt_manager.add_prompt(prompt)
|
180
|
+
|
64
181
|
def _convert_to_fastmcp_config(self, enabled_servers: list[MCPServerConfig]) -> dict[str, Any]:
|
65
182
|
"""
|
66
183
|
Convert Open Edison config format to FastMCP MCPConfig format.
|
@@ -88,46 +205,6 @@ class SingleUserMCP(FastMCP[Any]):
|
|
88
205
|
|
89
206
|
return {"mcpServers": mcp_servers}
|
90
207
|
|
91
|
-
async def create_composite_proxy(self, enabled_servers: list[MCPServerConfig]) -> bool:
|
92
|
-
"""
|
93
|
-
Create a unified composite proxy for all enabled MCP servers.
|
94
|
-
|
95
|
-
This replaces individual server mounting with a single FastMCP composite proxy
|
96
|
-
that handles all configured servers with automatic namespacing.
|
97
|
-
|
98
|
-
Args:
|
99
|
-
enabled_servers: List of enabled MCP server configurations
|
100
|
-
|
101
|
-
Returns:
|
102
|
-
True if composite proxy was created successfully, False otherwise
|
103
|
-
"""
|
104
|
-
if not enabled_servers:
|
105
|
-
log.info("No real servers to mount in composite proxy")
|
106
|
-
return True
|
107
|
-
|
108
|
-
oauth_manager = get_oauth_manager()
|
109
|
-
|
110
|
-
for server_config in enabled_servers:
|
111
|
-
server_name = server_config.name
|
112
|
-
|
113
|
-
# Skip if this server would produce an empty config (e.g., misconfigured)
|
114
|
-
fastmcp_config = self._convert_to_fastmcp_config([server_config])
|
115
|
-
if not fastmcp_config.get("mcpServers"):
|
116
|
-
log.warning(f"Skipping server '{server_name}' due to empty MCP config")
|
117
|
-
continue
|
118
|
-
|
119
|
-
try:
|
120
|
-
await self._mount_single_server(server_config, fastmcp_config, oauth_manager)
|
121
|
-
except Exception as e:
|
122
|
-
log.error(f"❌ Failed to mount server {server_name}: {e}")
|
123
|
-
# Continue with other servers even if one fails
|
124
|
-
continue
|
125
|
-
|
126
|
-
log.info(
|
127
|
-
f"✅ Created composite proxy with {len(enabled_servers)} servers ({mounted_servers.keys()})"
|
128
|
-
)
|
129
|
-
return True
|
130
|
-
|
131
208
|
async def _mount_single_server(
|
132
209
|
self,
|
133
210
|
server_config: MCPServerConfig,
|
@@ -141,6 +218,7 @@ class SingleUserMCP(FastMCP[Any]):
|
|
141
218
|
remote_url = server_config.get_remote_url()
|
142
219
|
oauth_info = await oauth_manager.check_oauth_requirement(server_name, remote_url)
|
143
220
|
|
221
|
+
client_timeout = 10
|
144
222
|
# Create proxy based on server type to avoid union type issues
|
145
223
|
if server_config.is_remote_server():
|
146
224
|
# Handle remote servers (with or without OAuth)
|
@@ -157,18 +235,22 @@ class SingleUserMCP(FastMCP[Any]):
|
|
157
235
|
server_config.oauth_client_name,
|
158
236
|
)
|
159
237
|
if oauth_auth:
|
160
|
-
client = FastMCPClient(
|
238
|
+
client = FastMCPClient(
|
239
|
+
remote_url,
|
240
|
+
auth=oauth_auth,
|
241
|
+
timeout=client_timeout,
|
242
|
+
)
|
161
243
|
log.info(
|
162
244
|
f"🔐 Created remote client with OAuth authentication for {server_name}"
|
163
245
|
)
|
164
246
|
else:
|
165
|
-
client = FastMCPClient(remote_url)
|
247
|
+
client = FastMCPClient(remote_url, timeout=client_timeout)
|
166
248
|
log.warning(
|
167
249
|
f"⚠️ OAuth auth creation failed, using unauthenticated client for {server_name}"
|
168
250
|
)
|
169
251
|
else:
|
170
252
|
# Remote server without OAuth or needs auth
|
171
|
-
client = FastMCPClient(remote_url)
|
253
|
+
client = FastMCPClient(remote_url, timeout=client_timeout)
|
172
254
|
log.info(f"🌐 Created remote client for {server_name}")
|
173
255
|
|
174
256
|
# Log OAuth status warnings
|
@@ -185,10 +267,12 @@ class SingleUserMCP(FastMCP[Any]):
|
|
185
267
|
|
186
268
|
else:
|
187
269
|
# Local server - create proxy directly from config (avoids union type issue)
|
188
|
-
log.
|
270
|
+
log.debug(f"🔧 Creating local process proxy for {server_name}")
|
189
271
|
proxy = FastMCP.as_proxy(fastmcp_config)
|
190
272
|
|
191
|
-
|
273
|
+
log.debug(f"🔧 Importing server {server_name} into single user MCP")
|
274
|
+
await self.import_server(proxy, prefix=server_name)
|
275
|
+
# await super().import_server(proxy, prefix=server_name)
|
192
276
|
mounted_servers[server_name] = MountedServerInfo(config=server_config, proxy=proxy)
|
193
277
|
|
194
278
|
server_type = "remote" if server_config.is_remote_server() else "local"
|
@@ -231,10 +315,7 @@ class SingleUserMCP(FastMCP[Any]):
|
|
231
315
|
try:
|
232
316
|
oauth_manager = get_oauth_manager()
|
233
317
|
await self._mount_single_server(server_config, fastmcp_config, oauth_manager)
|
234
|
-
|
235
|
-
_ = await self._tool_manager.list_tools()
|
236
|
-
_ = await self._resource_manager.list_resources()
|
237
|
-
_ = await self._prompt_manager.list_prompts()
|
318
|
+
|
238
319
|
return True
|
239
320
|
except Exception as e: # noqa: BLE001
|
240
321
|
log.error(f"❌ Failed to mount server {server_name}: {e}")
|
@@ -243,7 +324,6 @@ class SingleUserMCP(FastMCP[Any]):
|
|
243
324
|
async def unmount(self, server_name: str) -> bool:
|
244
325
|
"""
|
245
326
|
Unmount a previously mounted server by name.
|
246
|
-
|
247
327
|
Returns True if it was unmounted, False if it wasn't mounted.
|
248
328
|
"""
|
249
329
|
info = mounted_servers.pop(server_name, None)
|
@@ -251,88 +331,145 @@ class SingleUserMCP(FastMCP[Any]):
|
|
251
331
|
log.info(f"ℹ️ Server {server_name} was not mounted")
|
252
332
|
return False
|
253
333
|
|
254
|
-
|
255
|
-
|
256
|
-
# Manually remove from FastMCP managers' mounted lists
|
334
|
+
# Remove the server from mounted_servers lists in all managers
|
257
335
|
for manager_name in ("_tool_manager", "_resource_manager", "_prompt_manager"):
|
258
336
|
manager = getattr(self, manager_name, None)
|
337
|
+
if manager is None:
|
338
|
+
continue
|
259
339
|
mounted_list = getattr(manager, "_mounted_servers", None)
|
260
340
|
if mounted_list is None:
|
261
341
|
continue
|
262
342
|
|
263
|
-
#
|
264
|
-
|
265
|
-
m
|
266
|
-
for m in mounted_list
|
267
|
-
if not (m.prefix == server_name and (proxy is None or m.server is proxy))
|
268
|
-
]
|
269
|
-
if len(new_list) == len(mounted_list):
|
270
|
-
new_list = [m for m in mounted_list if m.prefix != server_name]
|
343
|
+
# Remove servers with matching prefix
|
344
|
+
mounted_list[:] = [m for m in mounted_list if m.prefix != server_name]
|
271
345
|
|
272
|
-
|
346
|
+
# Remove tools with matching prefix (server name)
|
347
|
+
self._tool_manager._tools = { # type: ignore
|
348
|
+
key: value
|
349
|
+
for key, value in self._tool_manager._tools.items() # type: ignore
|
350
|
+
if not key.startswith(f"{server_name}_")
|
351
|
+
}
|
273
352
|
|
274
|
-
#
|
275
|
-
|
276
|
-
|
277
|
-
|
353
|
+
# Remove transformations with matching prefix (server name)
|
354
|
+
self._tool_manager.transformations = { # type: ignore
|
355
|
+
key: value
|
356
|
+
for key, value in self._tool_manager.transformations.items() # type: ignore
|
357
|
+
if not key.startswith(f"{server_name}_")
|
358
|
+
}
|
359
|
+
|
360
|
+
# Remove resources with matching prefix (server name)
|
361
|
+
self._resource_manager._resources = { # type: ignore
|
362
|
+
key: value
|
363
|
+
for key, value in self._resource_manager._resources.items() # type: ignore
|
364
|
+
if not has_resource_prefix(key, server_name, self.resource_prefix_format) # type: ignore
|
365
|
+
}
|
366
|
+
|
367
|
+
# Remove templates with matching prefix (server name)
|
368
|
+
self._resource_manager._templates = { # type: ignore
|
369
|
+
key: value
|
370
|
+
for key, value in self._resource_manager._templates.items() # type: ignore
|
371
|
+
if not has_resource_prefix(key, server_name, self.resource_prefix_format) # type: ignore
|
372
|
+
}
|
373
|
+
|
374
|
+
# Remove prompts with matching prefix (server name)
|
375
|
+
self._prompt_manager._prompts = { # type: ignore
|
376
|
+
key: value
|
377
|
+
for key, value in self._prompt_manager._prompts.items() # type: ignore
|
378
|
+
if not key.startswith(f"{server_name}_")
|
379
|
+
}
|
278
380
|
|
279
381
|
log.info(f"🧹 Unmounted server {server_name} and cleared references")
|
280
382
|
return True
|
281
383
|
|
282
|
-
async def
|
283
|
-
"""
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
384
|
+
async def list_all_servers_tools_parallel(self) -> list[Tool]:
|
385
|
+
"""Reload all servers' tools in parallel.
|
386
|
+
Reimplements FastMCP's ToolManager._list_tools method with parallel execution.
|
387
|
+
"""
|
388
|
+
|
389
|
+
# Execute all server reloads in parallel
|
390
|
+
list_tasks = [
|
391
|
+
server.server._list_tools()
|
392
|
+
for server in self._tool_manager._mounted_servers # type: ignore
|
393
|
+
]
|
394
|
+
|
395
|
+
log.debug(f"Starting reload for {len(list_tasks)} servers' tools in parallel")
|
396
|
+
start_time = time.perf_counter()
|
397
|
+
all_tools: dict[str, Tool] = {}
|
398
|
+
if list_tasks:
|
399
|
+
# Use return_exceptions=True to prevent one failing server from breaking everything
|
400
|
+
tools_lists = await asyncio.gather(*list_tasks, return_exceptions=True)
|
401
|
+
for server, tools_result in zip(
|
402
|
+
self._tool_manager._mounted_servers, # type: ignore
|
403
|
+
tools_lists,
|
404
|
+
strict=False,
|
405
|
+
):
|
406
|
+
if isinstance(tools_result, Exception):
|
407
|
+
log.warning(f"Failed to get tools from server {server.prefix}: {tools_result}")
|
408
|
+
continue
|
409
|
+
|
410
|
+
tools_list = tools_result
|
411
|
+
if not tools_list or not isinstance(tools_list, list):
|
412
|
+
continue
|
413
|
+
|
414
|
+
tools_dict = {t.key: t for t in tools_list} # type: ignore
|
415
|
+
if server.prefix:
|
416
|
+
for tool in tools_dict.values():
|
417
|
+
prefixed_tool = tool.model_copy( # type: ignore
|
418
|
+
key=f"{server.prefix}_{tool.key}" # type: ignore
|
419
|
+
)
|
420
|
+
all_tools[prefixed_tool.key] = prefixed_tool # type: ignore
|
421
|
+
else:
|
422
|
+
all_tools.update(tools_dict) # type: ignore
|
423
|
+
log.debug(
|
424
|
+
f"Saved {len(all_tools)} tools from {len([r for r in tools_lists if not isinstance(r, Exception)])} servers"
|
425
|
+
)
|
426
|
+
else:
|
427
|
+
all_tools = {}
|
428
|
+
|
429
|
+
# Add local tools
|
430
|
+
all_tools.update(self._tool_manager._tools) # type: ignore
|
431
|
+
|
432
|
+
transformed_tools = apply_transformations_to_tools(
|
433
|
+
tools=all_tools,
|
434
|
+
transformations=self._tool_manager.transformations,
|
435
|
+
)
|
436
|
+
|
437
|
+
final_tools_list = list(transformed_tools.values())
|
438
|
+
|
439
|
+
end_time = time.perf_counter()
|
440
|
+
log.debug(f"Time taken to reload all servers' tools: {end_time - start_time:.1f} seconds")
|
441
|
+
return final_tools_list
|
299
442
|
|
300
443
|
async def initialize(self) -> None:
|
301
444
|
"""Initialize the FastMCP server using unified composite proxy approach."""
|
302
445
|
log.info("Initializing Single User MCP server with composite proxy")
|
303
446
|
log.debug(f"Available MCP servers in config: {[s.name for s in Config().mcp_servers]}")
|
304
|
-
|
447
|
+
start_time = time.perf_counter()
|
305
448
|
# Get all enabled servers
|
306
449
|
enabled_servers = [s for s in Config().mcp_servers if s.enabled]
|
307
450
|
log.info(
|
308
451
|
f"Found {len(enabled_servers)} enabled servers: {[s.name for s in enabled_servers]}"
|
309
452
|
)
|
310
453
|
|
311
|
-
#
|
312
|
-
for
|
313
|
-
|
454
|
+
# Figure out which servers are to be unmounted
|
455
|
+
enabled_server_names = {s.name for s in enabled_servers}
|
456
|
+
servers_to_unmount = [s for s in mounted_servers if s not in enabled_server_names]
|
314
457
|
|
315
|
-
#
|
316
|
-
|
317
|
-
if not success:
|
318
|
-
log.error("Failed to create composite proxy")
|
319
|
-
return
|
458
|
+
# Figure out which servers are to be mounted
|
459
|
+
servers_to_mount = [s.name for s in enabled_servers if s.name not in mounted_servers]
|
320
460
|
|
321
|
-
|
461
|
+
# Unmount those servers (quick)
|
462
|
+
for server_name in servers_to_unmount:
|
463
|
+
await self.unmount(server_name)
|
322
464
|
|
323
|
-
#
|
324
|
-
|
325
|
-
|
326
|
-
log.debug("Reloading resource list...")
|
327
|
-
_ = await self._resource_manager.list_resources()
|
328
|
-
log.debug("Reloading prompt list...")
|
329
|
-
_ = await self._prompt_manager.list_prompts()
|
330
|
-
log.debug("Reloading complete")
|
465
|
+
# Mount those servers (async gathered bc import does network roundtrip)
|
466
|
+
mount_tasks = [self.mount_server(server_name) for server_name in servers_to_mount]
|
467
|
+
await asyncio.gather(*mount_tasks)
|
331
468
|
|
332
|
-
|
333
|
-
log.debug(
|
334
|
-
|
335
|
-
|
469
|
+
log.info("✅ Single User MCP server initialized with composite proxy")
|
470
|
+
log.debug(
|
471
|
+
f"Time taken to initialize Single User MCP server: {time.perf_counter() - start_time:.1f} seconds"
|
472
|
+
)
|
336
473
|
|
337
474
|
def _calculate_risk_level(self, trifecta: dict[str, bool]) -> str:
|
338
475
|
"""
|
@@ -422,7 +559,7 @@ class SingleUserMCP(FastMCP[Any]):
|
|
422
559
|
"""
|
423
560
|
Get a list of all available tools. Use this tool to get an updated list of available tools.
|
424
561
|
"""
|
425
|
-
tool_list = await self.
|
562
|
+
tool_list = await self.list_all_servers_tools_parallel()
|
426
563
|
available_tools: list[str] = []
|
427
564
|
log.trace(f"Raw tool list: {tool_list}")
|
428
565
|
perms = Permissions()
|
@@ -458,7 +595,7 @@ class SingleUserMCP(FastMCP[Any]):
|
|
458
595
|
def _setup_demo_resources(self) -> None:
|
459
596
|
"""Set up built-in demo resources for testing."""
|
460
597
|
|
461
|
-
@self.resource("
|
598
|
+
@self.resource("info://builtin/app") # noqa
|
462
599
|
def builtin_get_app_config() -> dict[str, Any]:
|
463
600
|
"""Get application configuration."""
|
464
601
|
return {
|
@@ -467,7 +604,7 @@ class SingleUserMCP(FastMCP[Any]):
|
|
467
604
|
"total_mounted": len(mounted_servers),
|
468
605
|
}
|
469
606
|
|
470
|
-
log.info("✅ Added built-in demo resources:
|
607
|
+
log.info("✅ Added built-in demo resources: info://builtin/app")
|
471
608
|
|
472
609
|
def _setup_demo_prompts(self) -> None:
|
473
610
|
"""Set up built-in demo prompts for testing."""
|
@@ -1,40 +0,0 @@
|
|
1
|
-
src/__init__.py,sha256=bEYMwBiuW9jzF07iWhas4Vb30EcpnqfpNfz_Q6yO1jU,209
|
2
|
-
src/__main__.py,sha256=kQsaVyzRa_ESC57JpKDSQJAHExuXme0rM5beJsYxFeA,161
|
3
|
-
src/cli.py,sha256=PH2qPLma0PO1L75OSK06IdPy8RB5gTBp2R1HkacCQ0Q,4736
|
4
|
-
src/config.py,sha256=oO89omLCLoPfEGH6j4WSHQgZbhJZnYou-qdxY54VfDo,11037
|
5
|
-
src/config.pyi,sha256=FgehEGli8ZXSjGlANBgMGv5497q4XskQciOc1fUcxqM,2033
|
6
|
-
src/events.py,sha256=aFQrVXDIZwt55Dz6OtyoXu2yi9evqo-8jZzo3CR2Tto,4965
|
7
|
-
src/oauth_manager.py,sha256=MJ1gHVKiu-pMbskSCxRlZ6xP4wJOr-ELydOdgdUBKKw,9969
|
8
|
-
src/oauth_override.py,sha256=C7QS8sPA6JqJDiNZA0FGeXcB7jU-yYu-k8V56QVpsqU,393
|
9
|
-
src/permissions.py,sha256=dERB8s40gDInsbXtu0pJYDDuZ3_kD8rxXyYYTfrG3qs,11621
|
10
|
-
src/server.py,sha256=WseZks-r07tq7UGX0XFc0OUEzUrB9MGfDiHeYHa3NEE,46593
|
11
|
-
src/single_user_mcp.py,sha256=2xQgrxqulTD_1HySoMNZD5AoADJS5pz41Gn-tEFgBHI,18760
|
12
|
-
src/telemetry.py,sha256=-RZPIjpI53zbsKmp-63REeZ1JirWHV5WvpSRa2nqZEk,11321
|
13
|
-
src/vulture_whitelist.py,sha256=CjBOSsarbzbQt_9ATWc8MbruBsYX3hJVa_ysbRD9ZYM,135
|
14
|
-
src/frontend_dist/index.html,sha256=s95FMkH8VLisvawLH7bZxbLzRUFvMhHkH6ZMzpVBngs,673
|
15
|
-
src/frontend_dist/sw.js,sha256=rihX1es-vWwjmtnXyaksJjs2dio6MVAOTAWwQPeJUYw,2164
|
16
|
-
src/frontend_dist/assets/index-BUUcUfTt.js,sha256=awoyPI6u0v6ao2iarZdSkrSDUvyU8aNkMLqHMvgVgyY,257666
|
17
|
-
src/frontend_dist/assets/index-o6_8mdM8.css,sha256=nwmX_6q55mB9463XN2JM8BdeihjkALpQK83Fc3_iGvE,15936
|
18
|
-
src/mcp_importer/__init__.py,sha256=Mk59pVr7OMGfYGWeSYk8-URfhIcrs3SPLYS7fmJbMII,275
|
19
|
-
src/mcp_importer/__main__.py,sha256=mFcxXFqJMC0SFEqIP-9WVEqLJSYqShC0x1Ht7PQZPm8,479
|
20
|
-
src/mcp_importer/api.py,sha256=N5oVaTj3OMIROLx__UOSr60VMqXXX20JsOHmeHIGP48,17431
|
21
|
-
src/mcp_importer/cli.py,sha256=Pe0GLWm1nMd1VuNXOSkxIrFZuGNFc9dNvfBsvf-bdBI,3487
|
22
|
-
src/mcp_importer/export_cli.py,sha256=Fw0jDQCI8gGW4BDrJLzWjLUtV4q6v0h2QZ7HF1V2Jcg,6279
|
23
|
-
src/mcp_importer/exporters.py,sha256=fSgl6seduoXFp7YnKH26UEaC1sFBnd4whSut7CJLBQs,11348
|
24
|
-
src/mcp_importer/import_api.py,sha256=wD5yqxWwFfn1MQNKE79rEeyZODdmPgUDhsRYdCJYh4Q,59
|
25
|
-
src/mcp_importer/importers.py,sha256=zGN8lT7qQJ95jDTd-ck09j_w5PSvH-uj33TILoHfHbs,2191
|
26
|
-
src/mcp_importer/merge.py,sha256=KIGT7UgbAm07-LdyoUXEJ7ABSIiPTFlj_qjz669yFxg,1569
|
27
|
-
src/mcp_importer/parsers.py,sha256=MDhzODsvX5t1U_CI8byBxCpx6rA4WkpqX4bJMiNE74s,6298
|
28
|
-
src/mcp_importer/paths.py,sha256=4L-cPr7KCM9X9gAUP7Da6ictLNrPWuQ_IM419zqY-2I,2700
|
29
|
-
src/mcp_importer/quick_cli.py,sha256=Vv2vjNzpSOaic0YHFbPAuX0nZByawS2kDw6KiCtEX3A,1798
|
30
|
-
src/mcp_importer/types.py,sha256=nSaOLGqpCmA3R14QCO6wrpgX75VaLz9HfslUWzw_GPQ,102
|
31
|
-
src/middleware/data_access_tracker.py,sha256=bArBffWgYmvxOx9z_pgXQhogvnWQcc1m6WvEblDD4gw,15039
|
32
|
-
src/middleware/session_tracking.py,sha256=5W1VH9HNqIZeX0HNxDEm41U4GY6SqKSXtApDEeZK2qo,23084
|
33
|
-
src/setup_tui/__init__.py,sha256=mDFrQoiOtQOHc0sFfGKrNXVLEDeB1S0O5aISBVzfxYo,184
|
34
|
-
src/setup_tui/main.py,sha256=BM6t8YzWOSn2hDzVhp_nOol93NoHf6fiP4uFLOpx-BQ,11082
|
35
|
-
src/tools/io.py,sha256=hhc4pv3eUzYWSZ7BbThclxSMwWBQaGMoGsItIPf_pco,1047
|
36
|
-
open_edison-0.1.64.dist-info/METADATA,sha256=1X57SDfaooRExNvcCN-vHr1HH1QzbrVLWhJaNIaIEDI,11993
|
37
|
-
open_edison-0.1.64.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
38
|
-
open_edison-0.1.64.dist-info/entry_points.txt,sha256=YiGNm9x2I00hgT10HDyB4gxC1LcaV_mu8bXFjolu0Yw,171
|
39
|
-
open_edison-0.1.64.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
40
|
-
open_edison-0.1.64.dist-info/RECORD,,
|