paperswithcode-mcp / mcp_server /tests /test_parity.py
nielsr's picture
nielsr HF Staff
Prototype PwC MCP Space from pwc-cli@6a547659
ee0b555 verified
Raw History Blame Contribute Delete
3.83 kB
"""Every read-only CLI command is a tool, and every research flag a parameter."""
from __future__ import annotations
import argparse
import asyncio
from mcp.client import Client
from pwc_cli import queries
from pwc_mcp.server import (
ENTITY_PARAMETERS,
MCP_ONLY_PARAMETERS,
PARAMETER_NAMES,
TOOL_COMMANDS,
build_server,
cli_options,
)
from test_server import StubCatalog
# Deliberate default divergences from the CLI, documented in README.md: the
# hosted service caps rows per response at 25 (list_benchmarks and list_tasks
# leave the row count to the server instead of the CLI's 50).
DEFAULT_EXCEPTIONS = {
("list_benchmarks", "limit"): None,
("list_methods", "limit"): 25,
("list_tasks", "limit"): None,
}
# argparse placeholders for "not given"; the MCP schema spells these as null.
UNSET_DEFAULTS = (None, argparse.SUPPRESS, (), [])
def _tools():
async def exercise():
async with Client(build_server(StubCatalog())) as client:
return {tool.name: tool for tool in (await client.list_tools()).tools}
return asyncio.run(exercise())
def _enum_values(schema, root):
if "$ref" in schema:
schema = root["$defs"][schema["$ref"].rsplit("/", 1)[1]]
values = list(schema.get("enum") or [])
for branch in schema.get("anyOf") or []:
values.extend(_enum_values(branch, root))
return values
def _mcp_name(tool: str, destination: str) -> str:
if destination == "name":
return ENTITY_PARAMETERS[tool]
return PARAMETER_NAMES.get(destination, destination)
def test_every_read_only_cli_command_has_exactly_one_tool():
assert sorted(TOOL_COMMANDS.values()) == sorted(queries.READ_ONLY_COMMANDS)
assert len(set(TOOL_COMMANDS.values())) == len(TOOL_COMMANDS)
def test_every_cli_research_flag_is_a_tool_parameter_and_vice_versa():
tools = _tools()
assert set(tools) == set(TOOL_COMMANDS)
for tool, command in TOOL_COMMANDS.items():
cli = queries.query_options(command)
schema = tools[tool].input_schema
parameters = set(schema["properties"]) - MCP_ONLY_PARAMETERS.get(
tool, frozenset()
)
mapped = cli_options(tool, **dict.fromkeys(parameters))
assert set(mapped) == set(cli), tool
for destination, action in cli.items():
parameter = _mcp_name(tool, destination)
property_schema = schema["properties"][parameter]
if isinstance(action, argparse._StoreTrueAction):
assert property_schema.get("type") == "boolean", (tool, parameter)
elif action.choices and set(action.choices) != {"true", "false"}:
assert set(action.choices) <= set(
_enum_values(property_schema, schema)
), (
tool,
parameter,
)
if not action.option_strings or destination == "name":
assert parameter in schema["required"], (tool, parameter)
# Same arguments must mean the same query through `pwc` and the MCP
# tools, so every CLI default is the tool default unless documented.
if action.default not in UNSET_DEFAULTS:
expected = DEFAULT_EXCEPTIONS.get((tool, parameter), action.default)
assert property_schema.get("default") == expected, (tool, parameter)
def test_paper_references_and_entity_names_are_required_everywhere():
tools = _tools()
for tool, entity in ENTITY_PARAMETERS.items():
assert tools[tool].input_schema["required"] == [entity]
for tool in (
"get_paper_info",
"get_paper_evaluations",
"read_paper",
"get_related_papers",
"get_paper_lineage",
):
assert tools[tool].input_schema["required"] == ["paper"]