# SLIM-MCP This tutorial demonstrates how to use SLIM (Secure Low-Latency Interactive Messaging) to transport MCP (Model Context Protocol) messages. SLIM offers two primary integration options, depending on whether you're building a new system or integrating with an existing MCP server: 1. **Using SLIM as an MCP Custom Transport Protocol**: MCP is designed to support multiple transport protocols, with SLIM now available as one of these options. To implement SLIM as a custom transport, you can install the SLIM-MCP package through pip and integrate it directly into your application. This approach is ideal for new systems where you control both client and server components, providing native SLIM support for efficient MCP message transport. 2. **Using SLIM with a Proxy Server**: If you have an existing MCP server running that uses SSE (Server-Sent Events) for transport, you can integrate SLIM by deploying a proxy server. This proxy handles translation between SLIM clients and your SSE-based MCP server, allowing SLIM clients to connect seamlessly without requiring modifications to your existing server, making it an effective solution for established systems. This tutorial guides you through both integration methods. You'll learn how to use SLIM as a custom transport for MCP and how to configure the proxy server to enable SLIM support for an SSE-based MCP server. By the end, you'll have all the necessary tools to integrate SLIM with MCP in a way that best fits your system's architecture. ## Using SLIM as an MCP Custom Transport Protocol In this section of the tutorial, we implement and deploy two sample applications: - A [LlamaIndex agent](https://github.com/agntcy/slim/tree/main/data-plane/integrations/mcp/slim-mcp/examples/llamaindex-time-agent) that communicates with an MCP server over SLIM to perform time queries and timezone conversions. - An [MCP time server](https://github.com/agntcy/slim/tree/main/data-plane/integrations/mcp/slim-mcp/examples/mcp-server-time) that implements SLIM as its transport protocol and processes requests from the LlamaIndex agent. ### Prerequisites - [UV](https://docs.astral.sh/uv/getting-started/installation/) - A Python package installer and environment manager. - [Docker](https://docs.docker.com/get-started/get-docker/) - For running the SLIM instance. ### Setting Up the SLIM Instance Since the client and server communicate using SLIM, we first need to deploy a SLIM instance. We are using a pre-built Docker image for this purpose. First, execute the following command to create a configuration file for SLIM: ```bash cat << EOF > ./config.yaml tracing: log_level: debug display_thread_names: true display_thread_ids: true runtime: n_cores: 0 thread_name: "slim-data-plane" drain_timeout: 10s services: slim/0: pubsub: servers: - endpoint: "0.0.0.0:46357" tls: insecure: true clients: [] controller: server: endpoint: "0.0.0.0:46358" tls: insecure: true EOF ``` Now launch the SLIM instance using the just created configuration file: ```bash docker run -it \ -v ./config.yaml:/config.yaml -p 46357:46357 \ ghcr.io/agntcy/slim:latest /slim --config /config.yaml ``` This command deploys an SLIM instance that listens on port 46357 for incoming connections. This instance serves as the communication backbone between our client and server applications. ### Implementing the MCP Server Next, we'll implement a simple MCP server that processes requests from the LlamaIndex agent. This server demonstrates how to use SLIM as a custom transport protocol. First, create a new directory for our MCP server project: ```bash mkdir -p mcp-server-time/src/mcp_server_time cd mcp-server-time ``` Now, create a `pyproject.toml` file in the project root to define the project dependencies: ```toml # pyproject.toml [project] name = "mcp-server-time" version = "0.1.0" description = "MCP server providing tools for time queries and timezone conversions" requires-python = ">=3.10" dependencies = ["mcp==1.6.0", "slim-mcp>=0.1.0", "click>=8.1.8"] [project.scripts] mcp-server-time = "mcp_server_time:main" [build-system] requires = ["hatchling"] build-backend = "hatchling.build" ``` Next, let's implement the MCP server that handles time queries and timezone conversions. This implementation is based on the [official MCP example server](https://github.com/modelcontextprotocol/servers/tree/main/src/time), modified to support both SLIM and SSE as transport protocols. Create the following files in your project directory:
src/mcp_server_time/__init__.py
```python # src/mcp_server_time/__init__.py from .server import main if __name__ == "__main__": main() ```

src/mcp_server_time/server.py
```python # src/mcp_server_time/server.py """ MCP Time Server - A server designed to handle time and timezone conversion functionalities. This module offers tools to retrieve the current time across various timezones and convert times between them. """ import asyncio import json import logging from collections.abc import Sequence from datetime import datetime, timedelta from enum import Enum from zoneinfo import ZoneInfo import click from mcp import types from mcp.server.lowlevel import Server from mcp.shared.exceptions import McpError from pydantic import BaseModel from slim_mcp import SLIMServer, init_tracing # Configure logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) class TimeTools(str, Enum): """Enumeration of available time-related tools.""" GET_CURRENT_TIME = "get_current_time" # Tool to get current time in a timezone CONVERT_TIME = "convert_time" # Tool to convert time between timezones class TimeResult(BaseModel): """Model representing a time result with timezone information.""" timezone: str # IANA timezone name datetime: str # ISO formatted datetime string is_dst: bool # Whether the timezone is in daylight saving time class TimeConversionResult(BaseModel): """Model representing the result of a time conversion between timezones.""" source: TimeResult # Source timezone information target: TimeResult # Target timezone information time_difference: str # String representation of time difference (e.g., "+2.0h") class TimeConversionInput(BaseModel): """Model for time conversion input parameters.""" source_tz: str # Source timezone time: str # Time to convert in HH:MM format target_tz_list: list[str] # List of target timezones def get_local_tz(local_tz_override: str | None = None) -> ZoneInfo: """ Get the local timezone information. Args: local_tz_override: Optional timezone override string Returns: ZoneInfo: The local timezone information Raises: McpError: If timezone cannot be determined """ if local_tz_override: return ZoneInfo(local_tz_override) # Get local timezone from datetime.now() tzinfo = datetime.now().astimezone(tz=None).tzinfo if tzinfo is not None: return ZoneInfo(str(tzinfo)) raise McpError( types.ErrorData( code=types.INTERNAL_ERROR, message="Could not determine local timezone - tzinfo is None", ) ) def get_zoneinfo(timezone_name: str) -> ZoneInfo: """ Get ZoneInfo object for a given timezone name. Args: timezone_name: IANA timezone name Returns: ZoneInfo: The timezone information Raises: McpError: If timezone is invalid """ try: return ZoneInfo(timezone_name) except Exception as e: raise McpError( types.ErrorData( code=types.INTERNAL_ERROR, message=f"Invalid timezone: {str(e)}", ) ) class TimeServer: """Core time server implementation providing time-related functionality.""" def get_current_time(self, timezone_name: str) -> TimeResult: """ Get current time in specified timezone. Args: timezone_name: IANA timezone name Returns: TimeResult: Current time information in the specified timezone """ timezone = get_zoneinfo(timezone_name) current_time = datetime.now(timezone) return TimeResult( timezone=timezone_name, datetime=current_time.isoformat(timespec="seconds"), is_dst=bool(current_time.dst()), ) def convert_time( self, source_tz: str, time_str: str, target_tz: str ) -> TimeConversionResult: """ Convert time between timezones. Args: source_tz: Source timezone name time_str: Time to convert in HH:MM format target_tz: Target timezone name Returns: TimeConversionResult: Converted time information Raises: ValueError: If time format is invalid """ source_timezone = get_zoneinfo(source_tz) target_timezone = get_zoneinfo(target_tz) try: parsed_time = datetime.strptime(time_str, "%H:%M").time() except ValueError: raise ValueError("Invalid time format. Expected HH:MM [24-hour format]") # Create a datetime object for today with the specified time now = datetime.now(source_timezone) source_time = datetime( now.year, now.month, now.day, parsed_time.hour, parsed_time.minute, tzinfo=source_timezone, ) # Convert to target timezone target_time = source_time.astimezone(target_timezone) # Calculate time difference between timezones source_offset = source_time.utcoffset() or timedelta() target_offset = target_time.utcoffset() or timedelta() hours_difference = (target_offset - source_offset).total_seconds() / 3600 # Format time difference string if hours_difference.is_integer(): time_diff_str = f"{hours_difference:+.1f}h" else: # For fractional hours like Nepal's UTC+5:45 time_diff_str = f"{hours_difference:+.2f}".rstrip("0").rstrip(".") + "h" return TimeConversionResult( source=TimeResult( timezone=source_tz, datetime=source_time.isoformat(timespec="seconds"), is_dst=bool(source_time.dst()), ), target=TimeResult( timezone=target_tz, datetime=target_time.isoformat(timespec="seconds"), is_dst=bool(target_time.dst()), ), time_difference=time_diff_str, ) class TimeServerApp: """Main application class for the MCP Time Server.""" def __init__(self, local_timezone: str | None = None): """ Initialize the Time Server application. Args: local_timezone: Optional override for local timezone """ self.app: Server = Server("mcp-time") self.time_server = TimeServer() self.local_tz = str(get_local_tz(local_timezone)) self._setup_tools() def _setup_tools(self): """Setup tool definitions and handlers for the MCP server.""" @self.app.list_tools() async def list_tools() -> list[types.Tool]: """ List available time tools. Returns: list[types.Tool]: List of available time-related tools """ return [ types.Tool( name=TimeTools.GET_CURRENT_TIME.value, description="Get current time in a specific timezones", inputSchema={ "type": "object", "properties": { "timezone": { "type": "string", "description": f"IANA timezone name (e.g., 'America/New_York', 'Europe/London'). Use '{self.local_tz}' as local timezone if no timezone provided by the user.", } }, "required": ["timezone"], }, ), types.Tool( name=TimeTools.CONVERT_TIME.value, description="Convert time between timezones", inputSchema={ "type": "object", "properties": { "source_timezone": { "type": "string", "description": f"Source IANA timezone name (e.g., 'America/New_York', 'Europe/London'). Use '{self.local_tz}' as local timezone if no source timezone provided by the user.", }, "time": { "type": "string", "description": "Time to convert in 24-hour format (HH:MM)", }, "target_timezone": { "type": "string", "description": f"Target IANA timezone name (e.g., 'Asia/Tokyo', 'America/San_Francisco'). Use '{self.local_tz}' as local timezone if no target timezone provided by the user.", }, }, "required": ["source_timezone", "time", "target_timezone"], }, ), ] @self.app.call_tool() async def call_tool( name: str, arguments: dict ) -> Sequence[types.TextContent | types.ImageContent | types.EmbeddedResource]: """ Handle tool calls for time queries. Args: name: Name of the tool to call arguments: Dictionary of tool arguments Returns: Sequence of content types containing the tool response Raises: ValueError: If tool name is unknown or arguments are invalid """ result: TimeResult | TimeConversionResult try: match name: case TimeTools.GET_CURRENT_TIME.value: timezone = arguments.get("timezone") if not timezone: raise ValueError("Missing required argument: timezone") result = self.time_server.get_current_time(timezone) case TimeTools.CONVERT_TIME.value: if not all( k in arguments for k in ["source_timezone", "time", "target_timezone"] ): raise ValueError("Missing required arguments") result = self.time_server.convert_time( arguments["source_timezone"], arguments["time"], arguments["target_timezone"], ) case _: raise ValueError(f"Unknown tool: {name}") return [ types.TextContent( type="text", text=json.dumps(result.model_dump(), indent=2) ) ] except Exception as e: raise ValueError(f"Error processing mcp-server-time query: {str(e)}") async def handle_session(self, session, slim_server, tasks): """ Handle a single session with proper error handling and logging. Args: session: The session to handle slim_server: The SLIM server instance tasks: Set of active tasks """ try: async with slim_server.new_streams(session) as streams: logger.info( f"new session started - session_id: {session.id}, active_sessions: {len(tasks)}" ) await self.app.run( streams[0], streams[1], self.app.create_initialization_options(), ) logger.info( f"session {session.id} ended - active_sessions: {len(tasks)}" ) except Exception: logger.error( f"Error handling session {session.id}", extra={"session_id": session.id}, exc_info=True, ) raise async def cleanup_tasks(tasks): """ Clean up all tasks and wait for their completion. Args: tasks: Set of tasks to clean up """ for task in tasks: if not task.done(): task.cancel() if tasks: try: await asyncio.gather(*tasks, return_exceptions=True) except Exception: logger.error("Error during task cleanup", exc_info=True) raise async def serve_slim( local_timezone: str | None = None, organization: str = "org", namespace: str = "ns", mcp_server: str = "time-server", config: dict = {}, ) -> None: """ Main server function that initializes and runs the time server using SLIM transport. Args: local_timezone: Optional override for local timezone organization: Organization name namespace: Namespace name mcp_server: MCP server name config: Server configuration dictionary """ await init_tracing({"log_level": "info"}) time_app = TimeServerApp(local_timezone) tasks: set[asyncio.Task] = set() async with SLIMServer(config, organization, namespace, mcp_server) as slim_server: try: async for new_session in slim_server: task = asyncio.create_task( time_app.handle_session(new_session, slim_server, tasks) ) tasks.add(task) except Exception: logger.error("Error in session handler", exc_info=True) raise finally: await cleanup_tasks(tasks) logger.info("Server stopped") def serve_sse( local_timezone: str | None = None, port: int = 8000, ) -> None: """ Main server function that initializes and runs the time server using SSE transport. Args: local_timezone: Optional override for local timezone port: Server listening port """ time_app = TimeServerApp(local_timezone) from mcp.server.sse import SseServerTransport from starlette.applications import Starlette from starlette.responses import Response from starlette.routing import Mount, Route sse = SseServerTransport("/messages/") async def handle_sse(request): async with sse.connect_sse( request.scope, request.receive, request._send ) as streams: await time_app.app.run( streams[0], streams[1], time_app.app.create_initialization_options() ) return Response() starlette_app = Starlette( debug=True, routes=[ Route("/sse", endpoint=handle_sse, methods=["GET"]), Mount("/messages/", app=sse.handle_post_message), ], ) import uvicorn uvicorn.run(starlette_app, host="0.0.0.0", port=port) class DictParamType(click.ParamType): name = "dict" def convert(self, value, param, ctx): import json if isinstance(value, dict): return value # Already a dict (for default value) try: return json.loads(value) except json.JSONDecodeError: self.fail(f"{value} is not valid JSON", param, ctx) @click.command(context_settings={"auto_envvar_prefix": "MCP_TIME_SERVER"}) @click.option( "--local-timezone", type=str, help="Override local timezone", default=None ) @click.option("--transport", default="slim", help="transport option: slim or sse") @click.option( "--port", default="8000", type=int, help="listening port, used only with sse transport", ) @click.option( "--organization", default="org", help="server organization, used only with slim transport", ) @click.option( "--namespace", default="ns", help="server namespace, used only with slim transport" ) @click.option( "--mcp-server", default="time-server", help="server name, used only with slim transport", ) @click.option( "--config", default={ "endpoint": "http://127.0.0.1:46357", "tls": { "insecure": True, }, }, type=DictParamType(), help="slim server configuration, used only with slim transport", ) def main(local_timezone, transport, port, organization, namespace, mcp_server, config): """ MCP Time Server - Time and timezone conversion functionality for MCP. """ if transport == "slim": import asyncio asyncio.run( serve_slim(local_timezone, organization, namespace, mcp_server, config) ) else: serve_sse(local_timezone, port) ```

The core component of the server implementation is the `serve_slim` function. This function establishes a connection with our SLIM instance and handles all incoming client sessions. It leverages the `SLIMServer` class to create a SLIM server instance that listens for and processes client connections. External clients can address this server using the SLIM name `org/ns/time-server`. ```python async def serve_slim( local_timezone: str | None = None, organization: str = "org", namespace: str = "ns", mcp_server: str = "time-server", config: dict = {}, ) -> None: """ Main server function that initializes and runs the time server using SLIM transport. Args: local_timezone: Optional override for local timezone organization: Organization name namespace: Namespace name mcp_server: MCP server name config: Server configuration dictionary """ await init_tracing({"log_level": "info"}) time_app = TimeServerApp(local_timezone) tasks: set[asyncio.Task] = set() async with SLIMServer(config, organization, namespace, mcp_server) as slim_server: try: async for new_session in slim_server: task = asyncio.create_task( time_app.handle_session(new_session, slim_server, tasks) ) tasks.add(task) except Exception: logger.error("Error in session handler", exc_info=True) raise finally: await cleanup_tasks(tasks) logger.info("Server stopped") ``` After implementing all the necessary files, your project structure looks like this: ```bash mcp-server-time/ ├── src/ │ └── mcp_server_time/ │ ├── __init__.py │ └── server.py └── pyproject.toml ``` To launch the server and begin listening for incoming connections, navigate to the project directory and run: ```bash uv run mcp-server-time --local-timezone Europe/London ``` ### Implementing the LlamaIndex Agent With our MCP server up and running, let's implement a LlamaIndex agent that will interact with the server. This agent will send time queries and timezone conversion requests to our MCP server using the SLIM transport protocol. First, create a new directory for our LlamaIndex agent project: ```bash mkdir -p llamaindex-time-agent/src/llamaindex_time_agent cd llamaindex-time-agent ``` Next, create a `pyproject.toml` file to define the agent's dependencies: ```toml # pyproject.toml [project] name = "llamaindex-time-agent" version = "0.1.0" description = "A llamaindex agent using MCP server over SLIM for time queries" requires-python = ">=3.12" dependencies = [ "mcp==1.6.0", "slim-mcp>=0.1.0", "click>=8.1.8", "llama-index>=0.12.29", "llama-index-llms-azure-openai>=0.3.2", "llama-index-llms-ollama>=0.5.4", "llama-index-llms-openai-like>=0.3.4", "llama-index-tools-mcp>=0.1.2", ] [project.scripts] llamaindex-time-agent = "llamaindex_time_agent:main" [build-system] requires = ["hatchling"] build-backend = "hatchling.build" ``` Let's create the Python files for our LlamaIndex agent that will handle time queries and timezone conversions. Create the following files in your project directory:
src/llamaindex_time_agent/__init__.py
```python # src/llamaindex_time_agent/__init__.py from .main import main if __name__ == "__main__": main() ```

src/llamaindex_time_agent/main.py
```python # src/llamaindex_time_agent/main.py # Copyright AGNTCY Contributors (https://github.com/agntcy) # SPDX-License-Identifier: Apache-2.0 import asyncio import logging import click from dotenv import load_dotenv from llama_index.core.agent.workflow import ReActAgent from llama_index.llms.azure_openai import AzureOpenAI from llama_index.llms.ollama import Ollama from llama_index.tools.mcp import McpToolSpec from slim_mcp import SLIMClient logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) async def amain( llm_type, llm_endpoint, llm_key, organization, namespace, mcp_server, city, config ): if llm_type == "azure": kwargs = { "engine": "gpt-4o-mini", "model": "gpt-4o-mini", "is_chat_model": True, "azure_endpoint": llm_endpoint, "api_key": llm_key, "api_version": "2024-08-01-preview", } llm = AzureOpenAI(**kwargs) elif llm_type == "ollama": kwargs = { "model": "llama3.2", } llm = Ollama(**kwargs) else: raise Exception("LLM type must be azure or ollama") logger.info("Starting SLIM client") async with SLIMClient( config, "org", "ns", "time-agent", organization, namespace, mcp_server, ) as client1: async with client1.to_mcp_session() as mcp_session: logger.info("Creating MCP tool spec") await mcp_session.initialize() mcp_tool_spec = McpToolSpec( client=mcp_session, ) tools = await mcp_tool_spec.to_tool_list_async() agent = ReActAgent(llm=llm, tools=tools) response = await agent.run( user_msg=f"What is the current time in {city}?", ) print(response) await mcp_session.close() class DictParamType(click.ParamType): name = "dict" def convert(self, value, param, ctx): import json if isinstance(value, dict): return value # Already a dict (for default value) try: return json.loads(value) except json.JSONDecodeError: self.fail(f"{value} is not valid JSON", param, ctx) @click.command(context_settings={"auto_envvar_prefix": "TIME_AGENT"}) @click.option("--llm-type", default="azure") @click.option("--llm-endpoint", default=None) @click.option("--llm-key", default=None) @click.option("--mcp-server-organization", default="org") @click.option("--mcp-server-namespace", default="ns") @click.option("--mcp-server-name", default="time-server") @click.option("--city", default="New York") @click.option( "--config", default={ "endpoint": "http://127.0.0.1:46357", "tls": { "insecure": True, }, }, type=DictParamType(), ) def main( llm_type, llm_endpoint, llm_key, mcp_server_organization, mcp_server_namespace, mcp_server_name, city, config, ): try: asyncio.run( amain( llm_type, llm_endpoint, llm_key, mcp_server_organization, mcp_server_namespace, mcp_server_name, city, config, ) ) except KeyboardInterrupt: logger.info("Keyboard interrupt") except Exception as e: logger.error(f"Error: {e}") raise e ```

The key component of the agent is the `amain` function, which handles: 1. LLM configuration (Azure OpenAI or Ollama). 2. SLIM client initialization and connection to our MCP server. 3. Tool setup and agent execution. The agent establishes its identity through the SLIM name `org/ns/time-agent`, which is used for addressing. After implementing all the necessary files, your agent project structure should look like this: ```bash llamaindex-time-agent/ ├── src/ │ └── llamaindex_time_agent/ │ ├── __init__.py │ └── main.py └── pyproject.toml ``` To run the agent, navigate to the project directory and use one of the following commands based on your preferred LLM: **Option 1: Using Azure OpenAI:** ```bash uv run llamaindex-time-agent \ --llm-type=azure \ --llm-endpoint=${AZURE_OPENAI_ENDPOINT} \ --llm-key=${AZURE_OPENAI_API_KEY} \ --city 'New York' ``` **Option 2: Using Ollama (locally):** ```bash uv run llamaindex-time-agent \ --llm-type=ollama \ --city 'New York' ``` The agent connects to the MCP server through SLIM, send a time query for the specified city, and display the response. ## Using SLIM with a Proxy Server for SSE-based MCP Servers In this section, we demonstrate how to set up and configure the SLIM-MCP Proxy Server. This proxy enables SLIM-based clients to communicate with existing MCP servers that use SSE (Server-Sent Events) as their transport protocol. By following these steps, you'll create a bridge between SLIM clients and SSE-based MCP servers without modifying the servers themselves. ### Setting Up the SLIM Node First, ensure you have a SLIM node running in your environment. If you haven't already set one up, follow the instructions provided in the previous section to [deploy an SLIM instance](#setting-up-the-slim-instance). ### Running the MCP Server with SSE Transport Let's set up the time-server using the SSE transport protocol instead of SLIM. The server implementation is the same as described in the previous section, but we configure it to use SSE: ```bash uv run mcp-server-time --local-timezone Europe/London --transport sse ``` Once the server starts successfully, you should see logs similar to this: ```bash INFO: Started server process [27044] INFO: Waiting for application startup. INFO: Application startup complete. INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit) ``` At this point, your time-server is up and running with SSE transport. ### Setting up the SLIM-MCP Proxy To enable SLIM clients to communicate with the SSE-based time-server, you'll need to configure and run the SLIM-MCP Proxy Server. Follow these steps to set up a local proxy instance: 1. **Determine your local IP address** (works on both macOS and Linux): ```bash # For macOS LOCAL_ADDRESS=$(ifconfig | grep --color=never "inet " | grep -v 127.0.0.1 | awk '{print $2}' | head -n 1) # For Linux LOCAL_ADDRESS=$(ip addr show | grep --color=never "inet " | grep -v 127.0.0.1 | awk '{print $2}' | cut -d/ -f1 | head -n 1) # Verify the IP was found correctly echo "Using local IP address: ${LOCAL_ADDRESS}" ``` > If the automatic detection doesn't work for your system, you can manually > set your IP address: > ```bash > LOCAL_ADDRESS=192.168.1.10 # Replace with your actual local IP address > ``` 2. **Create the configuration file for the proxy**: ```bash cat << EOF > ./config-proxy.yaml # SLIM-MCP Proxy Configuration # Tracing settings for log visibility tracing: log_level: info display_thread_names: true display_thread_ids: true # Runtime configuration runtime: n_cores: 0 thread_name: "slim-data-plae" drain_timeout: 10s # Service configuration for connecting to the SLIM node services: slim/0: pubsub: clients: - endpoint: "http://${LOCAL_ADDRESS}:46357" tls: insecure: true EOF ``` 3. **Run the proxy using Docker**: ```bash docker run -it \ -v $(pwd)/config-proxy.yaml:/config-proxy.yaml \ ghcr.io/agntcy/slim/mcp-proxy:latest /slim-mcp-proxy \ --config /config-proxy.yaml \ --svc-name slim/0 \ --name org/mcp/proxy \ --mcp-server http://${LOCAL_ADDRESS}:8000/sse ``` This command: - Mounts your local configuration file into the container. - Uses the official SLIM-MCP proxy image. - Sets the service name and proxy identifier. - Configures the connection to your SSE-based MCP server. ### Running the Agent with the Proxy Finally, you can run the LlamaIndex agent as shown in the previous section. The agent automatically connects to the proxy, which then relays messages to and from the MCP server. Notice that the proxy is reachable using the name `org/mcp/proxy`: ```bash uv run llamaindex-time-agent \ --llm-type=azure \ --llm-endpoint=${AZURE_OPENAI_ENDPOINT} \ --llm-key=${AZURE_OPENAI_API_KEY} \ --city 'New York' \ --mcp-server-organization "org" \ --mcp-server-namespace "mcp" \ --mcp-server-name "proxy" ``` With this setup, your SLIM client can now communicate seamlessly with the SSE-based MCP server through the proxy, without requiring any changes to the server implementation.