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

@ -0,0 +1,41 @@
"""MCP subprocess env: env_schema defaults from registry (Cursor mcpServers.env import)."""
from __future__ import annotations
import os
import unittest
from unittest.mock import patch
from runtime.tools.mcp.runtime import McpProcessRuntime
class McpEnvDefaultsTests(unittest.TestCase):
@patch("runtime.operations.mcp_env.mcp_local_env_merged", return_value={})
def test_env_schema_default_used_when_not_in_os_environ(self, _mock: object) -> None:
os.environ.pop("NETX_API_URL", None)
try:
env = McpProcessRuntime._build_runtime_env(
["NETX_API_URL"],
{"NETX_API_URL": "http://10.0.0.5:8890"},
)
assert env is not None
self.assertEqual(env.get("NETX_API_URL"), "http://10.0.0.5:8890")
finally:
pass
@patch("runtime.operations.mcp_env.mcp_local_env_merged", return_value={})
def test_mcp_local_and_os_environ_override_schema_default(self, _mock: object) -> None:
os.environ["NETX_API_URL"] = "http://from-host:8890"
try:
env = McpProcessRuntime._build_runtime_env(
["NETX_API_URL"],
{"NETX_API_URL": "http://from-schema:8890"},
)
assert env is not None
self.assertEqual(env.get("NETX_API_URL"), "http://from-host:8890")
finally:
os.environ.pop("NETX_API_URL", None)
if __name__ == "__main__":
unittest.main()

View file

@ -23,6 +23,13 @@ class McpInstallerTests(unittest.TestCase):
self.assertIn(" install -g ", res.install_command)
self.assertIn("demo-server@1.0.0", res.install_command)
def test_install_local_skips_package_step(self) -> None:
m = McpServerManifest(server_id="netx", source_type="local", source_ref="netx-mcp-http", entry_command="python")
res = install_mcp_server(m, dry_run=False)
self.assertTrue(res.ok)
self.assertEqual(res.install_command, "")
self.assertEqual((res.details or {}).get("reason"), "local_source_no_package_install")
def test_invalid_source(self) -> None:
m = McpServerManifest(server_id="x", source_type="invalid", source_ref="x")
res = install_mcp_server(m, dry_run=True)

View file

@ -0,0 +1,35 @@
"""Expert registry: netx builtin tools gated by OCLAW_NETX_BUILTIN_TOOLS."""
from __future__ import annotations
import os
import pytest
from runtime.tools import expert_registry
@pytest.fixture(autouse=True)
def _clear_expert_cache():
expert_registry._CACHED_FACTORIES_BY_EXPERT = None
expert_registry._CACHED_SPECS_BY_EXPERT = None
yield
expert_registry._CACHED_FACTORIES_BY_EXPERT = None
expert_registry._CACHED_SPECS_BY_EXPERT = None
def test_netx_tools_skipped_when_builtin_disabled(monkeypatch):
monkeypatch.delenv("OCLAW_NETX_BUILTIN_TOOLS", raising=False)
factories = expert_registry.discover_expert_tool_factories()
network_ops = factories.get("network_ops") or []
names = {f().name for f in network_ops}
assert not any(n.startswith("netx_") for n in names)
def test_netx_tools_registered_when_builtin_enabled(monkeypatch):
monkeypatch.setenv("OCLAW_NETX_BUILTIN_TOOLS", "1")
factories = expert_registry.discover_expert_tool_factories()
network_ops = factories.get("network_ops") or []
names = {f().name for f in network_ops}
assert "netx_query_ume_alarms" in names
assert "netx_exec_managed_ne" in names