Migrate from FastMCP 2.14.5 to 3.1.0 with complete architectural overhaul. Adopt src-layout packaging, lazy config functions to eliminate .env race condition, and decorator-based tool registration. Consolidate 14 tool modules into 8 focused modules (33 tools total). Add 9 new schematic tools via kicad-sch-api for creating and manipulating .kicad_sch files. Drop pandas dependency (BOM uses stdlib csv). Remove ~17k lines of stubs, over-engineering, and dead code. All checks pass: ruff clean, mypy 0 errors, 17/17 tests green.
132 lines
4.0 KiB
Python
132 lines
4.0 KiB
Python
"""
|
|
Project management tools for KiCad MCP server.
|
|
|
|
Provides tools for discovering, inspecting, and opening KiCad projects
|
|
on the local filesystem.
|
|
"""
|
|
|
|
import logging
|
|
import os
|
|
from typing import Any
|
|
|
|
from mckicad.server import mcp
|
|
from mckicad.utils.file_utils import get_project_files, load_project_json
|
|
from mckicad.utils.kicad_utils import find_kicad_projects, open_kicad_project
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
@mcp.tool()
|
|
def list_projects() -> dict[str, Any]:
|
|
"""Find and list all KiCad projects in configured search paths.
|
|
|
|
Scans KICAD_SEARCH_PATHS and default project directories for
|
|
.kicad_pro files. Returns project name, path, relative path,
|
|
and last-modified timestamp for each discovered project.
|
|
|
|
Returns:
|
|
Dictionary with success status, project list, and count.
|
|
"""
|
|
logger.info("Scanning for KiCad projects")
|
|
try:
|
|
projects = find_kicad_projects()
|
|
logger.info("Found %d KiCad project(s)", len(projects))
|
|
return {
|
|
"success": True,
|
|
"data": projects,
|
|
"count": len(projects),
|
|
"error": None,
|
|
}
|
|
except Exception as e:
|
|
logger.error("Failed to list projects: %s", e)
|
|
return {
|
|
"success": False,
|
|
"data": [],
|
|
"count": 0,
|
|
"error": str(e),
|
|
}
|
|
|
|
|
|
@mcp.tool()
|
|
def get_project_structure(project_path: str) -> dict[str, Any]:
|
|
"""Get the file structure and metadata of a KiCad project.
|
|
|
|
Enumerates all files associated with a .kicad_pro project file
|
|
(schematic, PCB, netlist, BOM exports, etc.) and loads project
|
|
metadata from the JSON project file.
|
|
|
|
Args:
|
|
project_path: Absolute path to the .kicad_pro file.
|
|
|
|
Returns:
|
|
Dictionary with project name, directory, file map, and metadata.
|
|
"""
|
|
logger.info("Getting project structure for: %s", project_path)
|
|
|
|
if not os.path.exists(project_path):
|
|
logger.warning("Project file not found: %s", project_path)
|
|
return {
|
|
"success": False,
|
|
"data": None,
|
|
"error": f"Project not found: {project_path}",
|
|
}
|
|
|
|
try:
|
|
project_dir = os.path.dirname(project_path)
|
|
# Strip .kicad_pro extension to get the project name
|
|
basename = os.path.basename(project_path)
|
|
project_name = basename.rsplit(".kicad_pro", 1)[0] if basename.endswith(".kicad_pro") else basename
|
|
|
|
files = get_project_files(project_path)
|
|
|
|
metadata = {}
|
|
project_data = load_project_json(project_path)
|
|
if project_data and "metadata" in project_data:
|
|
metadata = project_data["metadata"]
|
|
|
|
logger.info(
|
|
"Project '%s' has %d associated file(s)", project_name, len(files)
|
|
)
|
|
|
|
return {
|
|
"success": True,
|
|
"data": {
|
|
"name": project_name,
|
|
"path": project_path,
|
|
"directory": project_dir,
|
|
"files": files,
|
|
"metadata": metadata,
|
|
},
|
|
"error": None,
|
|
}
|
|
except Exception as e:
|
|
logger.error("Failed to get project structure for %s: %s", project_path, e)
|
|
return {
|
|
"success": False,
|
|
"data": None,
|
|
"error": str(e),
|
|
}
|
|
|
|
|
|
@mcp.tool()
|
|
def open_project(project_path: str) -> dict[str, Any]:
|
|
"""Open a KiCad project in the KiCad application.
|
|
|
|
Launches KiCad (or the system default handler) with the specified
|
|
.kicad_pro file. Uses platform-appropriate open commands (open on
|
|
macOS, xdg-open on Linux).
|
|
|
|
Args:
|
|
project_path: Absolute path to the .kicad_pro file.
|
|
|
|
Returns:
|
|
Dictionary with success status and any error output.
|
|
"""
|
|
logger.info("Opening project: %s", project_path)
|
|
result = open_kicad_project(project_path)
|
|
if result.get("success"):
|
|
logger.info("Project opened successfully: %s", project_path)
|
|
else:
|
|
logger.warning("Failed to open project: %s", result.get("error"))
|
|
return result
|