Model Context Protocol (MCP) with InterSystems IRIS - From Zero to Hero A developer demonstrated building a custom Model Context Protocol (MCP) server in Python integrated with the intersystems-irispython package, showing how Anthropic's open MCP standard can connect AI coding assistants like Claude, GitHub Copilot and Cursor to InterSystems IRIS data sources. The writeup walks through MCP's client-host-server architecture, JSON-RPC 2.0 messaging, and its three core server primitives, using the uv package manager to scaffold a reproducible Python project. Today, coding assistants like Claude, GitHub Copilot and Cursor have transformed the way developers write code. However, these tools are limited by being isolated from the systems and data sources that developers work with daily. This limitation can be overcome through the Model Context Protocol MCP , an open standard designed to connect AI assistants to external data sources and tools in a secure and standardized way. In this review article, we'll explore the current state-of-the-art regarding the MCP within the InterSystems ecosystem. Moreover, we will examine how this open standard connects AI assistants to external data sources, presenting a first example of a custom MCP server developed in Python and integrated with the intersystems-irispython package. Table of Contents The Model Context Protocol MCP was introduced by Anthropic on November 25, 2024 \ 1\ https://www.anthropic.com/news/model-context-protocol as open-source with the goal of connecting AI systems to data sources, replacing fragmented integrations. Before MCP, every AI application required custom integrations to access each data source, like a database or an API. MCP solves this by providing a single, standardized protocol that any AI application can use to connect to compatible servers. The protocol addresses three key challenges: Official Resources: MCP follows a client-host-server architecture \ 5\ https://modelcontextprotocol.io/docs/learn : Each host can create multiple clients, with each client maintaining a 1:1 dedicated connection with its corresponding server. For example, Cursor acts as an MCP Host. When Cursor establishes a connection to an MCP server it instantiates an MCP client object that maintains the connection to that specific MCP server. When Cursor later connects to a different MCP server, it instantiates an additional MCP client object to maintain the new connection. You can find the original image source here \ 6\ https://mcpcat.io/blog/mcp-server-client-host/ . MCP supports two transport mechanisms: In MCP, the message structure and semantics is based on JSON-RPC 2.0 protocol \ 7\ https://www.jsonrpc.org/ . All communication follows a structure like the following: JSON-RPC 2.0 { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "query database", "arguments": { "sql": "SELECT FROM Patient LIMIT 10" } } } MCP defines three core primitives that servers can expose \ 5\ https://modelcontextprotocol.io/docs/learn : Before diving into code examples, let's understand how to build MCP servers in Python. MCP provides official SDKs for multiple programming languages, including TypeScript, Java, and Python \ 8\ https://modelcontextprotocol.io/docs/sdk . In this article, we'll focus on Python as it's widely used in the InterSystems community and integrates well with IRIS through the Native SDK \ 9\ https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=PAGE python native . Before talking about code, we should understand how to create a proper environment for our application, which won't just contain the Python code, but it also includes: To set up the environment we will use the uv Python package manager, a modern Python package manager that simplifies environment creation, dependency resolution, and reproducible execution of MCP servers \ 10\ https://docs.astral.sh/uv/concepts/projects/dependencies/ . You can initialize properly your folder structure for your MCP project using the uv init statement. Init package with uv You can initialize a new project with: uv init --package my-mcp-server Where the --package option tells uv to create a Python package. This command generates the following folder tree: my-mcp-server/ ├── pyproject.toml ├── README.md ├── .python-version └── src/ └── my mcp server/ └── init .py The .python-version file specifies which Python version the project should use. The init .py file marks a directory as a Python package. Since Python packages are expected to contain one or more Python modules, which are directories containing an init .py , each subfolder of the project needs an init .py can be empty to be importable as a package. The pyproject.toml contains the metadata and configuration for the project. It defines how the package is built, its dependencies, and the Python version requirements. It is composed by several sections like: build-system : specifies how the package is built. uv uses project : contains metadata and dependency definitions This is an example of pyproject.toml : build-system requires = "hatchling" build-backend = "hatchling.build" project name = "my-mcp-server" version = "0.1.0" description = "" readme = "README.md" requires-python = " =3.12" dependencies = After creating the structure, you can add dependencies to the project using the uv add statement. Add server dependencies You can use the uv add statement to add dependencies: uv add fastmcp intersystems-irispython requests This command: project.dependencies in pyproject.toml uv.lock file. This is used to recreate the environment while building your project on another machine. .venv/ After this step, the pyproject.toml will contain a new section for all your dependencies: project ... dependencies = "fastmcp =3.2", "intersystems-irispython =5.3", "requests =2.32", While the pyproject.toml contains all the direct dependencies, the uv.lock is a file that contains all resolved dependencies, namely the exact versions of all the direct and transitive dependencies in the project. At build time, instead of resolving dependencies again, uv reads uv.lock and installs exactly what’s written there. The uv.lock file looks like the following: package name = "fastmcp" version = "3.2.1" dependencies = "pydantic", "typing-extensions", Finally, it's time to add an entry point to start the server. Adding an entry point for the MCP server To make the MCP server executable, define a CLI entry point in pyproject.toml : project.scripts my-mcp-server = "my mcp server.entrypoint:main" This maps a command name my-mcp-server to a Python function using the format module.path:function . So, when the command is executed, Python imports my mcp server.entrypoint and runs the main function. Let's now take a look at the available packages that we can use to implement the MCP server. While the official mcp package provides the core protocol implementation \ 12\ https://pypi.org/project/mcp/ , FastMCP package has emerged as one of the easiest and fastest frameworks for building MCP servers with Python \ 13\ https://gofastmcp.com/ . This article will provide the minimal structure of an MCP server using FastMCP. A server is defined by three main steps: initializing the server defining tools, resources and prompt decorating the functions running the process FastMCP first example python from fastmcp import FastMCP 1. Create the MCP server instance mcp = FastMCP name="My MCP Server" 2. Define tools, resources and prompts using decorators @mcp.tool def my tool param: str - str: """Description of what this tool does. This text is sent to the LLM.""" return f"Result: {param}" 3. Run the server defaults to STDIO transport if name == " main ": mcp.run Let's see how to implement each primitive type: @mcp.tool decorator. Note that the docstring becomes the tool's description shown to the AI. In fact, when using this decorator, FastMCP automatically generates a JSON schema from Python type hints and docstrings, telling the AI exactly how to use the function. Tools Example php @mcp.tool def query database sql: str - str: """ Execute an SQL query against the database. Args: sql: The SQL query to execute Returns: Query results as a string """ Logic to connect to IRIS and execute SQL goes here return execute query sql @mcp.resource "uri" decorator and assigning it a unique URI. Resource Example php @mcp.resource "resource://database/schema" def get schema - dict: """Provides the database schema information to help the AI write better SQL.""" return { "tables": { "Patient": "id", "first name", "last name", "dob" , "Encounter": "id", "patient id", "date", "reason" , "Observation": "id", "encounter id", "type", "value" }, "version": "1.0", "storage type": "relational" } @mcp.prompt decorator that can guide the AI assistant to perform specific repetitive tasks like writing queries or importing data into the database. Prompt Example php @mcp.prompt def sql assistant - str: """A prompt template for IRIS SQL query assistance.""" return """You are an expert SQL developer specializing in InterSystems IRIS. When writing queries: - Use proper IRIS SQL syntax e.g., handle schema names correctly . - Favor performance-optimized indices. - Explain your reasoning if the query is complex.""" AI development tools are rapidly evolving from simple chat interfaces into sophisticated orchestrators capable of interacting with your local environment and remote servers by adopting the MCP protocol. Currently, while several platforms now support the protocol, the setup process is generally standardized across them. Most popular platforms nowadays are: Cursor : An AI-powered IDE built on Visual Studio Code that includes AI agent's capabilities and MCP integrations into the code editor \ 14\ https://cursor.com/ . Claude Desktop : Anthropic's official client, which uses MCP to give Claude "skills" like searching your local files or databases \ 15\ https://claude.com/download . Claude Code : A CLI-based agent that can integrate MCP servers directly from the terminal \ 16\ https://claude.com/product/claude-code . .cursor/mcp.json file within your repository. ~/.cursor/mcp.json on macOS/Linux or the equivalent AppData path on Windows . claude desktop config.json , that you can find within the application itself using the menu The following JSON structure shows how to connect Cursor or Claude to an IRIS MCP server: { "mcpServers": { "my-server-name": { "command": "executable-or-runtime", "args": "path/to/script-or-package", "--option", "value" , "env": { "KEY 1": "value-1", "KEY 2": "value-2" } } } } Every MCP server configuration is based on a few key fields that tell the AI how to launch and communicate with the server. Let's look at the JSON fields to understand how this configuration works: mcpServers : The root object that lists all your connected servers. You can have multiple servers running simultaneously for example: one for IRIS, one for Google Drive, one for Atlassian, etc. . command : The command used to start the server. Common examples include python , node , uvx , or npx . In particular, use args : An array of arguments passed to the command. This usually includes the MCP server file path or the name of a package to execute. env : An array containing your environment variables. This is the standard way to pass sensitive or environment-specific data like hostnames, port numbers, and credentials without hardcoding them into the server logic itself. Once you have this configured, the AI assistant can directly interact with your IRIS database, execute queries, and access schema information without you manually copying and pasting context. uv run : { "command": "uv", "args": "run", "