#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Timestamp: 2026-01-27
# File: tests/python/test_scitex_writer_mcp.py
"""Tests for scitex_writer MCP module.
The MCP server exposes comprehensive tools for manuscript operations.
"""
import asyncio
from pathlib import Path
def _get_tool_names(mcp) -> list:
"""Get registered tool names across FastMCP versions."""
try:
tools = asyncio.run(mcp.get_tools()) # FastMCP >= 2.3
return list(tools.keys())
except AttributeError:
tools = asyncio.run(mcp._list_tools()) # FastMCP 2.0–2.2
return [t.name for t in tools]
class TestMcpModule:
"""Test MCP module functionality."""
def test_mcp_import(self):
"""Test that MCP module can be imported."""
from scitex_writer._mcp import mcp
assert mcp is not None
# Server name uses branding
from scitex_writer._branding import get_mcp_server_name
assert mcp.name == get_mcp_server_name()
def test_instructions_via_branding(self):
"""Test that instructions are available via branding module."""
from scitex_writer._branding import get_mcp_instructions
instructions = get_mcp_instructions()
assert instructions is not None
assert isinstance(instructions, str)
assert "manuscript" in instructions
def test_run_server_function_exists(self):
"""Test that run_server function exists."""
from scitex_writer._mcp import run_server
assert callable(run_server)
class TestToolRegistration:
"""Test that the usage tool is registered."""
def test_usage_tool_registered(self):
"""Test that usage tool is registered with MCP."""
from scitex_writer._mcp import mcp
assert "usage" in _get_tool_names(mcp)
def test_tool_count(self):
"""Test that expected number of tools are registered."""
from scitex_writer._mcp import mcp
# 30+ tools: usage(1), project(4), compile(4), tables(5), figures(5),
# bib(6), guidelines(3), prompts(1), export(1), claim(6), update(1)
assert len(_get_tool_names(mcp)) >= 28
class TestUsageTool:
"""Test usage tool functionality."""
def test_usage_returns_instructions(self):
"""Test usage tool returns instructions."""
from scitex_writer._mcp import mcp
assert "usage" in _get_tool_names(mcp)
class TestInstructionsContent:
"""Test instructions content."""
def test_instructions_has_setup(self):
"""Test that instructions contains setup/clone info."""
from scitex_writer._branding import get_mcp_instructions
instructions = get_mcp_instructions()
assert "git clone" in instructions
assert "scitex-writer" in instructions
def test_instructions_has_structure(self):
"""Test that instructions contains project structure info."""
from scitex_writer._branding import get_mcp_instructions
instructions = get_mcp_instructions()
assert "00_shared/" in instructions
assert "01_manuscript/" in instructions
assert "02_supplementary/" in instructions
assert "03_revision/" in instructions
def test_instructions_has_editable_files(self):
"""Test that instructions lists editable files."""
from scitex_writer._branding import get_mcp_instructions
instructions = get_mcp_instructions()
assert "abstract.tex" in instructions
assert "introduction.tex" in instructions
assert "methods.tex" in instructions
assert "results.tex" in instructions
assert "discussion.tex" in instructions
def test_instructions_has_compile_options(self):
"""Test that instructions lists compile options."""
from scitex_writer._branding import get_mcp_instructions
instructions = get_mcp_instructions()
assert "--draft" in instructions
assert "--no_figs" in instructions
assert "--no_tables" in instructions
assert "--no_diff" in instructions
def test_instructions_has_output_info(self):
"""Test that instructions lists output files."""
from scitex_writer._branding import get_mcp_instructions
instructions = get_mcp_instructions()
assert "manuscript.pdf" in instructions
def test_instructions_has_scitex_writer_root(self):
"""Test that instructions explains SCITEX_WRITER_ROOT."""
from scitex_writer._branding import get_mcp_instructions
instructions = get_mcp_instructions()
assert "SCITEX_WRITER_ROOT" in instructions
assert "compile.sh" in instructions
def test_instructions_has_revision_info(self):
"""Test that instructions mentions revision directory."""
from scitex_writer._branding import get_mcp_instructions
instructions = get_mcp_instructions()
assert "03_revision" in instructions
def test_instructions_has_bib_merge(self):
"""Test that instructions explains bib auto-merge."""
from scitex_writer._branding import get_mcp_instructions
instructions = get_mcp_instructions()
assert "bib_files/" in instructions
assert "auto-merge" in instructions.lower() or "merge" in instructions.lower()
class TestHandlersModule:
"""Test handlers module can be imported."""
def test_handlers_import(self):
"""Test that handlers module can be imported."""
from scitex_writer._mcp import handlers
assert handlers is not None
def test_handlers_functions_exist(self):
"""Test that all handler functions exist (not registered, but available)."""
from scitex_writer._mcp import handlers
assert callable(handlers.clone_project)
assert callable(handlers.compile_manuscript)
assert callable(handlers.compile_supplementary)
assert callable(handlers.compile_revision)
assert callable(handlers.get_project_info)
assert callable(handlers.get_pdf)
assert callable(handlers.list_document_types)
assert callable(handlers.csv_to_latex)
assert callable(handlers.latex_to_csv)
assert callable(handlers.pdf_to_images)
assert callable(handlers.list_figures)
assert callable(handlers.convert_figure)
class TestUtilsModule:
"""Test utils module."""
def test_utils_import(self):
"""Test that utils module can be imported."""
from scitex_writer._mcp.utils import resolve_project_path, run_compile_script
assert callable(resolve_project_path)
assert callable(run_compile_script)
def test_resolve_project_path_relative(self):
"""Test resolve_project_path with relative path."""
from scitex_writer._mcp.utils import resolve_project_path
result = resolve_project_path(".")
assert result.is_absolute()
def test_resolve_project_path_absolute(self):
"""Test resolve_project_path with absolute path."""
from scitex_writer._mcp.utils import resolve_project_path
result = resolve_project_path("/tmp")
assert result == Path("/tmp")
class TestBranding:
"""Test branding module functionality."""
def test_default_brand_values(self):
"""Test default branding values."""
from scitex_writer._branding import BRAND_ALIAS, BRAND_NAME
# Default values when env vars not set
assert BRAND_NAME == "scitex-writer"
assert BRAND_ALIAS == "sw"
def test_get_mcp_server_name(self):
"""Test MCP server name generation."""
from scitex_writer._branding import get_mcp_server_name
# Should replace dots with hyphens
name = get_mcp_server_name()
assert "." not in name
def test_rebrand_text_noop(self):
"""Test rebrand_text returns original when no branding change."""
from scitex_writer._branding import rebrand_text
text = "import scitex_writer as sw"
result = rebrand_text(text)
assert result == text
def test_rebrand_text_none(self):
"""Test rebrand_text handles None."""
from scitex_writer._branding import rebrand_text
result = rebrand_text(None)
assert result is None
# EOF