PyExample-MCP#
This example shows how to implement a minimal MCP server for a
hypothetical PyAnsys library named PyExample.
Project structure#
The very first step is to create the project structure.
This example uses a standard Python package layout with a src directory:
pyexample-mcp/
├── pyproject.toml
├── README.md
└── src/
└── pyexample_mcp/
├── __init__.py
├── __main__.py
├── server.py
└── context.py
Once the structure is in place, you can start implementing the server.
Define the context#
It is recommended to define a custom context class that inherits from
ansys.common.mcp.context.PyAnsysBaseAppContext.
This context will hold any shared state or resources needed by your MCP tools.
File: src/pyexample_mcp/context.py
# Copyright (C) 2025 - 2026 ANSYS, Inc. and/or its affiliates.
# SPDX-License-Identifier: Apache-2.0
#
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Context for PyExample MCP server."""
from dataclasses import dataclass, field
from typing import Any, Optional
from ansys.common.mcp import PyAnsysBaseAppContext
@dataclass
class PyExampleContext(PyAnsysBaseAppContext):
"""Application context for PyExample MCP server.
Attributes
----------
example_instance : Optional[Any]
The connected PyExample instance
simulation_results : dict
Storage for simulation results
"""
example_instance: Optional[Any] = None
simulation_results: dict = field(default_factory=dict)
The context holds shared state accessible from all tools. See Architecture for details on context management.
Implement the server#
The server class inherits from ansys.common.mcp.server.PyAnsysBaseMCP and implements the
required methods to handle incoming requests.
File: src/pyexample_mcp/server.py
# Copyright (C) 2025 - 2026 ANSYS, Inc. and/or its affiliates.
# SPDX-License-Identifier: Apache-2.0
#
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""MCP server for PyExample."""
from ansys.common.mcp import PersistentPythonSession, PyAnsysBaseMCP
from ansys.common.mcp.logging_config import get_logger
from pyexample_mcp.context import PyExampleContext
logger = get_logger(__name__)
class PyExampleMCP(PyAnsysBaseMCP):
"""MCP Server for PyExample.
This server enables AI assistants to interact with PyExample
for simulation and analysis workflows.
"""
def __init__(self, launch_mode: str = "local", timeout: int = 60, *args, **kwargs):
"""Initialize PyExample MCP server.
Parameters
----------
launch_mode : str
Launch mode for PyExample ('local' or 'remote')
timeout : int
Connection timeout in seconds
*args : tuple
Additional positional arguments passed to parent class
**kwargs : dict
Additional keyword arguments passed to parent class
"""
self.launch_mode = launch_mode
self.timeout = timeout
super().__init__(*args, **kwargs)
def create_context(self) -> PyExampleContext:
"""Create PyExample-specific context.
Returns
-------
PyExampleContext
Context instance with Python session and command history
"""
# Custom startup code for PyExample workflows
startup_code = """
# Standard scientific libraries
import numpy as np
import pandas as pd
import matplotlib
matplotlib.use('Agg') # Non-interactive backend
import matplotlib.pyplot as plt
print("PyExample MCP session initialized")
"""
return PyExampleContext(
python_session=PersistentPythonSession(
python_executable=self.python_executable,
working_directory=self.working_directory,
startup_code=startup_code,
),
command_history=[],
)
def product_startup(self):
"""Launch PyExample instance when server starts.
This method is called automatically during server startup.
"""
logger.info(f"Launching PyExample in {self.launch_mode} mode...")
try:
# Use the mock PyExample library for testing
from pyexample_mcp.mock_pyexample import launch_pyexample
self.context.example_instance = launch_pyexample(
mode=self.launch_mode, timeout=self.timeout
)
logger.info(
f"PyExample {self.context.example_instance.version} "
f"launched successfully in {self.launch_mode} mode"
)
except Exception as e:
logger.error(f"Failed to launch PyExample: {e}")
raise
def product_cleanup(self):
"""Clean up PyExample instance when server stops.
This method is called automatically during server shutdown.
"""
if self.context.example_instance:
try:
logger.info("Closing PyExample instance...")
self.context.example_instance.exit()
logger.info("PyExample instance closed successfully")
except Exception as e:
logger.error(f"Error during PyExample cleanup: {e}")
# Create the MCP server instance
app = PyExampleMCP(
name="pyexample-mcp",
)
The product_startup() and product_cleanup() methods manage the lifecycle of the connection to
the product. The example uses a mock connection for demonstration purposes.
Those methods are required by the ansys.common.mcp.server.PyAnsysBaseMCP class.
Optional: Override create_context() only if using a custom context class.
Implement tools#
The server can have one or more tools that implement specific functionality.
File: src/pyexample_mcp/tools.py
# Copyright (C) 2025 - 2026 ANSYS, Inc. and/or its affiliates.
# SPDX-License-Identifier: Apache-2.0
#
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Tools for PyExample MCP server."""
import json
from typing import Optional
from fastmcp.server import Context
from ansys.common.mcp.logging_config import get_logger
from pyexample_mcp import app
logger = get_logger(__name__)
# Define tools for interacting with PyExample instance
@app.tool()
def execute_command(ctx: Context, command: str) -> str:
"""Execute a PyExample command.
Parameters
----------
ctx : Context
The FastMCP context
command : str
PyExample command to execute
Returns
-------
str
Command execution result
"""
app_context = ctx.fastmcp._lifespan_result
if not app_context.example_instance:
return "Error: PyExample not connected"
try:
result = app_context.example_instance.run_command(command)
app_context.command_history.append(command)
logger.info(f"Executed command: {command}")
return str(result)
except Exception as e:
logger.error(f"Command execution failed: {e}")
return f"Error: {e}"
@app.tool()
def create_model(
ctx: Context,
name: str,
model_type: str = "default",
parameters: Optional[dict] = None,
) -> str:
"""Create a new model in PyExample.
Parameters
----------
ctx : Context
The FastMCP context
name : str
Name for the new model
model_type : str
Type of model to create
parameters : Optional[dict]
Additional model parameters
Returns
-------
str
Status message
"""
app_context = ctx.fastmcp._lifespan_result
if not app_context.example_instance:
return "Error: PyExample not connected"
# Create model (simulated)
params = parameters or {}
model = app_context.example_instance.create_model(name, model_type, **params)
# Update command history
command = f"CREATE MODEL {name} TYPE {model_type}"
app_context.command_history.append(command)
logger.info(f"Created model: {name} (type: {model_type})")
return f"Model '{name}' created successfully\n{model}"
@app.tool()
def run_simulation(
ctx: Context, model_name: Optional[str] = None, save_results: bool = True
) -> str:
"""Run a simulation on the specified model.
Parameters
----------
ctx : Context
The FastMCP context
model_name : Optional[str]
Model to simulate (uses active model if not specified)
save_results : bool
Whether to save results in context
Returns
-------
str
Simulation results summary
"""
app_context = ctx.fastmcp._lifespan_result
if not app_context.example_instance:
return "Error: PyExample not connected"
# Determine which model to use
target_model = model_name or app_context.example_instance.active_model
if not target_model:
return "Error: No model specified or active"
# Run simulation (simulated)
command = f"SOLVE MODEL {target_model}"
result = app_context.example_instance.run_command(command)
# Save results if requested
if save_results:
app_context.simulation_results[target_model] = {"status": "completed", "summary": result}
app_context.command_history.append(command)
logger.info(f"Simulation completed for model: {target_model}")
return f"Simulation completed for '{target_model}'\n{result}"
@app.tool()
def get_command_history(ctx: Context, format: str = "list") -> str:
"""Retrieve command execution history.
Parameters
----------
ctx : Context
The FastMCP context
format : str
Output format: 'list', 'numbered', or 'json'
Returns
-------
str
Command history in requested format
"""
app_context = ctx.fastmcp._lifespan_result
if not app_context.command_history:
return "No commands executed yet"
if format == "numbered":
lines = [f"{i + 1}. {cmd}" for i, cmd in enumerate(app_context.command_history)]
return "\n".join(lines)
elif format == "json":
return json.dumps(app_context.command_history, indent=2)
else: # list format
return "\n".join(app_context.command_history)
@app.tool()
def execute_python_code(ctx: Context, code: str) -> str:
"""Execute Python code in the persistent session.
This allows for custom analysis and processing using the
full Python ecosystem.
Parameters
----------
ctx : Context
The FastMCP context
code : str
Python code to execute
Returns
-------
str
Execution output
"""
app_context = ctx.fastmcp._lifespan_result
result = app_context.python_session.execute(code)
if result["success"]:
output = str(result["stdout"])
if result["stderr"]:
output += f"\n\nWarnings:\n{result['stderr']}"
return output
else:
return f"Error: {result['error']}"
See the Architecture for details on context injection patterns.
Initialize the package#
File: src/pyexample_mcp/__init__.py
# Copyright (C) 2025 - 2026 ANSYS, Inc. and/or its affiliates.
# SPDX-License-Identifier: Apache-2.0
#
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""PyExample MCP server package."""
from pyexample_mcp.context import PyExampleContext
from pyexample_mcp.server import PyExampleMCP, app
__version__ = "0.1.0"
__all__ = [
"PyExampleMCP",
"PyExampleContext",
"app",
"__version__",
]
Define the entry point#
File: src/pyexample_mcp/__main__.py
# Copyright (C) 2025 - 2026 ANSYS, Inc. and/or its affiliates.
# SPDX-License-Identifier: Apache-2.0
#
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Entry point for running PyExample MCP server."""
import sys
from ansys.common.mcp.logging_config import setup_logging
from pyexample_mcp import app
# Import tools to register them with the app
import pyexample_mcp.tools # noqa: F401
def main():
"""Run the PyExample MCP server."""
# Setup logging
setup_logging(level="INFO")
# Run the server (app instance already has tools registered)
app.run()
return 0
if __name__ == "__main__":
sys.exit(main())
Configure the package#
File: pyproject.toml
[build-system]
requires = ["flit_core >=3.2,<4"]
build-backend = "flit_core.buildapi"
[project]
name = "pyexample-mcp"
version = "0.1.0"
description = "MCP server for PyExample"
readme = "README.md"
requires-python = ">=3.10,<4"
license = { file = "LICENSE" }
dependencies = [
"ansys-common-mcp>=0.0.1",
"numpy>=2.0",
"pandas>=3.0",
"matplotlib>=3.0",
]
[project.scripts]
pyexample-mcp = "pyexample_mcp.__main__:main"
Run the example#
Install the package in a virtual environment using pip:
pip install .
Then, start the server using the entry point:
python -m pyexample_mcp
Another option is to configure the server in VS Code for easier development and debugging. Create a file
.vscode/mcp.json with the following content:
{
"servers": {
"pyexample-mcp": {
"type": "stdio",
"command": ".\\.venv\\Scripts\\python.exe",
"args": ["-m", "pyexample_mcp"]
}
}
}
The server will start and communicate via stdio, ready to accept MCP requests from AI clients.