From da0cecfaf24613c399d914b601a1f57d3a62e697 Mon Sep 17 00:00:00 2001 From: Vlada Dusek Date: Wed, 17 Jun 2026 11:32:33 +0200 Subject: [PATCH 1/9] docs: add MCP servers guide --- README.md | 4 +- docs/01_introduction/index.mdx | 2 +- docs/01_introduction/quick-start.mdx | 1 + docs/03_guides/14_mcp_servers.mdx | 122 +++++++++++++++++++++++++++ docs/03_guides/code/14_mcp_server.py | 49 +++++++++++ 5 files changed, 175 insertions(+), 3 deletions(-) create mode 100644 docs/03_guides/14_mcp_servers.mdx create mode 100644 docs/03_guides/code/14_mcp_server.py diff --git a/README.md b/README.md index b9c9ea55..2c23bad2 100644 --- a/README.md +++ b/README.md @@ -117,7 +117,7 @@ Almost any Python project can become an Actor, including projects for: - **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). - **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. - **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). -- **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 +- **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). 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. @@ -199,7 +199,7 @@ The full SDK documentation lives at **[docs.apify.com/sdk/python](https://docs.a | [Overview](https://docs.apify.com/sdk/python/docs/overview) | What the SDK is, what Actors are, and how the pieces fit together. | | [Quick start](https://docs.apify.com/sdk/python/docs/quick-start) | Create, run, and deploy your first Python Actor. | | [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. | -| [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. | +| [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. | | [Upgrading](https://docs.apify.com/sdk/python/docs/upgrading/upgrading-to-v4) | Migrating between major versions. | | [API reference](https://docs.apify.com/sdk/python/reference) | Generated reference for every class and method. | | [Changelog](https://docs.apify.com/sdk/python/docs/changelog) | Release history and breaking changes. | diff --git a/docs/01_introduction/index.mdx b/docs/01_introduction/index.mdx index 133c6a47..fe9d3143 100644 --- a/docs/01_introduction/index.mdx +++ b/docs/01_introduction/index.mdx @@ -42,7 +42,7 @@ Almost any Python project can become an Actor, including projects for: - **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). - **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. - **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). -- **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. +- **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). 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. diff --git a/docs/01_introduction/quick-start.mdx b/docs/01_introduction/quick-start.mdx index 289659bf..0e55342d 100644 --- a/docs/01_introduction/quick-start.mdx +++ b/docs/01_introduction/quick-start.mdx @@ -118,3 +118,4 @@ For other aspects of Actor development, explore these guides: - [Project management with uv](./guides/uv) - [Input validation with Pydantic](./guides/input-validation) - [Running a web server](./guides/running-webserver) +- [Building MCP servers](./guides/mcp-servers) diff --git a/docs/03_guides/14_mcp_servers.mdx b/docs/03_guides/14_mcp_servers.mdx new file mode 100644 index 00000000..cd45a88f --- /dev/null +++ b/docs/03_guides/14_mcp_servers.mdx @@ -0,0 +1,122 @@ +--- +id: mcp-servers +title: Building MCP servers +description: Deploy a Python MCP server as an Apify Actor and make its tools available to any MCP client. +--- + +import RunnableCodeBlock from '@site/src/components/RunnableCodeBlock'; + +import McpServerExample from '!!raw-loader!roa-loader!./code/14_mcp_server.py'; + +In this guide, you'll learn how to deploy a [Model Context Protocol](https://modelcontextprotocol.io) (MCP) server as an Apify Actor. + +## Introduction + +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. + +Apify Actors are a good fit for MCP servers: + +- 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. +- The platform scales instances with demand, keeps logs, and handles the network, so you don't operate any infrastructure. +- Every request carries an Apify API token, so the platform authenticates clients for you. +- Pay-per-event charging lets you monetize the server, for example per tool call. + +There are two ways to build one, and Apify maintains a template for each: + +- Write a server from scratch with [FastMCP](https://gofastmcp.com/), starting from the [`python-mcp-empty`](https://apify.com/templates/python-mcp-empty) template. +- 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. + +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`. + +## Example MCP server + +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: + + + {McpServerExample} + + +Note that: + +- A `build_server` helper registers the tools and resources. Add your own with the `@server.tool()` and `@server.resource()` decorators. +- `server.http_app(transport='streamable-http')` returns an ASGI app that speaks MCP over Streamable HTTP, which [uvicorn](https://www.uvicorn.org/) then serves. +- 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. +- 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. + +## Exposing it over Standby + +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`: + +```json +{ + "actorSpecification": 1, + "name": "my-mcp-server", + "usesStandbyMode": true, + "webServerMcpPath": "/mcp" +} +``` + +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: + +```json +{ + "mcpServers": { + "my-mcp-server": { + "url": "https://me--my-mcp-server.apify.actor/mcp", + "headers": { + "Authorization": "Bearer " + } + } + } +} +``` + +## Wrapping an existing MCP server + +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. + +It supports three kinds of upstream server. For a local stdio server, the gateway spawns the process: + +```python +from mcp.client.stdio import StdioServerParameters + +from .models import ServerType + +server_type = ServerType.STDIO +MCP_SERVER_PARAMS = StdioServerParameters(command='uv', args=['run', 'arxiv-mcp-server']) +``` + +For a remote HTTP or SSE server, it forwards to a URL: + +```python +from .models import RemoteServerParameters, ServerType + +server_type = ServerType.HTTP # or ServerType.SSE +MCP_SERVER_PARAMS = RemoteServerParameters(url='https://mcp.apify.com') +``` + +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. + +## Monetizing with pay-per-event + +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: + +```python +await Actor.charge('tool-call') +``` + +This lets you charge per tool call, per resource read, or per any other operation, and covers the cost of running the server. + +## Conclusion + +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! + +## Additional resources + +- [Apify templates: MCP server](https://apify.com/templates/python-mcp-empty) +- [Apify templates: MCP proxy](https://apify.com/templates/python-mcp-proxy) +- [Apify: actor-templates repository](https://github.com/apify/actor-templates) +- [Apify: MCP server documentation](https://docs.apify.com/platform/integrations/mcp) +- [Model Context Protocol: Official documentation](https://modelcontextprotocol.io) +- [FastMCP: Official documentation](https://gofastmcp.com/) +- [Apify blog: What is the Model Context Protocol](https://blog.apify.com/what-is-model-context-protocol/) diff --git a/docs/03_guides/code/14_mcp_server.py b/docs/03_guides/code/14_mcp_server.py new file mode 100644 index 00000000..6d9fd3e1 --- /dev/null +++ b/docs/03_guides/code/14_mcp_server.py @@ -0,0 +1,49 @@ +import asyncio + +import uvicorn +from fastmcp import FastMCP + +from apify import Actor + + +def build_server() -> FastMCP: + """Create a FastMCP server exposing one tool and one resource.""" + server = FastMCP(name='calculator') + + @server.tool() + def add(a: float, b: float) -> float: + """Add two numbers and return the sum.""" + return a + b + + @server.resource(uri='resource://calculator/info', name='calculator-info') + def info() -> str: + """Describe what this MCP server does.""" + return 'A simple calculator MCP server that adds two numbers.' + + return server + + +async def main() -> None: + async with Actor: + # Build the server and expose it over the Streamable HTTP transport. + server = build_server() + app = server.http_app(transport='streamable-http') + + # Serve it on the platform's web server port. Binding to 0.0.0.0 makes + # the server reachable through the Actor's container URL. + config = uvicorn.Config( + app, + host='0.0.0.0', # noqa: S104 + port=Actor.configuration.web_server_port, + ) + + url = Actor.configuration.web_server_url + Actor.log.info(f'MCP server is available at {url}/mcp') + + # Keep serving until the platform shuts the Actor down, for example when + # a Standby instance has been idle past its timeout. + await uvicorn.Server(config).serve() + + +if __name__ == '__main__': + asyncio.run(main()) From 830dd3b787d4faa44b8a4d55c3485e1abd9f481d Mon Sep 17 00:00:00 2001 From: Vlada Dusek Date: Wed, 17 Jun 2026 14:04:25 +0200 Subject: [PATCH 2/9] docs: split MCP servers guide into server and proxy sections --- docs/03_guides/14_mcp_servers.mdx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/03_guides/14_mcp_servers.mdx b/docs/03_guides/14_mcp_servers.mdx index cd45a88f..14d50dd8 100644 --- a/docs/03_guides/14_mcp_servers.mdx +++ b/docs/03_guides/14_mcp_servers.mdx @@ -23,14 +23,14 @@ Apify Actors are a good fit for MCP servers: There are two ways to build one, and Apify maintains a template for each: -- Write a server from scratch with [FastMCP](https://gofastmcp.com/), starting from the [`python-mcp-empty`](https://apify.com/templates/python-mcp-empty) template. -- 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. +- Write a server from scratch with [FastMCP](https://gofastmcp.com/), starting from the [`python-mcp-empty`](https://apify.com/templates/python-mcp-empty) template. See [MCP server](#mcp-server) below. +- 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. See [MCP proxy](#mcp-proxy) below. 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`. -## Example MCP server +## MCP server -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: +Build your own server when you want to expose your own tools and resources. 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: {McpServerExample} @@ -43,7 +43,7 @@ Note that: - 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. - 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. -## Exposing it over Standby +### Exposing it over Standby 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`: @@ -71,7 +71,7 @@ Once deployed, an MCP client connects to the Actor's URL using the Streamable HT } ``` -## Wrapping an existing MCP server +## MCP proxy 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. @@ -95,7 +95,7 @@ server_type = ServerType.HTTP # or ServerType.SSE MCP_SERVER_PARAMS = RemoteServerParameters(url='https://mcp.apify.com') ``` -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. +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. Like a server you build yourself, the proxy runs over [Actor Standby](#exposing-it-over-standby) and clients connect to it the same way. For details, see the template's README. ## Monetizing with pay-per-event From 4f7fcfc30ec4326fa1f32fcb072516591762ec02 Mon Sep 17 00:00:00 2001 From: Vlada Dusek Date: Fri, 3 Jul 2026 13:23:52 +0200 Subject: [PATCH 3/9] docs: add standalone MCP proxy example and expand MCP servers guide --- docs/03_guides/14_mcp_servers.mdx | 69 ++++++++++++++++++++--------- docs/03_guides/code/14_mcp_proxy.py | 37 ++++++++++++++++ 2 files changed, 85 insertions(+), 21 deletions(-) create mode 100644 docs/03_guides/code/14_mcp_proxy.py diff --git a/docs/03_guides/14_mcp_servers.mdx b/docs/03_guides/14_mcp_servers.mdx index 14d50dd8..8862570e 100644 --- a/docs/03_guides/14_mcp_servers.mdx +++ b/docs/03_guides/14_mcp_servers.mdx @@ -7,6 +7,7 @@ description: Deploy a Python MCP server as an Apify Actor and make its tools ava import RunnableCodeBlock from '@site/src/components/RunnableCodeBlock'; import McpServerExample from '!!raw-loader!roa-loader!./code/14_mcp_server.py'; +import McpProxyExample from '!!raw-loader!roa-loader!./code/14_mcp_proxy.py'; In this guide, you'll learn how to deploy a [Model Context Protocol](https://modelcontextprotocol.io) (MCP) server as an Apify Actor. @@ -24,10 +25,20 @@ Apify Actors are a good fit for MCP servers: There are two ways to build one, and Apify maintains a template for each: - Write a server from scratch with [FastMCP](https://gofastmcp.com/), starting from the [`python-mcp-empty`](https://apify.com/templates/python-mcp-empty) template. See [MCP server](#mcp-server) below. -- 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. See [MCP proxy](#mcp-proxy) below. +- Wrap an existing MCP server (stdio, HTTP, or SSE) with a proxy, starting from the [`python-mcp-proxy`](https://apify.com/templates/python-mcp-proxy) template. See [MCP proxy](#mcp-proxy) below. 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`. +## Before you start + +To follow along, install the [Apify CLI](https://docs.apify.com/cli/docs/installation) and log in with `apify login`. Both examples build on [FastMCP](https://gofastmcp.com/) and are served by [uvicorn](https://www.uvicorn.org/), so declare them in your Actor's `requirements.txt` next to the SDK: + +```text +apify +fastmcp +uvicorn +``` + ## MCP server Build your own server when you want to expose your own tools and resources. 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: @@ -73,43 +84,58 @@ Once deployed, an MCP client connects to the Actor's URL using the Streamable HT ## MCP proxy -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. +If you already have an MCP server, or want to expose a third-party one, you don't need to rewrite it. FastMCP can wrap an existing server and re-expose it over Streamable HTTP, so you put the same always-ready Apify endpoint in front of a server you didn't write. The following Actor proxies a remote MCP server, serving it on its web server port exactly like the server above: -It supports three kinds of upstream server. For a local stdio server, the gateway spawns the process: + + {McpProxyExample} + -```python -from mcp.client.stdio import StdioServerParameters +Note that: -from .models import ServerType +- `create_proxy` connects to the upstream server and returns a FastMCP instance, so the rest of the Actor is identical to a server you build yourself. +- The example wraps a remote URL. To spawn and wrap a local stdio server, pass an `mcpServers` config instead: -server_type = ServerType.STDIO -MCP_SERVER_PARAMS = StdioServerParameters(command='uv', args=['run', 'arxiv-mcp-server']) -``` + ```python + proxy = create_proxy( + {'mcpServers': {'arxiv': {'command': 'uv', 'args': ['run', 'arxiv-mcp-server']}}}, + name='my-mcp-proxy', + ) + ``` -For a remote HTTP or SSE server, it forwards to a URL: +- Serving and [Standby](#exposing-it-over-standby) work the same as before, so clients connect to `/mcp` with a bearer token. +- To control which tools clients may call, or to charge per call, add a FastMCP [middleware](https://gofastmcp.com/servers/middleware) that hooks `on_list_tools` and `on_call_tool`. -```python -from .models import RemoteServerParameters, ServerType +For a batteries-included gateway with a tool whitelist and per-operation charging already wired up, start from the [`python-mcp-proxy`](https://apify.com/templates/python-mcp-proxy) template. -server_type = ServerType.HTTP # or ServerType.SSE -MCP_SERVER_PARAMS = RemoteServerParameters(url='https://mcp.apify.com') -``` +## Monetizing with pay-per-event -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. Like a server you build yourself, the proxy runs over [Actor Standby](#exposing-it-over-standby) and clients connect to it the same way. For details, see the template's README. +Both approaches support [pay-per-event charging](../concepts/pay-per-event), so you can cover the cost of running the server or turn it into a paid product. First, define the events in the Actor's `.actor/pay_per_event.json`: -## Monetizing with pay-per-event +```json +{ + "tool-call": { + "eventTitle": "Price for completing a tool call", + "eventDescription": "Flat fee for completing a tool call.", + "eventPriceUsd": 0.05 + } +} +``` -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: +Then charge the event from your code when a client calls a tool: ```python -await Actor.charge('tool-call') +@server.tool() +async def add(a: float, b: float) -> float: + """Add two numbers and return the sum.""" + await Actor.charge(event_name='tool-call') + return a + b ``` -This lets you charge per tool call, per resource read, or per any other operation, and covers the cost of running the server. +You can charge per tool call, per resource read, or per any other operation. For the full setup, see the [pay-per-event](../concepts/pay-per-event) guide. ## Conclusion -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! +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 a proxy, 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! ## Additional resources @@ -120,3 +146,4 @@ In this guide, you learned how to deploy an MCP server as an Apify Actor. You ca - [Model Context Protocol: Official documentation](https://modelcontextprotocol.io) - [FastMCP: Official documentation](https://gofastmcp.com/) - [Apify blog: What is the Model Context Protocol](https://blog.apify.com/what-is-model-context-protocol/) +- [Apify blog: How to use MCP with Apify Actors](https://blog.apify.com/how-to-use-mcp/) diff --git a/docs/03_guides/code/14_mcp_proxy.py b/docs/03_guides/code/14_mcp_proxy.py new file mode 100644 index 00000000..5b3083f4 --- /dev/null +++ b/docs/03_guides/code/14_mcp_proxy.py @@ -0,0 +1,37 @@ +import asyncio + +import uvicorn +from fastmcp.server import create_proxy + +from apify import Actor + +# The upstream MCP server to expose. Point this at any remote Streamable HTTP or +# SSE endpoint. To wrap a local stdio server instead, pass an `mcpServers` config +# (see the MCP proxy guide section). +UPSTREAM_URL = 'https://mcp.example.com/mcp' + + +async def main() -> None: + async with Actor: + # Connect to the upstream server and re-expose it over Streamable HTTP. + proxy = create_proxy(UPSTREAM_URL, name='my-mcp-proxy') + app = proxy.http_app(transport='streamable-http') + + # Serve it on the platform's web server port, exactly like a server you + # build yourself. Binding to 0.0.0.0 makes it reachable through the + # Actor's container URL. + config = uvicorn.Config( + app, + host='0.0.0.0', # noqa: S104 + port=Actor.configuration.web_server_port, + ) + + url = Actor.configuration.web_server_url + Actor.log.info(f'MCP proxy is available at {url}/mcp') + + # Keep serving until the platform shuts the Actor down. + await uvicorn.Server(config).serve() + + +if __name__ == '__main__': + asyncio.run(main()) From a47483595066f92def3b6aa4f73f34fbc5fb9c91 Mon Sep 17 00:00:00 2001 From: Vlada Dusek Date: Fri, 3 Jul 2026 16:05:54 +0200 Subject: [PATCH 4/9] docs: retitle MCP servers guide and address review feedback --- docs/03_guides/14_mcp_servers.mdx | 10 +++++----- docs/03_guides/code/14_mcp_proxy.py | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/03_guides/14_mcp_servers.mdx b/docs/03_guides/14_mcp_servers.mdx index 8862570e..7f968d25 100644 --- a/docs/03_guides/14_mcp_servers.mdx +++ b/docs/03_guides/14_mcp_servers.mdx @@ -1,6 +1,6 @@ --- id: mcp-servers -title: Building MCP servers +title: Deploying MCP servers description: Deploy a Python MCP server as an Apify Actor and make its tools available to any MCP client. --- @@ -31,11 +31,11 @@ Both templates live in the [actor-templates repository](https://github.com/apify ## Before you start -To follow along, install the [Apify CLI](https://docs.apify.com/cli/docs/installation) and log in with `apify login`. Both examples build on [FastMCP](https://gofastmcp.com/) and are served by [uvicorn](https://www.uvicorn.org/), so declare them in your Actor's `requirements.txt` next to the SDK: +To follow along, install the [Apify CLI](https://docs.apify.com/cli/docs/installation) and log in with `apify login`. Both examples build on [FastMCP](https://gofastmcp.com/) v3 and are served by [uvicorn](https://www.uvicorn.org/), so declare them in your Actor's `requirements.txt` next to the SDK: ```text apify -fastmcp +fastmcp>=3 uvicorn ``` @@ -67,7 +67,7 @@ To make the server an always-ready HTTP API, enable [Actor Standby](https://docs } ``` -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: +Deploy the Actor with `apify push`. Once it's running, 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: ```json { @@ -121,7 +121,7 @@ Both approaches support [pay-per-event charging](../concepts/pay-per-event), so } ``` -Then charge the event from your code when a client calls a tool: +Then charge the event from your code when a client calls a tool. The `add` tool from the [server example](#mcp-server) becomes `async` so it can `await` the charge: ```python @server.tool() diff --git a/docs/03_guides/code/14_mcp_proxy.py b/docs/03_guides/code/14_mcp_proxy.py index 5b3083f4..5ecac968 100644 --- a/docs/03_guides/code/14_mcp_proxy.py +++ b/docs/03_guides/code/14_mcp_proxy.py @@ -6,8 +6,8 @@ from apify import Actor # The upstream MCP server to expose. Point this at any remote Streamable HTTP or -# SSE endpoint. To wrap a local stdio server instead, pass an `mcpServers` config -# (see the MCP proxy guide section). +# SSE endpoint. To wrap a local stdio server instead, pass `create_proxy` an +# `mcpServers` config mapping (`{'command': ..., 'args': [...]}`) instead of a URL. UPSTREAM_URL = 'https://mcp.example.com/mcp' From eb9dbfb391e32c35a1d6ca49f7af1d3c3a81736d Mon Sep 17 00:00:00 2001 From: Vlada Dusek Date: Fri, 3 Jul 2026 16:31:52 +0200 Subject: [PATCH 5/9] docs: refine MCP servers guide and make examples terminate --- docs/03_guides/14_mcp_servers.mdx | 60 ++++++++++++++-------------- docs/03_guides/code/14_mcp_proxy.py | 14 ++++++- docs/03_guides/code/14_mcp_server.py | 16 ++++++-- 3 files changed, 55 insertions(+), 35 deletions(-) diff --git a/docs/03_guides/14_mcp_servers.mdx b/docs/03_guides/14_mcp_servers.mdx index 7f968d25..ceb4bf5a 100644 --- a/docs/03_guides/14_mcp_servers.mdx +++ b/docs/03_guides/14_mcp_servers.mdx @@ -31,11 +31,11 @@ Both templates live in the [actor-templates repository](https://github.com/apify ## Before you start -To follow along, install the [Apify CLI](https://docs.apify.com/cli/docs/installation) and log in with `apify login`. Both examples build on [FastMCP](https://gofastmcp.com/) v3 and are served by [uvicorn](https://www.uvicorn.org/), so declare them in your Actor's `requirements.txt` next to the SDK: +To follow along, install the [Apify CLI](https://docs.apify.com/cli/docs/installation) and log in with `apify login`. Both examples build on [FastMCP](https://gofastmcp.com/) and are served by [uvicorn](https://www.uvicorn.org/), so declare them in your Actor's `requirements.txt` next to the SDK: ```text apify -fastmcp>=3 +fastmcp uvicorn ``` @@ -52,11 +52,36 @@ Note that: - A `build_server` helper registers the tools and resources. Add your own with the `@server.tool()` and `@server.resource()` decorators. - `server.http_app(transport='streamable-http')` returns an ASGI app that speaks MCP over Streamable HTTP, which [uvicorn](https://www.uvicorn.org/) then serves. - 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. -- 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. +- The example serves in the background for a short window so the run finishes on its own. A production Actor keeps serving until the platform shuts it down. With [Standby](#exposing-it-over-standby), shutdown happens automatically once an instance has been idle for a while. -### Exposing it over Standby +## MCP proxy + +If you already have an MCP server, or want to expose a third-party one, you don't need to rewrite it. FastMCP can wrap an existing server and re-expose it over Streamable HTTP, so you put the same always-ready Apify endpoint in front of a server you didn't write. The following Actor proxies a remote MCP server, serving it on its web server port exactly like the server above: + + + {McpProxyExample} + + +Note that: + +- `create_proxy` connects to the upstream server and returns a FastMCP instance, so the rest of the Actor is identical to a server you build yourself. +- The example wraps a remote URL. To spawn and wrap a local stdio server, pass an `mcpServers` config instead: + + ```python + proxy = create_proxy( + {'mcpServers': {'arxiv': {'command': 'uv', 'args': ['run', 'arxiv-mcp-server']}}}, + name='my-mcp-proxy', + ) + ``` + +- Serving works the same as the server above. You expose it over [Standby](#exposing-it-over-standby) the same way, so clients connect to `/mcp` with a bearer token. +- To control which tools clients may call, or to charge per call, add a FastMCP [middleware](https://gofastmcp.com/servers/middleware) that hooks `on_list_tools` and `on_call_tool`. + +For a batteries-included gateway with a tool whitelist and per-operation charging already wired up, start from the [`python-mcp-proxy`](https://apify.com/templates/python-mcp-proxy) template. + +## Exposing it over Standby -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`: +Both the server and the proxy listen on the web server port, so you expose either one the same way. To make it 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`: ```json { @@ -82,31 +107,6 @@ Deploy the Actor with `apify push`. Once it's running, an MCP client connects to } ``` -## MCP proxy - -If you already have an MCP server, or want to expose a third-party one, you don't need to rewrite it. FastMCP can wrap an existing server and re-expose it over Streamable HTTP, so you put the same always-ready Apify endpoint in front of a server you didn't write. The following Actor proxies a remote MCP server, serving it on its web server port exactly like the server above: - - - {McpProxyExample} - - -Note that: - -- `create_proxy` connects to the upstream server and returns a FastMCP instance, so the rest of the Actor is identical to a server you build yourself. -- The example wraps a remote URL. To spawn and wrap a local stdio server, pass an `mcpServers` config instead: - - ```python - proxy = create_proxy( - {'mcpServers': {'arxiv': {'command': 'uv', 'args': ['run', 'arxiv-mcp-server']}}}, - name='my-mcp-proxy', - ) - ``` - -- Serving and [Standby](#exposing-it-over-standby) work the same as before, so clients connect to `/mcp` with a bearer token. -- To control which tools clients may call, or to charge per call, add a FastMCP [middleware](https://gofastmcp.com/servers/middleware) that hooks `on_list_tools` and `on_call_tool`. - -For a batteries-included gateway with a tool whitelist and per-operation charging already wired up, start from the [`python-mcp-proxy`](https://apify.com/templates/python-mcp-proxy) template. - ## Monetizing with pay-per-event Both approaches support [pay-per-event charging](../concepts/pay-per-event), so you can cover the cost of running the server or turn it into a paid product. First, define the events in the Actor's `.actor/pay_per_event.json`: diff --git a/docs/03_guides/code/14_mcp_proxy.py b/docs/03_guides/code/14_mcp_proxy.py index 5ecac968..24864fb2 100644 --- a/docs/03_guides/code/14_mcp_proxy.py +++ b/docs/03_guides/code/14_mcp_proxy.py @@ -25,12 +25,22 @@ async def main() -> None: host='0.0.0.0', # noqa: S104 port=Actor.configuration.web_server_port, ) + web_server = uvicorn.Server(config) + + # Run the server in the background. + server_task = asyncio.create_task(web_server.serve()) url = Actor.configuration.web_server_url Actor.log.info(f'MCP proxy is available at {url}/mcp') - # Keep serving until the platform shuts the Actor down. - await uvicorn.Server(config).serve() + # In production the server runs until the platform shuts the Actor down. + # This runnable example instead serves for a short window so the run + # finishes on its own. + await asyncio.sleep(60) + + # Signal the server to shut down and wait. + web_server.should_exit = True + await server_task if __name__ == '__main__': diff --git a/docs/03_guides/code/14_mcp_server.py b/docs/03_guides/code/14_mcp_server.py index 6d9fd3e1..2bfff8a8 100644 --- a/docs/03_guides/code/14_mcp_server.py +++ b/docs/03_guides/code/14_mcp_server.py @@ -36,13 +36,23 @@ async def main() -> None: host='0.0.0.0', # noqa: S104 port=Actor.configuration.web_server_port, ) + web_server = uvicorn.Server(config) + + # Run the server in the background. + server_task = asyncio.create_task(web_server.serve()) url = Actor.configuration.web_server_url Actor.log.info(f'MCP server is available at {url}/mcp') - # Keep serving until the platform shuts the Actor down, for example when - # a Standby instance has been idle past its timeout. - await uvicorn.Server(config).serve() + # In production the server runs until the platform shuts the Actor down, + # for example when a Standby instance has been idle past its timeout. This + # runnable example instead serves for a short window so the run finishes + # on its own. + await asyncio.sleep(60) + + # Signal the server to shut down and wait. + web_server.should_exit = True + await server_task if __name__ == '__main__': From 807f036962da341024313de26ccad906c67de3d5 Mon Sep 17 00:00:00 2001 From: Vlada Dusek Date: Fri, 3 Jul 2026 16:53:42 +0200 Subject: [PATCH 6/9] docs: unify web server guide section titles by library name --- docs/03_guides/12_running_webserver.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/03_guides/12_running_webserver.mdx b/docs/03_guides/12_running_webserver.mdx index eac3816c..fb5fb284 100644 --- a/docs/03_guides/12_running_webserver.mdx +++ b/docs/03_guides/12_running_webserver.mdx @@ -23,7 +23,7 @@ The URL is available in the following places: The web server running inside the container must listen at the port defined by the `Actor.configuration.web_server_port` property. When running Actors locally, the port defaults to `4321`, so the web server will be accessible at `http://localhost:4321`. -## Example Actor +## Using the standard library The following example shows how to start a simple web server in your Actor, which will respond to every GET request with the number of items that the Actor has processed so far: From b2aa51861b5ad461832903761892a5fa5cde6a0e Mon Sep 17 00:00:00 2001 From: Vlada Dusek Date: Fri, 3 Jul 2026 16:59:19 +0200 Subject: [PATCH 7/9] docs: renumber MCP guide to 13 and list Pydantic guide in README --- README.md | 2 +- docs/03_guides/{14_mcp_servers.mdx => 13_mcp_servers.mdx} | 4 ++-- docs/03_guides/code/{14_mcp_proxy.py => 13_mcp_proxy.py} | 0 docs/03_guides/code/{14_mcp_server.py => 13_mcp_server.py} | 0 4 files changed, 3 insertions(+), 3 deletions(-) rename docs/03_guides/{14_mcp_servers.mdx => 13_mcp_servers.mdx} (98%) rename docs/03_guides/code/{14_mcp_proxy.py => 13_mcp_proxy.py} (100%) rename docs/03_guides/code/{14_mcp_server.py => 13_mcp_server.py} (100%) diff --git a/README.md b/README.md index 2c23bad2..28674d30 100644 --- a/README.md +++ b/README.md @@ -199,7 +199,7 @@ The full SDK documentation lives at **[docs.apify.com/sdk/python](https://docs.a | [Overview](https://docs.apify.com/sdk/python/docs/overview) | What the SDK is, what Actors are, and how the pieces fit together. | | [Quick start](https://docs.apify.com/sdk/python/docs/quick-start) | Create, run, and deploy your first Python Actor. | | [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. | -| [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. | +| [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, validating input with Pydantic, and using uv. | | [Upgrading](https://docs.apify.com/sdk/python/docs/upgrading/upgrading-to-v4) | Migrating between major versions. | | [API reference](https://docs.apify.com/sdk/python/reference) | Generated reference for every class and method. | | [Changelog](https://docs.apify.com/sdk/python/docs/changelog) | Release history and breaking changes. | diff --git a/docs/03_guides/14_mcp_servers.mdx b/docs/03_guides/13_mcp_servers.mdx similarity index 98% rename from docs/03_guides/14_mcp_servers.mdx rename to docs/03_guides/13_mcp_servers.mdx index ceb4bf5a..2a5216e5 100644 --- a/docs/03_guides/14_mcp_servers.mdx +++ b/docs/03_guides/13_mcp_servers.mdx @@ -6,8 +6,8 @@ description: Deploy a Python MCP server as an Apify Actor and make its tools ava import RunnableCodeBlock from '@site/src/components/RunnableCodeBlock'; -import McpServerExample from '!!raw-loader!roa-loader!./code/14_mcp_server.py'; -import McpProxyExample from '!!raw-loader!roa-loader!./code/14_mcp_proxy.py'; +import McpServerExample from '!!raw-loader!roa-loader!./code/13_mcp_server.py'; +import McpProxyExample from '!!raw-loader!roa-loader!./code/13_mcp_proxy.py'; In this guide, you'll learn how to deploy a [Model Context Protocol](https://modelcontextprotocol.io) (MCP) server as an Apify Actor. diff --git a/docs/03_guides/code/14_mcp_proxy.py b/docs/03_guides/code/13_mcp_proxy.py similarity index 100% rename from docs/03_guides/code/14_mcp_proxy.py rename to docs/03_guides/code/13_mcp_proxy.py diff --git a/docs/03_guides/code/14_mcp_server.py b/docs/03_guides/code/13_mcp_server.py similarity index 100% rename from docs/03_guides/code/14_mcp_server.py rename to docs/03_guides/code/13_mcp_server.py From 75d79267a7e16c21c3a4ca7dd75b23de22a3e3d4 Mon Sep 17 00:00:00 2001 From: Vlada Dusek Date: Fri, 3 Jul 2026 17:11:30 +0200 Subject: [PATCH 8/9] docs: address MCP guide review feedback --- docs/03_guides/13_mcp_servers.mdx | 2 +- docs/03_guides/code/13_mcp_proxy.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/03_guides/13_mcp_servers.mdx b/docs/03_guides/13_mcp_servers.mdx index 2a5216e5..13c266e0 100644 --- a/docs/03_guides/13_mcp_servers.mdx +++ b/docs/03_guides/13_mcp_servers.mdx @@ -51,7 +51,7 @@ Note that: - A `build_server` helper registers the tools and resources. Add your own with the `@server.tool()` and `@server.resource()` decorators. - `server.http_app(transport='streamable-http')` returns an ASGI app that speaks MCP over Streamable HTTP, which [uvicorn](https://www.uvicorn.org/) then serves. -- 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. +- 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. Such routing is the same mechanism described in the [Running a web server](./running-webserver) guide. - The example serves in the background for a short window so the run finishes on its own. A production Actor keeps serving until the platform shuts it down. With [Standby](#exposing-it-over-standby), shutdown happens automatically once an instance has been idle for a while. ## MCP proxy diff --git a/docs/03_guides/code/13_mcp_proxy.py b/docs/03_guides/code/13_mcp_proxy.py index 24864fb2..70ce76d1 100644 --- a/docs/03_guides/code/13_mcp_proxy.py +++ b/docs/03_guides/code/13_mcp_proxy.py @@ -7,7 +7,8 @@ # The upstream MCP server to expose. Point this at any remote Streamable HTTP or # SSE endpoint. To wrap a local stdio server instead, pass `create_proxy` an -# `mcpServers` config mapping (`{'command': ..., 'args': [...]}`) instead of a URL. +# `mcpServers` config mapping instead of a URL, for example +# `{'mcpServers': {'my-server': {'command': ..., 'args': [...]}}}`. UPSTREAM_URL = 'https://mcp.example.com/mcp' From e9d8ff0c3907d289fc0e7eb0eb47acb94b4a6d3a Mon Sep 17 00:00:00 2001 From: Vlada Dusek Date: Fri, 3 Jul 2026 17:27:18 +0200 Subject: [PATCH 9/9] docs: add Standby config examples to web server guide --- docs/03_guides/12_running_webserver.mdx | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/docs/03_guides/12_running_webserver.mdx b/docs/03_guides/12_running_webserver.mdx index fb5fb284..cdc575fb 100644 --- a/docs/03_guides/12_running_webserver.mdx +++ b/docs/03_guides/12_running_webserver.mdx @@ -52,14 +52,31 @@ Note that: - `uvicorn.Server(...).serve()` is a coroutine. It runs as an `asyncio` task alongside the Actor's own work instead of blocking it. Setting `server.should_exit = True` triggers a graceful shutdown once the work is done. - The server binds to `0.0.0.0` (all interfaces) rather than `localhost`. This makes it reachable through the container URL, not only from inside the container. -- The same pattern powers an [Actor Standby](#actor-standby) service. Swap the one-off work loop for an Actor that keeps serving requests. +- The same pattern powers an [Actor Standby](#exposing-it-over-standby) service. Swap the one-off work loop for an Actor that keeps serving requests. -## Actor Standby +## Exposing it over Standby The example runs a web server for the duration of a single Actor run. With [Actor Standby](https://docs.apify.com/platform/actors/development/programming-interface/standby), you can instead expose your Actor as an always-ready HTTP API: the platform keeps the Actor running in the background and routes incoming HTTP requests to the web server inside it, spinning up additional instances as the load grows. From the SDK's perspective, a Standby Actor is built the same way as the web server above. You start an HTTP server listening on the port from `Actor.configuration.web_server_port`. The difference is operational: instead of doing its work once and exiting, a Standby Actor stays up and serves requests. This makes it a good fit for low-latency, on-demand use cases, such as serving scraped data or acting as a microservice. +To enable Standby, set `usesStandbyMode` in the Actor's `.actor/actor.json`: + +```json +{ + "actorSpecification": 1, + "name": "my-standby-server", + "usesStandbyMode": true +} +``` + +Deploy the Actor with `apify push`. Once it's running, a client reaches the web server at the Actor's Standby URL, passing an [Apify API token](https://console.apify.com/account/integrations) as a bearer token: + +```bash +curl "https://me--my-standby-server.apify.actor" \ + -H "Authorization: Bearer " +``` + To get started, use the [Standby Python template](https://apify.com/templates/python-standby). For details on enabling Standby, request routing, and readiness probes, see the [Actor Standby documentation](https://docs.apify.com/platform/actors/development/programming-interface/standby). ## Conclusion