feat(mcp): integrate netx via netx_mcp and improve MCP install

- Gate builtin netx tools; ops docs/skills use mcp__netx__*

- Import Cursor mcpServers JSON; pass env_schema defaults to subprocess

- Allow source_type=local; UTF-8 MCP stdio on Windows

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
oliver 2026-05-30 15:44:34 +08:00
parent bbd7b0300f
commit a0a3f4b65f
17 changed files with 408 additions and 536 deletions

View file

@ -3,6 +3,7 @@ from __future__ import annotations
import importlib.util
import inspect
import logging
import os
from collections.abc import Callable
from pathlib import Path
from typing import Any
@ -21,6 +22,22 @@ _DEPRECATED_TOOL_NAMES: set[str] = {
}
def netx_builtin_tools_enabled() -> bool:
"""When false, skip ``network_ops/netx_tools.py`` factories (use MCP ``mcp__netx__*`` instead)."""
return str(os.getenv("OCLAW_NETX_BUILTIN_TOOLS") or "0").strip().lower() not in {
"0",
"false",
"no",
"off",
}
def _skip_expert_module(module_path: Path) -> bool:
if module_path.stem == "netx_tools" and not netx_builtin_tools_enabled():
return True
return False
def _load_module_from_path(module_path: Path, module_name: str) -> Any | None:
try:
spec = importlib.util.spec_from_file_location(module_name, module_path)
@ -52,12 +69,14 @@ def discover_expert_tool_factories() -> dict[str, list[ToolFactory]]:
for module_path in sorted(expert_dir.glob("*.py")):
if module_path.name == "__init__.py":
continue
if _skip_expert_module(module_path):
continue
mod_name = f"runtime.tools.experts.{expert}.{module_path.stem}"
module = _load_module_from_path(module_path, mod_name)
if module is None:
continue
exported = getattr(module, "__all__", None)
if isinstance(exported, list) and exported:
if isinstance(exported, list):
for name in sorted(exported):
value = getattr(module, name, None)
if callable(value) and name.endswith("_tool"):