Skip to content

Commit 94ea736

Browse files
committed
docs: add MCP servers guide
1 parent 81b0110 commit 94ea736

5 files changed

Lines changed: 175 additions & 3 deletions

File tree

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ Almost any Python project can become an Actor, including projects for:
117117
- **Browser automation** — Drive a real browser with [Playwright](https://docs.apify.com/sdk/python/docs/guides/playwright) or [Selenium](https://docs.apify.com/sdk/python/docs/guides/selenium), or with higher-level tools such as [Browser Use](https://docs.apify.com/sdk/python/docs/guides/browser-use).
118118
- **Web servers and APIs** — Run a [web server](https://docs.apify.com/sdk/python/docs/guides/running-webserver) inside an Actor to serve HTTP requests, for example to expose your scraper as a live API.
119119
- **AI agents** — Host agents built with your framework of choice. Ready-made Actor templates cover [PydanticAI](https://apify.com/templates/python-pydanticai), [CrewAI](https://apify.com/templates/python-crewai), [LangGraph](https://apify.com/templates/python-langgraph), [LlamaIndex](https://apify.com/templates/python-llamaindex-agent), and [Smolagents](https://apify.com/templates/python-smolagents).
120-
- **MCP servers** — Deploy a Python MCP server as an Actor and make its tools available to any MCP client. See [MCP server](https://apify.com/templates/python-mcp-empty) and [MCP proxy](https://apify.com/templates/python-mcp-proxy) templates
120+
- **MCP servers** — Deploy a Python MCP server as an Actor and make its tools available to any MCP client (see the [MCP servers guide](https://docs.apify.com/sdk/python/docs/guides/mcp-servers)). Ready-made Actor templates cover the [MCP server](https://apify.com/templates/python-mcp-empty) and [MCP proxy](https://apify.com/templates/python-mcp-proxy).
121121
122122
Whatever you build, the Apify SDK doesn't lock you into a particular framework. Bring the libraries you already use, and let Apify run your project in the cloud.
123123

@@ -199,7 +199,7 @@ The full SDK documentation lives at **[docs.apify.com/sdk/python](https://docs.a
199199
| [Overview](https://docs.apify.com/sdk/python/docs/overview) | What the SDK is, what Actors are, and how the pieces fit together. |
200200
| [Quick start](https://docs.apify.com/sdk/python/docs/quick-start) | Create, run, and deploy your first Python Actor. |
201201
| [Concepts](https://docs.apify.com/sdk/python/docs/concepts/actor-lifecycle) | Actor lifecycle, input, storages, events, proxy management, interacting with other Actors, webhooks, accessing the Apify API, logging, configuration, and pay-per-event. |
202-
| [Guides](https://docs.apify.com/sdk/python/docs/guides/beautifulsoup-httpx) | Integrations with BeautifulSoup, Parsel, Playwright, Selenium, Crawlee, Scrapy, Scrapling, Crawl4AI, and Browser Use, plus running a web server and using uv. |
202+
| [Guides](https://docs.apify.com/sdk/python/docs/guides/beautifulsoup-httpx) | Integrations with BeautifulSoup, Parsel, Playwright, Selenium, Crawlee, Scrapy, Scrapling, Crawl4AI, and Browser Use, plus building MCP servers, running a web server, and using uv. |
203203
| [Upgrading](https://docs.apify.com/sdk/python/docs/upgrading/upgrading-to-v4) | Migrating between major versions. |
204204
| [API reference](https://docs.apify.com/sdk/python/reference) | Generated reference for every class and method. |
205205
| [Changelog](https://docs.apify.com/sdk/python/docs/changelog) | Release history and breaking changes. |

docs/01_introduction/index.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ Almost any Python project can become an Actor, including projects for:
4242
- **Browser automation** - Drive a real browser with [Playwright](./guides/playwright) or [Selenium](./guides/selenium), or with higher-level tools such as [Browser Use](./guides/browser-use).
4343
- **Web servers and APIs** - Run a [web server](./guides/running-webserver) inside an Actor to serve HTTP requests, for example to expose your scraper as a live API.
4444
- **AI agents** - Host agents built with your framework of choice. Ready-made Actor templates cover [PydanticAI](https://apify.com/templates/python-pydanticai), [CrewAI](https://apify.com/templates/python-crewai), [LangGraph](https://apify.com/templates/python-langgraph), [LlamaIndex](https://apify.com/templates/python-llamaindex-agent), and [Smolagents](https://apify.com/templates/python-smolagents).
45-
- **MCP servers** - Deploy a Python MCP server as an Actor and make its tools available to any MCP client. See the [MCP server](https://apify.com/templates/python-mcp-empty) and [MCP proxy](https://apify.com/templates/python-mcp-proxy) templates.
45+
- **MCP servers** - Deploy a Python MCP server as an Actor and make its tools available to any MCP client (see the [MCP servers guide](./guides/mcp-servers)). Ready-made Actor templates cover the [MCP server](https://apify.com/templates/python-mcp-empty) and [MCP proxy](https://apify.com/templates/python-mcp-proxy).
4646

4747
Whatever you build, the Apify SDK doesn't lock you into a particular framework. Bring the libraries you already use, and let Apify run your project in the cloud.
4848

docs/01_introduction/quick-start.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,3 +118,4 @@ For other aspects of Actor development, explore these guides:
118118
- [Project management with uv](../guides/uv)
119119
- [Input validation with Pydantic](../guides/input-validation)
120120
- [Running a web server](../guides/running-webserver)
121+
- [Building MCP servers](../guides/mcp-servers)

docs/03_guides/14_mcp_servers.mdx

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
---
2+
id: mcp-servers
3+
title: Building MCP servers
4+
description: Deploy a Python MCP server as an Apify Actor and make its tools available to any MCP client.
5+
---
6+
7+
import RunnableCodeBlock from '@site/src/components/RunnableCodeBlock';
8+
9+
import McpServerExample from '!!raw-loader!roa-loader!./code/14_mcp_server.py';
10+
11+
In this guide, you'll learn how to deploy a [Model Context Protocol](https://modelcontextprotocol.io) (MCP) server as an Apify Actor.
12+
13+
## Introduction
14+
15+
The [Model Context Protocol](https://modelcontextprotocol.io) is an open standard that lets AI applications connect to external tools and data. An MCP server exposes tools, resources, and prompts, and any MCP client (such as Claude or an IDE assistant) can call them. Hosting that server as an Apify Actor turns it into a remote, always-ready service.
16+
17+
Apify Actors are a good fit for MCP servers:
18+
19+
- With [Actor Standby](https://docs.apify.com/platform/actors/development/programming-interface/standby), the platform keeps the Actor running in the background and routes incoming HTTP requests to it, so your server is always ready to answer an MCP client.
20+
- The platform scales instances with demand, keeps logs, and handles the network, so you don't operate any infrastructure.
21+
- Every request carries an Apify API token, so the platform authenticates clients for you.
22+
- Pay-per-event charging lets you monetize the server, for example per tool call.
23+
24+
There are two ways to build one, and Apify maintains a template for each:
25+
26+
- Write a server from scratch with [FastMCP](https://gofastmcp.com/), starting from the [`python-mcp-empty`](https://apify.com/templates/python-mcp-empty) template.
27+
- Wrap an existing MCP server (stdio, HTTP, or SSE) behind a gateway, starting from the [`python-mcp-proxy`](https://apify.com/templates/python-mcp-proxy) template.
28+
29+
Both templates live in the [actor-templates repository](https://github.com/apify/actor-templates) and can be scaffolded with the [Apify CLI](https://docs.apify.com/cli), for example `apify create my-mcp-server --template python-mcp-empty`.
30+
31+
## Example MCP server
32+
33+
The following Actor runs a small [FastMCP](https://gofastmcp.com/) server that exposes a single `add` tool and an informational resource. It serves the MCP protocol over the [Streamable HTTP](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports#streamable-http) transport on the Actor's web server port:
34+
35+
<RunnableCodeBlock className="language-python" language="python">
36+
{McpServerExample}
37+
</RunnableCodeBlock>
38+
39+
Note that:
40+
41+
- A `build_server` helper registers the tools and resources. Add your own with the `@server.tool()` and `@server.resource()` decorators.
42+
- `server.http_app(transport='streamable-http')` returns an ASGI app that speaks MCP over Streamable HTTP, which [uvicorn](https://www.uvicorn.org/) then serves.
43+
- The server binds to `Actor.configuration.web_server_port` and `0.0.0.0`, so the platform can route the Actor's container URL to it. This is the same mechanism described in the [Running a web server](./running-webserver) guide.
44+
- The server keeps running until the platform shuts the Actor down. With Standby, that happens automatically once an instance has been idle for a while.
45+
46+
## Exposing it over Standby
47+
48+
To make the server an always-ready HTTP API, enable [Actor Standby](https://docs.apify.com/platform/actors/development/programming-interface/standby) and tell the platform where the MCP endpoint lives. Set both in the Actor's `.actor/actor.json`:
49+
50+
```json
51+
{
52+
"actorSpecification": 1,
53+
"name": "my-mcp-server",
54+
"usesStandbyMode": true,
55+
"webServerMcpPath": "/mcp"
56+
}
57+
```
58+
59+
Once deployed, an MCP client connects to the Actor's URL using the Streamable HTTP transport, passing an [Apify API token](https://console.apify.com/account/integrations) as a bearer token:
60+
61+
```json
62+
{
63+
"mcpServers": {
64+
"my-mcp-server": {
65+
"url": "https://me--my-mcp-server.apify.actor/mcp",
66+
"headers": {
67+
"Authorization": "Bearer <YOUR_APIFY_API_TOKEN>"
68+
}
69+
}
70+
}
71+
}
72+
```
73+
74+
## Wrapping an existing MCP server
75+
76+
If you already have an MCP server (or want to expose a third-party one), you don't need to rewrite it. The [`python-mcp-proxy`](https://apify.com/templates/python-mcp-proxy) template runs a gateway that connects to an existing server and re-exposes it over Streamable HTTP, while adding Apify's charging and tool authorization on top.
77+
78+
It supports three kinds of upstream server. For a local stdio server, the gateway spawns the process:
79+
80+
```python
81+
from mcp.client.stdio import StdioServerParameters
82+
83+
from .models import ServerType
84+
85+
server_type = ServerType.STDIO
86+
MCP_SERVER_PARAMS = StdioServerParameters(command='uv', args=['run', 'arxiv-mcp-server'])
87+
```
88+
89+
For a remote HTTP or SSE server, it forwards to a URL:
90+
91+
```python
92+
from .models import RemoteServerParameters, ServerType
93+
94+
server_type = ServerType.HTTP # or ServerType.SSE
95+
MCP_SERVER_PARAMS = RemoteServerParameters(url='https://mcp.apify.com')
96+
```
97+
98+
On top of forwarding requests, the gateway gives you a `TOOL_WHITELIST` to control which tools clients may call, and a hook to charge for each MCP operation. For details, see the template's README.
99+
100+
## Monetizing with pay-per-event
101+
102+
Both templates support [pay-per-event charging](../concepts/pay-per-event). You define events such as `tool-call` in the Actor's monetization settings and trigger them from the code when a client calls a tool:
103+
104+
```python
105+
await Actor.charge('tool-call')
106+
```
107+
108+
This lets you charge per tool call, per resource read, or per any other operation, and covers the cost of running the server.
109+
110+
## Conclusion
111+
112+
In this guide, you learned how to deploy an MCP server as an Apify Actor. You can now write a server with FastMCP or wrap an existing one with the proxy template, expose it over Actor Standby, connect MCP clients to it, and monetize it with pay-per-event. To get started, see the [Actor templates](https://apify.com/templates/categories/python). If you have questions or need assistance, feel free to reach out on our [GitHub](https://github.com/apify/apify-sdk-python) or join our [Discord community](https://discord.com/invite/jyEM2PRvMU). Happy building!
113+
114+
## Additional resources
115+
116+
- [Apify templates: MCP server](https://apify.com/templates/python-mcp-empty)
117+
- [Apify templates: MCP proxy](https://apify.com/templates/python-mcp-proxy)
118+
- [Apify: actor-templates repository](https://github.com/apify/actor-templates)
119+
- [Apify: MCP server documentation](https://docs.apify.com/platform/integrations/mcp)
120+
- [Model Context Protocol: Official documentation](https://modelcontextprotocol.io)
121+
- [FastMCP: Official documentation](https://gofastmcp.com/)
122+
- [Apify blog: What is the Model Context Protocol](https://blog.apify.com/what-is-model-context-protocol/)
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import asyncio
2+
3+
import uvicorn
4+
from fastmcp import FastMCP
5+
6+
from apify import Actor
7+
8+
9+
def build_server() -> FastMCP:
10+
"""Create a FastMCP server exposing one tool and one resource."""
11+
server = FastMCP(name='calculator')
12+
13+
@server.tool()
14+
def add(a: float, b: float) -> float:
15+
"""Add two numbers and return the sum."""
16+
return a + b
17+
18+
@server.resource(uri='resource://calculator/info', name='calculator-info')
19+
def info() -> str:
20+
"""Describe what this MCP server does."""
21+
return 'A simple calculator MCP server that adds two numbers.'
22+
23+
return server
24+
25+
26+
async def main() -> None:
27+
async with Actor:
28+
# Build the server and expose it over the Streamable HTTP transport.
29+
server = build_server()
30+
app = server.http_app(transport='streamable-http')
31+
32+
# Serve it on the platform's web server port. Binding to 0.0.0.0 makes
33+
# the server reachable through the Actor's container URL.
34+
config = uvicorn.Config(
35+
app,
36+
host='0.0.0.0', # noqa: S104
37+
port=Actor.configuration.web_server_port,
38+
)
39+
40+
url = Actor.configuration.web_server_url
41+
Actor.log.info(f'MCP server is available at {url}/mcp')
42+
43+
# Keep serving until the platform shuts the Actor down, for example when
44+
# a Standby instance has been idle past its timeout.
45+
await uvicorn.Server(config).serve()
46+
47+
48+
if __name__ == '__main__':
49+
asyncio.run(main())

0 commit comments

Comments
 (0)