Skip to content

Commit e4b716b

Browse files
committed
patch: update mcp sample and add readme
1 parent f877984 commit e4b716b

4 files changed

Lines changed: 103 additions & 18 deletions

File tree

examples/mcp/README.md

Lines changed: 95 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,105 @@
1-
# MCP Client Example
1+
# Stream × FastMCP Demo
22

3-
This example shows the *minimal* wiring needed to
4-
use GetStream's optional **MCP** support (`getstream[mcp]`).
3+
This folder contains a **complete Stream × FastMCP demo**. Running a *single* script spins up everything you need – the MCP server, the LLM agent and a Stream-Video call bot – and shows the whole tool-calling loop end-to-end.
54

6-
It does **not** implement any custom tools or resources yet –
7-
that will be added once the `fastmcp`-based implementation lands.
5+
Run it, talk, and the bot will answer – possibly after calling a tool.
86

9-
## Running
7+
> The supporting files (`server.py`, `agent.py`) are here mostly to show how
8+
> MCP tools are defined and how the agent lets the LLM call them. You don't
9+
> run them directly; `main.py` orchestrates the whole thing for you.
10+
11+
---
12+
13+
## 1 — Prerequisites
14+
15+
• Python ≥ 3.10 (⁠we use `pyproject.toml` to pin the packages)
16+
• A Stream account with API key/secret
17+
• An OpenAI API key
18+
• Deepgram + ElevenLabs keys if you want speech-to-text **and** text-to-speech
19+
20+
Create an `.env` file in this directory (or export the variables any other way):
21+
22+
```env
23+
OPENAI_API_KEY=sk-…
24+
25+
# stream.io credentials
26+
STREAM_API_KEY=…
27+
STREAM_API_SECRET=…
28+
29+
# optional – only needed if you run `main.py`
30+
DEEPGRAM_API_KEY=…
31+
ELEVENLABS_API_KEY=…
32+
```
33+
34+
---
35+
36+
## 2 — Install deps into an isolated venv
37+
38+
We recommend [uv](https://github.com/astral-sh/uv):
1039

1140
```bash
41+
# create a fresh virtual-env in .venv and install everything from pyproject.toml
42+
uv venv .venv
43+
uv pip install -r <(uv pip compile examples/mcp/pyproject.toml)
44+
45+
# or, if you already have uv 0.1.26+
1246
uv sync -q --all-packages -p examples/mcp/pyproject.toml
13-
uv run examples/mcp/main.py
1447
```
1548

16-
Requirements: Python ≥ 3.10 and `getstream[mcp]` extra installed
17-
(the `pyproject.toml` here declares it for you).
49+
Feel free to use `pip`/`poetry`/`pip-tools` instead; the TOML lists the same deps.
50+
51+
---
52+
53+
## 3 — Run the demo (1-liner)
54+
55+
```bash
56+
uv run examples/mcp/main.py # or: python examples/mcp/main.py
57+
```
58+
59+
`main.py` will
60+
61+
1. launch the tiny MCP server defined in `server.py`,
62+
2. create a temporary Stream call and open it in your browser,
63+
3. join the call as a bot participant, and
64+
4. feed speech → text → LLM → tools → speech.
65+
66+
Speak in the browser tab. The Deepgram STT engine turns your voice into
67+
text; the LLM decides whether it should answer directly or call a tool (see
68+
the `get_forecast` sample); if it does, the result is fed back to the LLM and
69+
finally read out loud via ElevenLabs.
70+
71+
Stop with `Ctrl-C`. The script tears everything down and removes the temporary
72+
users from your Stream instance.
73+
74+
---
75+
76+
## 4 — Anatomy of the example
77+
78+
| File | Role |
79+
|------|------|
80+
| `server.py` | Shows how to **define MCP tools** with the `@mcp.tool()` decorator. (Launched automatically by `main.py`.) |
81+
| `agent.py` | Very small helper that lets the LLM call tools and loops the results back. Imported by `main.py`. |
82+
| `main.py` | The only file you *run*. Orchestrates the video call, launches the MCP server as a subprocess and drives the agent. |
83+
84+
---
85+
86+
## 5 — Adding your own tools
87+
88+
1. Edit `server.py` and drop in a regular Python function. Annotate the
89+
parameters and return type — those become the tool's schema.
1890

19-
## What's inside?
91+
```python
92+
@mcp.tool()
93+
def table_availability(restaurant: str, date: str, seats: int) -> str:
94+
"""Check if *restaurant* has a free table for *seats* people on *date*."""
95+
# Imagine this hits the booking API for the venue.
96+
resp = httpx.get(
97+
"https://api.example.com/availability",
98+
params={"restaurant": restaurant, "date": date, "seats": seats},
99+
timeout=5,
100+
)
101+
return resp.json()["status"] # e.g. "available" / "fully booked"
102+
```
20103

21-
* `main.py` – placeholder async script that will eventually
22-
connect to an MCP server.
23-
* `pyproject.toml` – isolated env spec that pulls in the
24-
`getstream[mcp]` extra.
104+
2. Restart the demo. The agent advertises the new tool automatically; the LLM
105+
can now call `table_availability[{"restaurant": "Pasta Place", "date": "2025-07-01", "seats": 4}]`.

examples/mcp/agent.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
You are a friendly AI assistant. When the user asks for real-world data, look through the
1010
available MCP tools and call the one that matches. Reply with plain text.
1111
If you call a tool, use "tool_name"["argument_key": "argument_value"] on its own line.
12+
You can make up to 3 tool calls in a row.
1213
"""
1314

1415
def call_llm(messages):
@@ -33,6 +34,7 @@ async def chat_with_tools(prompt: str, client: fastmcp.Client) -> str:
3334
]
3435

3536
while True:
37+
tool_calls = 0
3638
reply = call_llm(history)
3739
m = re.match(r"^(\w[\w\.]+)\[(.*)\]$", reply.strip())
3840

@@ -45,6 +47,12 @@ async def chat_with_tools(prompt: str, client: fastmcp.Client) -> str:
4547
for result in results:
4648
history.append({"role": "assistant",
4749
"content": f"(result) {result.text}"})
50+
tool_calls += 1
51+
if tool_calls == 2:
52+
history.append({"role": "assistant",
53+
"content": "You can make up to 3 tool calls in a row. You have already made 2 tool calls. Please answer the user's question directly."})
54+
if tool_calls >= 3:
55+
return reply
4856
continue
4957
else:
5058
return reply

examples/mcp/main.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import logging
2-
import time
32

43
from dotenv import load_dotenv
54
from getstream.stream import Stream

examples/mcp/server.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,6 @@
33

44
mcp = FastMCP("Stream MCP Server")
55

6-
@mcp.tool()
7-
def greet(name: str) -> str:
8-
return f"Hello, {name}!"
96

107
@mcp.tool()
118
def get_forecast(city: str) -> str:

0 commit comments

Comments
 (0)