Add M7.3 CmdForge as MCP server with FastMCP, expose/deny policy, and schema mapping
This commit is contained in:
@@ -5,6 +5,7 @@ import time
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
from unittest.mock import patch, MagicMock
|
||||
from cmdforge.mcp_client import (
|
||||
DEFAULT_INHERITED_ENV,
|
||||
McpServerConfig,
|
||||
@@ -397,3 +398,99 @@ class TestMcpCli:
|
||||
monkeypatch.setattr(sys, "argv", ["cmdforge", "mcp"])
|
||||
assert main() == 0
|
||||
assert "No MCP servers configured" in capsys.readouterr().out
|
||||
|
||||
|
||||
class TestMcpServeConfig:
|
||||
def test_empty_expose_exposes_nothing(self):
|
||||
from cmdforge.mcp_client import McpServeConfig
|
||||
config = McpServeConfig(expose=[], deny=[])
|
||||
assert not config.is_exposed("summarize")
|
||||
|
||||
def test_wildcard_exposes_all_public(self):
|
||||
from cmdforge.mcp_client import McpServeConfig
|
||||
config = McpServeConfig(expose=["*"], deny=[])
|
||||
assert config.is_exposed("summarize")
|
||||
assert config.is_exposed("any-tool")
|
||||
|
||||
def test_deny_overrides_expose(self):
|
||||
from cmdforge.mcp_client import McpServeConfig
|
||||
config = McpServeConfig(expose=["*"], deny=["dangerous"])
|
||||
assert config.is_exposed("summarize")
|
||||
assert not config.is_exposed("dangerous")
|
||||
|
||||
def test_prefix_deny(self):
|
||||
from cmdforge.mcp_client import McpServeConfig
|
||||
config = McpServeConfig(expose=["*"], deny=["internal-*"])
|
||||
assert not config.is_exposed("internal-cleanup")
|
||||
assert not config.is_exposed("internal-")
|
||||
assert config.is_exposed("public-tool")
|
||||
|
||||
def test_explicit_expose(self):
|
||||
from cmdforge.mcp_client import McpServeConfig
|
||||
config = McpServeConfig(expose=["summarize", "extract"])
|
||||
assert config.is_exposed("summarize")
|
||||
assert config.is_exposed("extract")
|
||||
assert not config.is_exposed("other")
|
||||
|
||||
|
||||
class TestToolNameMapping:
|
||||
def test_simple_name_passes_through(self):
|
||||
from cmdforge.mcp_server import _cmdforge_tool_name
|
||||
assert _cmdforge_tool_name("summarize") == "summarize"
|
||||
|
||||
def test_qualified_name_uses_double_underscore(self):
|
||||
from cmdforge.mcp_server import _cmdforge_tool_name
|
||||
assert _cmdforge_tool_name("owner/tool") == "owner__tool"
|
||||
|
||||
|
||||
class TestArgumentToJsonSchema:
|
||||
def test_string_arg(self):
|
||||
from cmdforge.mcp_server import _argument_to_json_schema
|
||||
from cmdforge.tool import ToolArgument
|
||||
arg = ToolArgument(flag="--name", variable="name", description="The name")
|
||||
schema = _argument_to_json_schema(arg)
|
||||
assert schema["type"] == "string"
|
||||
assert schema["description"] == "The name"
|
||||
|
||||
def test_integer_arg_with_default(self):
|
||||
from cmdforge.tool import ToolArgument
|
||||
from cmdforge.mcp_server import _argument_to_json_schema
|
||||
arg = ToolArgument(flag="--limit", variable="limit", default="10")
|
||||
setattr(arg, "type", "integer")
|
||||
schema = _argument_to_json_schema(arg)
|
||||
assert schema["type"] == "integer"
|
||||
assert schema["default"] == "10"
|
||||
|
||||
def test_arg_with_enum(self):
|
||||
from cmdforge.tool import ToolArgument
|
||||
from cmdforge.mcp_server import _argument_to_json_schema
|
||||
arg = ToolArgument(flag="--mode", variable="mode")
|
||||
setattr(arg, "enum", ["fast", "accurate"])
|
||||
schema = _argument_to_json_schema(arg)
|
||||
assert schema["enum"] == ["fast", "accurate"]
|
||||
|
||||
|
||||
class TestBuildToolSchema:
|
||||
def test_builds_input_schema(self, tmp_path):
|
||||
from cmdforge.tool import Tool, ToolArgument, save_tool
|
||||
from cmdforge.mcp_server import _build_tool_schema
|
||||
|
||||
with patch('cmdforge.tool.TOOLS_DIR', tmp_path / ".cmdforge"):
|
||||
tool = Tool(
|
||||
name="greet",
|
||||
description="Greet someone",
|
||||
arguments=[
|
||||
ToolArgument(flag="--name", variable="name",
|
||||
description="Who to greet", default="World"),
|
||||
ToolArgument(flag="--count", variable="count", default="1"),
|
||||
],
|
||||
output="Hello {name} x{count}\n",
|
||||
)
|
||||
|
||||
schema = _build_tool_schema(tool)
|
||||
|
||||
assert schema["name"] == "greet"
|
||||
assert "Greet" in schema["description"]
|
||||
assert schema["inputSchema"]["type"] == "object"
|
||||
assert "name" in schema["inputSchema"]["properties"]
|
||||
assert "count" in schema["inputSchema"]["properties"]
|
||||
|
||||
Reference in New Issue
Block a user