Skip to content

Commit

Permalink
Merge pull request #736 from Mirascope/release/v1.12
Browse files Browse the repository at this point in the history
Release/v1.12
  • Loading branch information
willbakst authored Dec 7, 2024
2 parents 8511777 + a3474da commit c140237
Show file tree
Hide file tree
Showing 16 changed files with 2,702 additions and 4 deletions.
85 changes: 85 additions & 0 deletions docs/learn/mcp/client.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# MCP Client

!!! mira ""

<div align="center">
If you haven't already, we recommend first reading the section on [MCP Server](./mcp_server.md)
</div>

MCP Client in Mirascope enables you to interact with MCP servers through a standardized protocol. The client provides methods to access resources, tools, and prompts exposed by MCP servers.

## Basic Usage and Syntax

Let's connect to our book recommendation server using the MCP client:

```python hl_lines="11-15 19-26 28-33 35 39 44-47"
--8<-- "examples/learn/mcp/client.py"
```

This example demonstrates:

1. Creating server parameters with `StdioServerParameters`
2. Using the `create_mcp_client` context manager to connect to the server
3. Accessing server components:
- Listing and using prompts
- Reading resources
- Using tools with Mirascope calls

## Client Components

### Server Connection

To connect to an MCP server, use the `create_mcp_client` context manager with appropriate server parameters:

```python hl_lines="3-7 11"
--8<-- "examples/learn/mcp/client.py:9:19"
```

The `StdioServerParameters` specify how to launch and connect to the server process.

### Prompts

You can list available prompts and get prompt templates from the server:

```python
--8<-- "examples/learn/mcp/client.py:20:26"
```

The client automatically converts server prompts into Mirascope-compatible prompt templates that return `BaseMessageParam` instances.

### Resources

Resources can be listed and read from the server:

```python
--8<-- "examples/learn/mcp/client.py:28:33"
```

The client provides methods to:
- `list_resources()`: Get available resources
- `read_resource(uri)`: Read resource content by URI

### Tools

Tools from the server can be used with Mirascope's standard call decorators:

```python hl_lines="1 5 10-13"
--8<-- "examples/learn/mcp/client.py:35:47"
```

The client automatically converts server tools into Mirascope-compatible tool types that can be used with any provider's call decorator.

## Type Safety

The MCP client preserves type information from the server:

1. **Prompts**: Arguments and return types from server prompt definitions
2. **Resources**: MIME types and content types for resources
3. **Tools**: Input schemas and return types for tools

This enables full editor support and type checking when using server components.


## Next Steps

By using the MCP client with Mirascope's standard features like [Calls](../calls.md), [Tools](../tools.md), and [Prompts](../prompts.md), you can build powerful applications that leverage local services through MCP servers.
97 changes: 97 additions & 0 deletions docs/learn/mcp/server.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# MCP Server

!!! mira ""

<div align="center">
If you haven't already, we recommend first reading the section on [Tools](./tools.md) and [Calls](./calls.md)
</div>

MCP (Model Context Protocol) Server in Mirascope enables you to expose resources, tools, and prompts to LLM clients through a standardized protocol. This allows for secure and controlled interactions between host applications (like Claude Desktop) and local services.

## Basic Usage and Syntax

Let's build a simple book recommendation server using MCP:

```python hl_lines="7 10 27-32 40"
--8<-- "examples/learn/mcp/server_decorators.py:3"
```


This example demonstrates:

1. Creating an MCP server with the `MCPServer` class
2. Registering a tool to get book recommendations by genre
3. Exposing a books database as a resource
4. Creating a prompt template for book recommendations
5. Running the server asynchronously

## Server Components

### Tools

Tools in MCP Server expose callable functions to clients. Tools can be registered using the `@app.tool()` decorator, which follows the same patterns as described in the [Tools](./tools.md) documentation:

```python
--8<-- "examples/learn/mcp/server_decorators.py:12:26"
```

The `@app.tool()` decorator supports all the same functionality as the standard Mirascope tool decorators, including:

- Function-based tools
- Class-based tools inheriting from `BaseTool`
- Tool configurations and validation
- Computed fields and dynamic configuration

See the [Tools documentation](.././tools.md) for more details on defining and using tools.

### Resources

Resources provide access to data through URIs. They can be registered using the `@app.resource()` decorator with configuration options:

```python
--8<-- "examples/learn/mcp/server_decorators.py:29:39"
```

Resources support both synchronous and asynchronous functions, making them flexible for different types of data access.

### Prompts

Prompts define reusable message templates. They can be registered using the `@app.prompt()` decorator, which provides the same functionality as the standard Mirascope `@prompt_template` decorator described in the [Prompts](./prompts.md) documentation:

```python
--8<-- "examples/learn/mcp/server_decorators.py:42:49"
```

The `@app.prompt()` decorator supports all the features of standard Mirascope prompts, including:

- String templates
- Multi-line prompts
- Chat history
- Object attribute access
- Format specifiers
- Computed fields and dynamic configuration

See the [Prompts documentation](.././prompts.md) for more details on creating and using prompts.

## Alternative Definition Style

In addition to using decorators, you can also define your functions first and then register them when creating the MCP server. This style enables better function reusability and separation of concerns:

```python hl_lines="46-59"
--8<-- "examples/learn/mcp/server.py:3:69"
```

This alternative style offers several advantages:

1. **Function Reusability**: Functions can be used both independently and as part of the MCP server
2. **Cleaner Separation**: Clear separation between function definitions and server configuration
3. **Easier Testing**: Functions can be tested in isolation before being registered with the server
4. **Code Organization**: Related functions can be grouped together in separate modules

The same applies for prompts defined with `@prompt_template` - see the [Prompts](.././prompts.md) documentation for more details about prompt reusability.

Both the decorator style and this alternative style are fully supported - choose the one that better fits your application's needs.

## Next Steps

By leveraging MCP Server in Mirascope, you can create secure and standardized integrations between LLM clients and local services. This enables powerful capabilities while maintaining control over how LLMs interact with your systems.
2 changes: 2 additions & 0 deletions examples/learn/mcp/books.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
The Name of the Wind by Patrick Rothfuss
The Silent Patient by Alex Michaelides
50 changes: 50 additions & 0 deletions examples/learn/mcp/client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import asyncio

from pathlib import Path

from mirascope.core import openai
from mirascope.mcp.client import create_mcp_client, StdioServerParameters


server_file = Path(__file__).parent / "server.py"

server_params = StdioServerParameters(
command="uv",
args=["run", "python", str(server_file)],
env=None,
)


async def main() -> None:
async with create_mcp_client(server_params) as client:
prompts = await client.list_prompts()
print(prompts[0])
# name='recommend_book' description='Get book recommendations by genre.' arguments=[PromptArgument(name='genre', description='Genre of book to recommend (fantasy, mystery, sci-fi, etc.)', required=True)]
prompt_template = await client.get_prompt_template(prompts[0].name)
prompt = await prompt_template(genre="fantasy")
print(prompt)
# [BaseMessageParam(role='user', content='Recommend a fantasy book')]

resources = await client.list_resources()
resource = await client.read_resource(resources[0].uri)
print(resources[0])
# uri=AnyUrl('file://books.txt/') name='Books Database' description='Read the books database file.' mimeType='text/plain'
print(resource)
# ['The Name of the Wind by Patrick Rothfuss\nThe Silent Patient by Alex Michaelides']

tools = await client.list_tools()

@openai.call(
"gpt-4o-mini",
tools=tools,
)
def recommend_book(genre: str) -> str:
return f"Recommend a {genre} book"

if tool := recommend_book("fantasy").tool:
call_result = await tool.call()
print(call_result)
# ['The Name of the Wind by Patrick Rothfuss']


asyncio.run(main())
72 changes: 72 additions & 0 deletions examples/learn/mcp/server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
"""Example of MCP server for book recommendations."""

import asyncio
from pathlib import Path

from mcp.types import Resource

from pydantic import AnyUrl

from mirascope.core import prompt_template
from mirascope.mcp import MCPServer


def get_book(genre: str) -> str:
"""Get a recommendation for a specific book genre.
Args:
genre: Genre of book (fantasy, mystery, sci-fi, etc.)
"""
book_recommendations = {
"fantasy": "The Name of the Wind by Patrick Rothfuss",
"mystery": "The Silent Patient by Alex Michaelides",
"sci-fi": "Project Hail Mary by Andy Weir",
"romance": "The Love Hypothesis by Ali Hazelwood",
"historical": "The Seven Husbands of Evelyn Hugo by Taylor Jenkins Reid",
}
return book_recommendations.get(genre, "Please specify a valid genre")


async def read_books_database():
"""Read the books database file."""
data = Path(__file__).parent / "books.txt"
with data.open() as f:
return f.read()


@prompt_template()
def recommend_book(genre: str) -> str:
"""Get book recommendations by genre.
Args:
genre: Genre of book to recommend (fantasy, mystery, sci-fi, etc.)
"""
return f"Recommend a {genre} book"


# Create a server for book recommendations
app = MCPServer(
name="book-recommendations", # Server name
version="1.0.0", # Server version
tools=[get_book], # Pre-register tools
resources=[ # Pre-register resources
(
Resource(
uri=AnyUrl("file://books.txt"),
name="Books Database",
mimeType="text/plain",
),
read_books_database,
)
],
prompts=[recommend_book], # Pre-register prompts
)


async def main():
"""Run the book recommendation server."""
await app.run()


if __name__ == "__main__":
asyncio.run(main())
58 changes: 58 additions & 0 deletions examples/learn/mcp/server_decorators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
"""Example of MCP server for book recommendations."""

import asyncio
from pathlib import Path

from mirascope.mcp import MCPServer

# Create a server for book recommendations
app = MCPServer("book-recommendations")


@app.tool()
def get_book(genre: str) -> str:
"""Get a recommendation for a specific book genre.
Args:
genre: Genre of book (fantasy, mystery, sci-fi, etc.)
"""
book_recommendations = {
"fantasy": "The Name of the Wind by Patrick Rothfuss",
"mystery": "The Silent Patient by Alex Michaelides",
"sci-fi": "Project Hail Mary by Andy Weir",
"romance": "The Love Hypothesis by Ali Hazelwood",
"historical": "The Seven Husbands of Evelyn Hugo by Taylor Jenkins Reid",
}
return book_recommendations.get(genre, "Please specify a valid genre")


@app.resource(
uri="file://books.txt",
name="Books Database",
mime_type="text/plain",
description="Curated database of book recommendations by genre",
)
async def read_books_database():
"""Read the books database file."""
data = Path(__file__).parent / "books.txt"
with data.open() as f:
return f.read()


@app.prompt()
def recommend_book(genre: str) -> str:
"""Get book recommendations by genre.
Args:
genre: Genre of book to recommend (fantasy, mystery, sci-fi, etc.)
"""
return f"Recommend a {genre} book"


async def main():
"""Run the book recommendation server."""
await app.run()


if __name__ == "__main__":
asyncio.run(main())
6 changes: 6 additions & 0 deletions mirascope/mcp/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
"""Mirascope Model Context Protocol (MCP) implementation."""

from .server import MCPServer
from .tools import MCPTool

__all__ = ["MCPServer", "MCPTool"]
Loading

0 comments on commit c140237

Please sign in to comment.