ltspice://symbols, ltspice://examples, ltspice://status raised TypeError on read -- they called list_symbols/list_examples/check_installation, which are @mcp.tool() FunctionTool objects (not callable). Split those three into plain *_impl functions + thin tool wrappers, and point the resources at the impls. The other library tools move verbatim. Adds test_resources.py to read every resource (regression guard). Library tool outputs verified byte-identical old-vs-new. server.py: 2168 -> 1876 lines.
60 lines
1.9 KiB
Python
60 lines
1.9 KiB
Python
"""Resource-read regression tests.
|
|
|
|
ltspice://symbols, ltspice://examples, and ltspice://status previously raised
|
|
TypeError on read because the resource functions called @mcp.tool()-decorated
|
|
names (FunctionTool objects are not callable). These tests read every resource
|
|
to guard against that regressing.
|
|
"""
|
|
|
|
import inspect
|
|
import json
|
|
|
|
from mcltspice.library_tools import (
|
|
check_installation_impl,
|
|
list_examples_impl,
|
|
list_symbols_impl,
|
|
)
|
|
from mcltspice.server import mcp # importing registers all resources
|
|
|
|
|
|
async def _read(uri: str) -> str:
|
|
resources = await mcp.get_resources()
|
|
assert uri in resources, f"{uri} not registered"
|
|
out = resources[uri].fn()
|
|
if inspect.isawaitable(out):
|
|
out = await out
|
|
return out
|
|
|
|
|
|
class TestResourceReadsWithoutError:
|
|
async def test_symbols_resource_reads(self):
|
|
out = await _read("ltspice://symbols")
|
|
json.loads(out) # valid JSON, no TypeError
|
|
|
|
async def test_examples_resource_reads(self):
|
|
out = await _read("ltspice://examples")
|
|
json.loads(out)
|
|
|
|
async def test_status_resource_reads(self):
|
|
out = await _read("ltspice://status")
|
|
data = json.loads(out)
|
|
assert "valid" in data
|
|
|
|
async def test_templates_resource_reads(self):
|
|
out = await _read("ltspice://templates")
|
|
data = json.loads(out)
|
|
assert "netlist_templates" in data and "schematic_templates" in data
|
|
|
|
|
|
class TestLibraryImplsAreCallable:
|
|
"""The fix: the *_impl functions must be plain callables (not FunctionTool)."""
|
|
|
|
def test_impls_are_callable(self):
|
|
assert callable(list_symbols_impl)
|
|
assert callable(list_examples_impl)
|
|
assert callable(check_installation_impl)
|
|
|
|
def test_check_installation_impl_shape(self):
|
|
result = check_installation_impl()
|
|
assert set(result) >= {"valid", "message", "paths", "lib_exists"}
|