mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-11-03 05:51:15 +02:00
refactor: sink shared REST handlers into gns3_client, drop gns3fy wrappers
- Slim custom_gns3fy.py to connector-only Gns3Connector and rename to connector.py; delete the unused Node/Link/Project dataclasses and endpoint wrapper methods (~3000 lines) - Move the MCP node/link handler implementations into gns3_client/api_handlers.py as the shared REST client layer consumed by both the MCP service and copilot tools; add available_filters handler (exposed as link_available_filters MCP tool) and build_gns3_ctx() for copilot callers - Rewrite the tools_v2 node/link tools on top of the handlers: batch lifecycle actions now run in parallel, node creation is a single POST, project-wide status reads replace per-node GETs - Port Project.nodes_inventory/links_summary aggregation into project_inventory.py (output shape preserved) and rewrite the topology reader / project info tools on it, dropping the unused stats/snapshots/drawings calls - Delete the dead mcp/nodes.py and mcp/links.py (NODE_TOOLS/LINK_TOOLS had no consumers; __init__ imports handlers from api_handlers) - Retarget mcp handler tests to patch api_handlers._get_connector and replace test_custom_gns3fy.py with inventory contract tests
This commit is contained in:
35 files changed
+2180
-5298
No files matched your search
@@ -440,7 +440,7 @@ sequenceDiagram
|
||||
- **FastMCP** (Anthropic MCP SDK) is used for tool registration and SSE transport
|
||||
- The SSE app is mounted as a Starlette sub-application under `/v3/mcp/transport`
|
||||
- **Auth:** JWT validation via `auth_service`. API key (`gns3_<uuid>_<secret>`) extracts UUID for O(1) DB lookup, runs bcrypt in thread pool, returns a fresh JWT — subsequent calls use the JWT with zero extra bcrypt.
|
||||
- Tool handlers use `Gns3Connector` (from `custom_gns3fy`) to call GNS3's own REST API, keeping the MCP layer decoupled
|
||||
- Tool handlers use `Gns3Connector` (from `gns3_copilot.gns3_client.connector`) via the shared handler layer (`gns3_copilot.gns3_client.api_handlers`), keeping the MCP layer decoupled
|
||||
- The JWT token is stored in a `contextvars.ContextVar` so it is available within tool handler threads (Python ≥ 3.9 propagates contextvars through `asyncio.to_thread`)
|
||||
|
||||
### Console WebSocket
|
||||
|
||||
@@ -114,8 +114,8 @@ if await asyncio.to_thread(bcrypt.checkpw, secret.encode(), db_key.key_hash.enco
|
||||
| Node creation Pool | 5 | 100 | `controller/project.py` |
|
||||
| Link creation Pool | 5 | 100 | `controller/project.py` |
|
||||
| MCP BATCH_MAX_WORKERS | 10 | 100 | `agent/mcp/nodes.py` |
|
||||
| MCP HTTP timeout | 10s | 30s | `agent/gns3_copilot/gns3_client/custom_gns3fy.py` |
|
||||
| HTTP connection pool | 10 (default) | 500/1000 | `agent/gns3_copilot/gns3_client/custom_gns3fy.py` |
|
||||
| MCP HTTP timeout | 10s | 30s | `agent/gns3_copilot/gns3_client/connector.py` |
|
||||
| HTTP connection pool | 10 (default) | 500/1000 | `agent/gns3_copilot/gns3_client/connector.py` |
|
||||
| Start nodes Pool | 3 | 3 (unchanged) | `controller/project.py` |
|
||||
|
||||
### 8. MCP Auth Returns JWT
|
||||
@@ -144,5 +144,5 @@ if user:
|
||||
| `gns3server/agent/mcp/__init__.py` | Auth returns JWT, tool enhancements |
|
||||
| `gns3server/agent/mcp/nodes.py` | fields filter, inherited template_id, name passthrough |
|
||||
| `gns3server/agent/mcp/links.py` | fields filter, compact array format |
|
||||
| `gns3server/agent/gns3_copilot/gns3_client/custom_gns3fy.py` | Timeout 30s, connection pool 500/1000 |
|
||||
| `gns3server/agent/gns3_copilot/gns3_client/connector.py` | Timeout 30s, connection pool 500/1000 |
|
||||
| `gns3server/utils/images.py` | md5sum cache error → warning |
|
||||
@@ -516,29 +516,39 @@ gns3server/agent/gns3_copilot/tools_v2/
|
||||
|
||||
### API Integration
|
||||
|
||||
The tools use the `Node` and `Link` classes from `custom_gns3fy`:
|
||||
The tools call the shared REST handler layer (`gns3_copilot.gns3_client.api_handlers`), the same functions the MCP service exposes as MCP tools:
|
||||
|
||||
```python
|
||||
from gns3server.agent.gns3_copilot.gns3_client import Node, Link, get_gns3_connector
|
||||
from gns3server.agent.gns3_copilot.gns3_client.api_handlers import (
|
||||
build_gns3_ctx, create_node_handler, create_link_handler,
|
||||
get_nodes_handler, start_node_handler, stop_node_handler,
|
||||
suspend_node_handler, update_node_handler,
|
||||
)
|
||||
|
||||
# Get templates
|
||||
templates = get_gns3_connector().get_templates()
|
||||
gns3_ctx = build_gns3_ctx() # JWT + server URL from the request context
|
||||
|
||||
# Create node
|
||||
node = Node(project_id=project_id, template_id=template_id, x=x, y=y, connector=gns3_server)
|
||||
node.create()
|
||||
# Create node (single POST, batch mode is parallel)
|
||||
created = create_node_handler(
|
||||
{"project_id": project_id, "template_id": template_id, "x": x, "y": y, "name": name},
|
||||
gns3_ctx,
|
||||
)
|
||||
|
||||
# Create link
|
||||
link = Link(project_id=project_id, connector=gns3_server, nodes=[...])
|
||||
link.create()
|
||||
link = create_link_handler(
|
||||
{"project_id": project_id, "nodes": [{"node_id": nid, "adapter_number": 0, "port_number": 0}, ...]},
|
||||
gns3_ctx,
|
||||
)
|
||||
|
||||
# Update node name
|
||||
node = Node(project_id=project_id, node_id=node_id, connector=gns3_server)
|
||||
node.update(name=new_name)
|
||||
# Update node name — the PUT response is the updated node
|
||||
updated = update_node_handler({"project_id": project_id, "node_id": node_id, "name": new_name}, gns3_ctx)
|
||||
|
||||
# Start/stop/suspend node
|
||||
node = Node(project_id=project_id, node_id=node_id, connector=gns3_server)
|
||||
node.start() # or node.stop() / node.suspend()
|
||||
# Start/stop/suspend nodes (node_ids batch runs in parallel)
|
||||
start_node_handler({"project_id": project_id, "node_ids": [nid1, nid2]}, gns3_ctx)
|
||||
stop_node_handler({"project_id": project_id, "node_ids": [nid1, nid2]}, gns3_ctx)
|
||||
suspend_node_handler({"project_id": project_id, "node_ids": [nid1, nid2]}, gns3_ctx)
|
||||
|
||||
# Node listing/status (single call for the whole project)
|
||||
listing = get_nodes_handler({"project_id": project_id}, gns3_ctx)
|
||||
```
|
||||
|
||||
### Progress Tracking
|
||||
|
||||
Reference in new issue
Block a user