This MCP server automates dSPACE SystemDesk through its COM automation interface. It covers the SystemDesk lifecycle, project operations, and AUTOSAR file import/validation.
SystemDesk acts as a strong partner for AI-driven AUTOSAR generation, where AI agents create comprehensive architecture descriptions and SystemDesk ensures correctness, consistency, and compliance through its robust validation capabilities.
- Installed dSPACE SystemDesk with a valid license
- Python 3.10 or newer
uv, the Python package and project manager used to create the environment, install dependencies, and run the server from this checkout. Install it from the official Astral documentation: https://docs.astral.sh/uv/getting-started/installation/- An MCP client (e.g. VS Code, Cursor, Claude Desktop)
- Open the repository/folder.
- Create the project environment and install dependencies from
pyproject.tomlusing the following command:
uv sync-
In your MCP client (e.g. VS Code, Claude Code, Cursor, Claude Desktop), add a new MCP server.
-
Configure it as a stdio MCP server using the following command:
path\to\this\cloned\repo\SystemDeskMCP.cmdFor example, as a
.vscode/mcp.jsonentry:{ "servers": { "SystemDeskMCP": { "type": "stdio", "command": "path\\to\\this\\cloned\\repo\\SystemDeskMCP.cmd", "args": [] } } } -
Reconnect/reload MCP servers in your client.
-
Run a quick check prompt, for example: "Call
start_systemdesk()and report the result."
Recommended flow:
start_systemdesk()create_project(project_path, project_name)or open an existing projectimport_autosar_file(file_path, timeout=60)validate_autosar(output_format="markdown" | "json", timeout=120)close_project(save_project=True)close_systemdesk(save_project=True)
Important:
start_systemdesk()must be called before any other SystemDesk tool.import_autosar_fileonly supports files with the.arxmlextension.- If SystemDesk shows blocking dialogs, startup/COM access may fail transiently.
Create the project environment and install the development dependencies from pyproject.toml using the following command:
uv sync --extra devuv run pytestTo also collect coverage:
uv run pytest --cov=systemdesk_mcpFor an end-to-end smoke test against an existing SystemDesk installation, use the prompt in .github/prompts/smoke-test.prompt.md.
start_systemdesk()get_systemdesk_status()close_systemdesk(save_project=True)create_project(project_path, project_name)close_project(save_project=True)import_autosar_file(file_path, timeout=60)validate_autosar(output_format="markdown" | "json", timeout=120)
Add a new tool in the appropriate domain-specific module under src/systemdesk_mcp/tools/. For a new domain, add a module under tools/, then add it to the imports and __all__ in tools/__init__.py.
Use the shared mcp instance from mcp_app.py, COM access, execution, and logging helpers from runtime.py, and errors, responses, and input constraints from common.py. Keep domain-specific helpers alongside their tools.
Declare each input as a flat function parameter typed Annotated[<type>, Field(...)]: the type, constraints, and description (with an Example:) live in the Field, the default in the signature. This is the single definition; FastMCP derives the JSON schema (including additionalProperties: false) and validates calls from it. Constraints shared across tools are defined once as Annotated aliases (e.g. TimeoutSeconds) and combined with a tool-specific description. Tools without inputs take no parameters.
from typing import Annotated, Any
from pydantic import Field
from systemdesk_mcp.common import (
ErrorCode, PermanentError, TimeoutSeconds, error_response, success_response,
)
from systemdesk_mcp.mcp_app import mcp
from systemdesk_mcp.runtime import get_app, logged, on_com_thread
@mcp.tool(
annotations={
"readOnlyHint": True,
"destructiveHint": False,
"idempotentHint": True,
"openWorldHint": False,
},
output_schema=None,
)
@logged
@on_com_thread
def my_new_tool(
element_path: Annotated[str, Field(
min_length=1,
description="AUTOSAR path of the element. Example: '/Package/MyElement'",
)],
timeout: Annotated[TimeoutSeconds, Field( # pylint: disable=unused-argument # used by @on_com_thread
description="Maximum number of seconds to wait. Example: 60",
)] = 60,
) -> dict[str, Any]:
"""Description shown to the MCP client."""
try:
app = get_app() # active COM connection
# ... call app.* COM methods using element_path ...
return success_response({"message": "Done."})
except PermanentError as exc:
return error_response(str(exc), error_type="permanent", error_code=exc.error_code)
except Exception as exc: # pylint: disable=broad-exception-caught
return error_response(
"my_new_tool failed.",
error_type="transient",
error_code=ErrorCode.MY_NEW_TOOL_FAILED,
details=str(exc),
)Notes:
- Keep the decorators exactly in the order shown, with no others (checked by tests). Set all four annotations.
- Use
get_app()to access the live SystemDesk COM object. Never touch COM objects outside the dedicated COM thread. @on_com_threadruns the tool on the dedicated COM thread. If the operation can block, add atimeoutparameter as shown; it is used as the call timeout.@loggedwrites a structured log entry for every call.- Validate file paths with the existing
*_PATTERNregexes; only absolute local or UNC paths are accepted. - Return
success_response(...)orerror_response(...)so that every tool has the samestatus/error_type/error_coderesponse format. Useerror_type="permanent"(viaPermanentError) for non-retryable failures and"transient"for the genericexcept Exceptionfallback. - Use a stable UPPER_SNAKE_CASE
error_codeper failure condition: add it as a member of theErrorCodeenum (e.g.MY_NEW_TOOL_FAILED = "MY_NEW_TOOL_FAILED"), passErrorCode.<MEMBER>toerror_response(...)orPermanentError(message, error_code=...), and list it in the tool's "Failure codes" docstring section. Never rename or remove existing codes; clients depend on them. - Document in the docstring: purpose, preconditions, return fields, and a
Failure codes (error_type / error_code: condition):section listing every code the tool can return. - Update Usage (Tool Order), Available MCP Tools, and, if relevant, the
instructionsstring passed toFastMCP(...).
win32com is not installed: runuv syncto installpywin32class not registered/invalid class string: check SystemDesk installation/COM registrationblocked/rejected: close open dialogs in SystemDesk and try againaccess denied: start the MCP server with elevated privileges if needed
src/systemdesk_mcp/server.py: imports the tools package and starts the stdio serversrc/systemdesk_mcp/tools/: domain-specific tool implementationssrc/systemdesk_mcp/tools/__init__.py: imports for domain-specific module registrationsrc/systemdesk_mcp/mcp_app.py: shared FastMCP instance and server instructionssrc/systemdesk_mcp/runtime.py: shared COM connection, worker thread, timeouts, cleanup, and tool loggingsrc/systemdesk_mcp/common.py: shared errors, responses, and input constraintssrc/systemdesk_mcp/__init__.py: package versiontests/: unit tests and ARXML test fixturespyproject.toml: project metadata and Python dependencies (managed withuv)SystemDeskMCP.cmd: Convenience launcher used by MCP clients.github/prompts/smoke-test.prompt.md: end-to-end smoke test prompt
For technical questions and issues related to the dSPACE MCP Servers and related GitHub repositories, please open a GitHub issue.
As a valued dSPACE customer, you are always welcome to contact dSPACE Support directly via http://www.dspace.com/go/supportrequest.