Skip to content

Commit f735902

Browse files
authored
feat: Tools as MCP (#221)
* Add as_mcp helper and docs * Small docs changes
1 parent 0438163 commit f735902

6 files changed

Lines changed: 317 additions & 15 deletions

File tree

docs/api/tools.mdx

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,105 @@ transport: Transport = transport
574574
```
575575

576576
The transport to use
577+
578+
as\_mcp
579+
-------
580+
581+
```python
582+
as_mcp(
583+
tools: Iterable[Tool[..., Any] | Callable[..., Any]],
584+
*,
585+
name: str = "Rigging Tools",
586+
) -> FastMCP
587+
```
588+
589+
Serves a collection of Rigging tools over the Model Context Protocol (MCP).
590+
591+
This function creates a FastMCP server instance that exposes your
592+
Rigging tools to any compliant MCP client. It acts as a bridge, handling
593+
the conversion between Rigging's `Tool` objects and the MCP specification.
594+
595+
**Parameters:**
596+
597+
* **`tools`**
598+
(`Iterable[Tool[..., Any] | Callable[..., Any]]`)
599+
–An iterable of `rigging.Tool` objects or raw Python functions
600+
to be exposed. If raw functions are passed, they will be
601+
converted to `Tool` objects automatically.
602+
* **`name`**
603+
(`str`, default:
604+
`'Rigging Tools'`
605+
)
606+
–The name of the MCP server. This is used for identification
607+
608+
Example
609+
610+
```python
611+
# in my_tool_server.py
612+
import asyncio
613+
import rigging as rg
614+
615+
@rg.tool
616+
def add_numbers(a: int, b: int) -> int:
617+
"""Adds two numbers together."""
618+
return a + b
619+
620+
if __name__ == "__main__":
621+
rg.as_mcp([add_numbers]).run(
622+
transport="stdio"
623+
)
624+
```
625+
626+
627+
<Accordion title="Source code in rigging/tools/mcp.py" icon="code">
628+
```python
629+
def as_mcp(
630+
tools: t.Iterable[Tool[..., t.Any] | t.Callable[..., t.Any]],
631+
*,
632+
name: str = "Rigging Tools",
633+
) -> "FastMCP":
634+
"""
635+
Serves a collection of Rigging tools over the Model Context Protocol (MCP).
636+
637+
This function creates a FastMCP server instance that exposes your
638+
Rigging tools to any compliant MCP client. It acts as a bridge, handling
639+
the conversion between Rigging's `Tool` objects and the MCP specification.
640+
641+
Args:
642+
tools: An iterable of `rigging.Tool` objects or raw Python functions
643+
to be exposed. If raw functions are passed, they will be
644+
converted to `Tool` objects automatically.
645+
name: The name of the MCP server. This is used for identification
646+
647+
Example:
648+
~~~python
649+
# in my_tool_server.py
650+
import asyncio
651+
import rigging as rg
652+
653+
@rg.tool
654+
def add_numbers(a: int, b: int) -> int:
655+
\"\"\"Adds two numbers together.\"\"\"
656+
return a + b
657+
658+
if __name__ == "__main__":
659+
rg.as_mcp([add_numbers]).run(
660+
transport="stdio"
661+
)
662+
~~~
663+
"""
664+
rigging_tools = [t if isinstance(t, Tool) else Tool.from_callable(t) for t in tools]
665+
fastmcp_tools = [
666+
FastMCPTool.from_function(
667+
fn=_create_mcp_handler(tool.fn), name=tool.name, description=tool.description
668+
)
669+
for tool in rigging_tools
670+
]
671+
return FastMCP(name, tools=fastmcp_tools)
672+
```
673+
674+
675+
</Accordion>
577676
Utilities for integrating tools from a Robopages server.
578677

579678
robopages

docs/topics/migrations.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ We worked hard in v3 to bring together some of the early tool systems and unify
130130
- It uses a `mode: ToolMode` parameter (`auto`, `api`, `xml`, `json-in-xml`) to control calling convention.
131131
- It uses `max_depth: int` to limit recursive tool calls.
132132
- Parameters like `force`, `attempt_recovery`, `drop_dialog`, `max_rounds` are removed.
133-
- The `rigging.integrations` module is removed. Use `rigging.tool.robopages` and the new `rigging.tool.mcp` to use those integrations as tools.
133+
- The `rigging.integrations` module is removed. Use `rigging.tools.robopages` and the new `rigging.tools.mcp` to use those integrations as tools.
134134

135135
<CodeGroup>
136136
```python v2.x

docs/topics/tools.mdx

Lines changed: 109 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -264,16 +264,17 @@ This stop indication won't completely halt the pipeline, but it will let it cont
264264

265265
The [Model Context Protocol (MCP)](https://github.com/model-context-protocol/specification) is an open standard for language models to interact with external tools and services. Rigging provides a client to connect to MCP-compliant servers.
266266

267-
Use the `rigging.tool.mcp` function, specifying the transport method (`stdio` or `sse`) and the connection parameters. It returns an *async context manager*.
267+
### Using MCP Tools in Rigging
268268

269-
### Using `stdio` (Standard Input/Output)
269+
Use the `rigging.mcp` function, specifying the transport method (`stdio` or `sse`) and the connection parameters. It returns an *async context manager*.
270270

271-
Connect to an MCP server launched as a local process that communicates over standard input/output.
272-
273-
```python
271+
<CodeGroup>
272+
```python Using "stdio" (Standard Input/Output)
274273
import rigging as rg
275274

276-
# Example: Assuming 'my-mcp-server' is an executable that starts the MCP server
275+
# Connect to an MCP server launched as a local process that communicates over standard input/output.
276+
# (Assuming 'my-mcp-server' is an executable that starts the MCP server)
277+
277278
command = "my-mcp-server"
278279
args = ["--port", "stdio"] # Example arguments
279280

@@ -294,13 +295,11 @@ async with rg.mcp("stdio", command=command, args=args) as mcp_client:
294295

295296
```
296297

297-
### Using `sse` (Server-Sent Events)
298-
299-
Connect to an MCP server exposed via an HTTP endpoint using Server-Sent Events.
300-
301-
```python
298+
```python Using "sse" (Server-Sent Events)
302299
import rigging as rg
303300

301+
# Connect to an MCP server exposed via an HTTP endpoint using Server-Sent Events.
302+
304303
# URL of the MCP SSE endpoint
305304
MCP_SSE_URL = "http://localhost:8001/mcp" # Example URL
306305

@@ -322,11 +321,109 @@ async with rg.mcp("sse", url=MCP_SSE_URL) as mcp_client:
322321

323322
The `mcp` context manager handles the connection, tool discovery, and communication with the MCP server. Inside the `async with` block, `mcp_client.tools` provides the list of discovered `Tool` objects ready to be used with `.using()`.
324323

324+
#### Example: Using Claude Code as a Rigging Tool
325+
326+
```python
327+
import rigging as rg
328+
329+
async with rg.mcp("stdio", command="claude", args=["mcp", "serve"]) as mcp_client:
330+
print(f"Discovered {len(mcp_client.tools)} tools:")
331+
for tool in mcp_client.tools:
332+
print(f" |- {tool.name}")
333+
334+
chat = (
335+
await rg.get_generator("claude-3-7-sonnet-latest")
336+
.chat("Using tools, create a file_writer.py rigging tool that writes to a file.")
337+
.using(*mcp_client.tools)
338+
.run()
339+
)
340+
print(chat.conversation)
341+
```
342+
343+
### Serving Tools with MCP
344+
345+
In addition to consuming tools from mcp servers, Rigging can **expose its own tools** as an MCP server. This allows you to write a singular tool implementation with the freedom to use it in other MCP-compliant clients, such as claude code.
346+
347+
The `rigging.as_mcp` function is the primary way to create an MCP server from your tools. It takes a list of Rigging `Tool` objects (or raw Python functions) and returns a `mcp.server.fastmcp.FastMCP` server instance, which you can then configure and run.
348+
349+
**How it works:**
350+
1. You provide a list of tools to `as_mcp`.
351+
2. It automatically converts raw functions into `rigging.Tool` objects.
352+
3. For each tool, it creates a bridge that handles:
353+
- Exposing the tool's name, description, and parameter schema.
354+
- Calling your Python function when a request comes in.
355+
- Converting your function's return value (including `rigging.Message` and `rigging.Stop`) into a format the MCP client can understand.
356+
4. It returns a `FastMCP` server instance, ready to be run.
357+
358+
Let's create a server that exposes a `file_writer` tool.
359+
360+
**1. Define your tool(s) in a file (e.g., `file_writer.py`):**
361+
362+
```python
363+
import rigging as rg
364+
from typing import Annotated
365+
366+
@rg.tool()
367+
def write_file(
368+
filename: Annotated[str, "The name of the file to write to."],
369+
content: Annotated[str, "The content to write to the file."],
370+
) -> str:
371+
"""
372+
Writes content to a local file.
373+
Creates the file if it doesn't exist, and overwrites it if it does.
374+
"""
375+
with open(filename, "w") as f:
376+
f.write(content)
377+
return f"Successfully wrote {len(content)} bytes to {filename}."
378+
379+
if __name__ == "__main__":
380+
# Create the MCP server instance from a list of tools
381+
rg.as_mcp([write_file], name="File IO Tools").run()
382+
```
383+
384+
Now, this server is ready to be used by any MCP client:
385+
386+
- [claude-code](https://docs.anthropic.com/en/docs/claude-code/mcp)
387+
- [gemini-cli](https://github.com/google-gemini/gemini-cli/blob/main/docs/tools/mcp-server.md)
388+
- [codex](https://github.com/openai/codex/blob/main/codex-rs/config.md#mcp_servers)
389+
390+
**Claude Code Example:**
391+
392+
```bash
393+
$ claude mcp add file_writer -- uv run --with rigging --no-project file_writer.py
394+
Added stdio MCP server file_writer with command: uv run --with rigging --no-project file_writer.py to local config
395+
396+
$ claude
397+
> /mcp
398+
399+
╭────────────────────────────────────────────────────────────────────────────────╮
400+
| Manage MCP servers │
401+
│ │
402+
│ ❯ 1. file_writer ✔ connected · Enter to view details │ │
403+
|
404+
╰────────────────────────────────────────────────────────────────────────────────╯
405+
406+
╭────────────────────────────────────────────────────────────────────────────────╮
407+
│ Rigging Tools │
408+
│ │
409+
│ Status: ✔ connected │
410+
│ Command: uv │
411+
│ Args: run --with rigging --no-project file_writer.py │
412+
│ Config location: /Users/user/.claude.json [project: /Users/user/code] │
413+
│ Capabilities: tools │
414+
│ Tools: 1 tools │
415+
│ │
416+
│ ❯ 1. View tools │
417+
╰────────────────────────────────────────────────────────────────────────────────╯
418+
419+
> Summarize the README file using the file_writer tool.
420+
```
421+
325422
## Robopages
326423

327424
[Robopages](https://github.com/context-labs/robopages) is a framework for building and hosting tool-enabled "pages" or APIs. Rigging can dynamically fetch the available tools from a running Robopages server and make them available to your language model.
328425

329-
Use the `rigging.tool.robopages` function to connect to a Robopages endpoint and retrieve its tools.
426+
Use the `rigging.robopages` function to connect to a Robopages endpoint and retrieve its tools.
330427

331428
```python
332429
import rigging as rg

rigging/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
from rigging.model import Model, attr, element, wrapped
5151
from rigging.prompt import Ctx, Prompt, prompt
5252
from rigging.tokenizer import TokenizedChat, Tokenizer, get_tokenizer, register_tokenizer
53-
from rigging.tools import Tool, mcp, robopages, tool, tool_method
53+
from rigging.tools import Tool, as_mcp, mcp, robopages, tool, tool_method
5454
from rigging.transform import PostTransform, Transform
5555
from rigging.util import await_
5656
from rigging.version import VERSION
@@ -93,6 +93,7 @@
9393
"Tokenizer",
9494
"Tool",
9595
"Transform",
96+
"as_mcp",
9697
"attr",
9798
"await_",
9899
"caching",

rigging/tools/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
tool,
1414
tool_method,
1515
)
16-
from rigging.tools.mcp import mcp
16+
from rigging.tools.mcp import as_mcp, mcp
1717
from rigging.tools.robopages import robopages
1818

1919
__all__ = [
@@ -24,6 +24,7 @@
2424
"ToolChoice",
2525
"ToolDefinition",
2626
"ToolMode",
27+
"as_mcp",
2728
"mcp",
2829
"robopages",
2930
"tool",

0 commit comments

Comments
 (0)