Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

MCP Server Documentation

The MCPServer is a bridge between a Tango control system and the Model Context Protocol (MCP). It allows LLM agents to interact directly with hardware by exposing Tango device commands as MCP tools.


What is MCP?

The Model Context Protocol (MCP) is an open standard that lets AI agents connect to external tools and data sources through a unified interface. Think of it as a standardized API layer specifically designed for LLM interactions.

MCP defines three core primitives that servers can expose:

Why MCP + Asyncroscopy?

Asyncroscopy uses PyTango to control microscope hardware. The MCPServer automatically discovers every Tango device command in your system and exposes them as MCP tools. This means an LLM agent can query detector settings, move the stage, acquire images, and adjust beam parameters — all through natural language. The Asyncroscopy MCP server exposes microscopy hardware (via pyTango) to language models. This enables LLM-driven microscopy workflows without direct hardware knowledge.

Core Functionality

1. Dynamic Device Discovery

On startup, the server queries the Tango Database to find all exported devices via _list_all_devices(). It then:

2. Automatic Tool Generation

Each discovered Tango command is wrapped into an MCP tool via _create_wrapper(). The server:


Configuration & Customization

Block Lists

You can restrict which commands or classes are exposed through the following __init__() arguments:

Adding Native MCP Tools, Resources, and Prompts

Beyond dynamic Tango commands, you can add native Python methods directly to the MCPServer instance using decorators. These methods are automatically registered during setup() via _register_instance_methods().

Native Tools

Use @tool() to define custom logic that requires arbitrary Python code.

from fastmcp.tools import tool
from asyncroscopy.mcp.mcp_server import MCPServer

class MyCustomMCPServer(MCPServer):
    @tool()
    def custom_helper_tool(self, data: str) -> str:
        """This tool will be automatically registered alongside Tango commands."""
        return f"Processed: {data}"

Resources

Use @resource() to expose static or dynamic content (like configuration files or documentation) as data sources for LLMs.

from fastmcp.resources import resource
from asyncroscopy.mcp.mcp_server import MCPServer

class MyCustomMCPServer(MCPServer):
    @resource("config://network")
    def get_network_config(self) -> str:
        """Expose current network configuration."""
        return "TANGO_HOST=localhost:9094"

Prompts

Use @prompt() to provide pre-defined templates that help LLMs structure their interactions with the hardware.

from fastmcp.prompts import prompt
from asyncroscopy.mcp.mcp_server import MCPServer

class MyCustomMCPServer(MCPServer):
    @prompt()
    def optimize_beam_setup(self, voltage: float) -> str:
        """A prompt template for optimizing beam alignment."""
        return f"Please check the alignment for {voltage}kV setup and report any deviation."

Data Transport & Encoding

Tango DevEncoded commands often return binary data (like images). The _normalize_command_result() method normalizes these into a standard JSON structure:

{
  "encoding": "base64",
  "metadata": "header_string",
  "payload": "base64_encoded_binary_data"
}

Running the Server

The server can be started as a standalone process. It requires a connection to a running Tango Database.

from asyncroscopy.mcp.mcp_server import MCPServer

# Initialize and start the server
server = MCPServer(
    name="AsyncroscopyServer",
    tango_host="localhost",
    tango_port=9094
)

# Use server.start() for stdio (default) or server.start_http() for HTTP
server.start()

By default, start() uses stdio transport for piping to agents. To expose the server over HTTP, use start_http() (wraps host="0.0.0.0", port=8000).