"""xcsift MCP Server.
This module defines the main MCP server that exposes xcsift functionality
to AI coding assistants through the Model Context Protocol.
"""
import argparse
import sys
from collections.abc import AsyncIterator
from contextlib import asynccontextmanager
from dataclasses import dataclass
from mcp.server.fastmcp import FastMCP
from xcsift_mcp.xcsift_installer import ensure_xcsift, XcsiftNotFoundError
@dataclass
class AppContext:
"""Application context with xcsift path."""
xcsift_path: str
@asynccontextmanager
async def app_lifespan(server: FastMCP) -> AsyncIterator[AppContext]:
"""Manage application lifecycle - ensure xcsift is available on startup."""
try:
xcsift_path = await ensure_xcsift()
except XcsiftNotFoundError as e:
print(f"Error: {e}", file=sys.stderr)
print(
"Please install xcsift manually: brew install xcsift",
file=sys.stderr,
)
raise
yield AppContext(xcsift_path=xcsift_path)
# Create the MCP server
mcp = FastMCP(
name="xcsift",
instructions="""xcsift MCP Server - Parse Xcode build output for AI coding agents.
This server provides tools to:
- Parse xcodebuild/swift build output into structured JSON or TOON format
- Execute build commands and get parsed results
- Extract errors, warnings, test failures, and coverage data
The output format is optimized for token efficiency when consumed by LLMs.
Use TOON format for 30-60% fewer tokens compared to JSON.
""",
lifespan=app_lifespan,
)
# Import and register tools
from xcsift_mcp.tools import register_parse_tools, register_build_tools
from xcsift_mcp.resources import register_resources
from xcsift_mcp.prompts import register_prompts
register_parse_tools(mcp)
register_build_tools(mcp)
register_resources(mcp)
register_prompts(mcp)
def main():
"""Main entry point for the xcsift MCP server."""
parser = argparse.ArgumentParser(description="xcsift MCP Server")
parser.add_argument(
"--transport",
choices=["stdio", "http"],
default="stdio",
help="Transport type (default: stdio)",
)
parser.add_argument(
"--port",
type=int,
default=8000,
help="Port for HTTP transport (default: 8000)",
)
args = parser.parse_args()
if args.transport == "stdio":
mcp.run(transport="stdio")
else:
mcp.run(transport="streamable-http", port=args.port)
if __name__ == "__main__":
main()