Metadata-Version: 2.4
Name: slim-mcp
Version: 0.2.2
Summary: Model Context Protocol with SLIM as transport
License-Expression: Apache-2.0
License-File: LICENSE
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries
Requires-Python: <4.0,>=3.11
Requires-Dist: anyio>=4.5
Requires-Dist: mcp==1.26.0
Requires-Dist: slim-bindings~=1.0
Description-Content-Type: text/markdown

# SLIM-MCP Integration

Leverage SLIM as a transport mechanism for MCP, enabling efficient load balancing
and dynamic discovery across MCP servers.

## Installation

```bash
pip install slim-mcp
```

## Overview

SLIM-MCP provides a seamless integration between SLIM (Secure Low-Latency
Interactive Messaging) and MCP (Model Context Protocol), allowing you to:

- Create MCP servers that can be discovered and accessed through SLIM
- Connect MCP clients to servers using SLIM as the transport layer
- Handle multiple concurrent sessions
- Leverage SLIM's load balancing and service discovery capabilities

## Quick Start

### Server Setup

```python
import asyncio
import slim_bindings
from mcp.server.lowlevel import Server
import mcp.types as types
from slim_mcp import create_local_app, run_mcp_server

# Create an MCP server application
mcp_app = Server("example-server")

# Define your tools
@mcp_app.list_tools()
async def list_tools() -> list[types.Tool]:
    return [
        types.Tool(
            name="example",
            description="An example tool",
            inputSchema={
                "type": "object",
                "required": ["url"],
                "properties": {
                    "url": {"type": "string", "description": "URL parameter"}
                },
            },
        )
    ]

async def main():
    # Create SLIM app
    name = slim_bindings.Name("org", "namespace", "server-name")
    slim_app, _ = await create_local_app(name, shared_secret="your-secret-here")

    # Run MCP server
    await run_mcp_server(slim_app, mcp_app)

asyncio.run(main())
```

### Client Setup

```python
import asyncio
import slim_bindings
from mcp import ClientSession
from slim_mcp import create_local_app, create_client_streams

async def main():
    # Create SLIM app
    client_name = slim_bindings.Name("org", "namespace", "client-id")
    client_app, _ = await create_local_app(client_name, shared_secret="your-secret-here")

    # Connect to server using standard MCP transport pattern
    destination = slim_bindings.Name("org", "namespace", "server-name")
    async with create_client_streams(client_app, destination) as (read, write):
        async with ClientSession(read, write) as session:
            # Initialize the session
            await session.initialize()

            # List available tools
            tools = await session.list_tools()
            print(f"Available tools: {tools}")

asyncio.run(main())
```

### Client with Upstream Connection

When connecting through a SLIM gateway or upstream server:

```python
import asyncio
import slim_bindings
from mcp import ClientSession
from slim_mcp import create_local_app, create_client_streams

async def main():
    # Create SLIM app with upstream connection
    client_name = slim_bindings.Name("org", "namespace", "client-id")
    config = slim_bindings.new_insecure_client_config("http://127.0.0.1:46357")
    client_app, connection_id = await create_local_app(client_name, config, shared_secret="your-secret-here")

    # Set route to destination through upstream connection
    destination = slim_bindings.Name("org", "namespace", "server-name")
    if connection_id is not None:
        await client_app.set_route_async(destination, connection_id)

    # Connect to server
    async with create_client_streams(client_app, destination) as (read, write):
        async with ClientSession(read, write) as session:
            await session.initialize()
            tools = await session.list_tools()
            print(f"Available tools: {tools}")

asyncio.run(main())
```

## API Reference

### Core Functions

#### `create_local_app(name, config=None, enable_opentelemetry=False, shared_secret=None, spire_socket_path=None, spire_target_spiffe_id=None, spire_jwt_audiences=None)`

Create a local SLIM app and optionally connect to an upstream server.

Authentication mode is determined by:
1. SPIRE — when `spire_socket_path` is provided
2. Shared secret — when `shared_secret` is provided

**At least one authentication method must be provided.**

**Parameters:**
- `name` (slim_bindings.Name): The name of the local app
- `config` (slim_bindings.ClientConfig | None): Optional upstream server configuration
- `enable_opentelemetry` (bool): Enable OpenTelemetry tracing
- `shared_secret` (str | None): Shared secret for authentication (None by default)
- `spire_socket_path` (str | None): Path to SPIRE Workload API socket (enables SPIRE auth)
- `spire_target_spiffe_id` (str | None): Specific SPIFFE ID to request (optional)
- `spire_jwt_audiences` (list[str] | None): Audience list for JWT SVID requests (optional)

**Returns:** `tuple[slim_bindings.App, int | None]` - The app and optional connection ID

**Raises:** `ValueError` if neither `shared_secret` nor `spire_socket_path` is provided

**Examples:**
```python
# Local app with shared secret
name = slim_bindings.Name("org", "ns", "my-app")
app, _ = await create_local_app(name, shared_secret="mysecret")

# App with SPIRE authentication
app, _ = await create_local_app(
    name,
    spire_socket_path="/run/spire/sockets/agent.sock",
    spire_jwt_audiences=["my-audience"]
)

# App with upstream connection
config = slim_bindings.new_insecure_client_config("http://localhost:46357")
app, conn_id = await create_local_app(name, config, shared_secret="mysecret")
```

### Server Functions

#### `run_mcp_server(slim_app, mcp_app, session_timeout=None)`

Run an MCP server that listens for SLIM sessions and handles MCP requests.

**Parameters:**
- `slim_app` (slim_bindings.App): The SLIM app instance
- `mcp_app` (mcp.server.lowlevel.Server): The MCP server instance
- `session_timeout` (datetime.timedelta | None): Optional timeout for listening

**Example:**
```python
from mcp.server.lowlevel import Server
import slim_bindings
from slim_mcp import create_local_app, run_mcp_server

mcp_app = Server("my-server")

# Define tools...
@mcp_app.list_tools()
async def list_tools():
    return [...]

# Create and run
name = slim_bindings.Name("org", "ns", "my-server")
slim_app, _ = await create_local_app(name, shared_secret="your-secret-here")
await run_mcp_server(slim_app, mcp_app)
```

### Client Functions

#### `create_client_streams(slim_app, destination, max_retries=2, timeout=timedelta(seconds=15))`

Create MCP client streams using SLIM transport. This follows the standard MCP transport pattern.

**Parameters:**
- `slim_app` (slim_bindings.App): The SLIM app instance
- `destination` (slim_bindings.Name): The destination name to connect to
- `max_retries` (int): Maximum number of retries for messages
- `timeout` (datetime.timedelta): Timeout for message delivery

**Yields:** `tuple[ReadStream, WriteStream]` - MCP-compatible read/write streams

**Example:**
```python
from mcp import ClientSession
import slim_bindings
from slim_mcp import create_local_app, create_client_streams

name = slim_bindings.Name("org", "ns", "client")
client_app, _ = await create_local_app(name, shared_secret="your-secret-here")

destination = slim_bindings.Name("org", "ns", "server")
async with create_client_streams(client_app, destination) as (read, write):
    async with ClientSession(read, write) as session:
        await session.initialize()
        tools = await session.list_tools()
```

### Configuration

#### Creating Client Configurations

Use slim_bindings helper functions to create configurations:

```python
import slim_bindings

# Insecure connection (for development)
config = slim_bindings.new_insecure_client_config("http://localhost:46357")

# Custom configuration
from slim_mcp.examples.mcp_server_time.server import ClientConfigType
config_type = ClientConfigType()
config = config_type.convert({
    "endpoint": "http://localhost:46357",
    "tls": {"insecure": True}
}, None, None)
```

## Features

- **Standard MCP Transport Pattern**: Follows the same pattern as stdio, SSE, and WebSocket transports
- **Simple Functional API**: Clean functions instead of complex class hierarchies
- **Automatic Session Management**: Handles session lifecycle and cleanup
- **Concurrent Sessions**: Support for multiple concurrent sessions
- **TLS Support**: Built-in support for secure connections
- **Dynamic Discovery**: Leverage SLIM's service discovery capabilities
- **Load Balancing**: Utilize SLIM's load balancing features
- **Connection Routing**: Set routes to destinations through upstream connections

## Examples

Check out the `slim_mcp/examples` directory for complete examples:

- **MCP Time Server**: A server that provides time and timezone conversion tools
- **LlamaIndex Agent**: A client that uses LlamaIndex to interact with MCP servers

## Error Handling

The library provides comprehensive error handling and logging. All operations
are wrapped with proper cleanup to ensure resources are released.

```python
import logging

# Enable debug logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger("slim_mcp")
```

## Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

## License

Apache-2.0

