Revenyu MCP
Enables Frappe Framework applications to function as Streamable HTTP MCP servers by providing a WSGI-compatible implementation for tool registration and JSON-RPC handling. It allows developers to expose Frappe app functionality to LLMs through a simple decorator-based interface.
README
Revenyu MCP
Revenyu MCP allows your Frappe Framework app to function as a Streamable HTTP MCP server.
# app/app/mcp.py
import revenyu_mcp
mcp = revenyu_mcp.MCP("todo-mcp")
@mcp.tool()
def fetch_todos(username: str): ...
@mcp.tool()
def mark_done(name: str): ...
# MCP endpoint at: http://<BASE_URL>/api/method/app.mcp.handle_mcp
@mcp.register()
def handle_mcp(): ...
[!NOTE]
Why not use the official Python SDK?
The official Python SDK only supports async Python, i.e. it assumes that your server is an ASGI server.
Frappe Framework is not async, it makes use of Werkzeug, a WSGI server, and so a from scratch implementation was needed.
[!IMPORTANT]
Revenyu MCP is in a highly experimental state, there will be bugs, breaking changes and large updates. Mostly without notice.
On GitHub, click the Index button on the top right to view the index.
Installation
To install Revenyu MCP in your Frappe 15 bench environment, navigate to your frappe-bench directory and run:
# Activate your bench environment (optional but recommended)
source ./env/bin/activate
# Install the package locally using pip
pip install -e /home/zaqout/Projects/cloud-apps/mcp
Limitations
Revenyu MCP is yet in its infancy, as of now it only supports Tools. Remaining server features such as resources, prompts, tool streaming using SSE will be added as needed.
Auth
If you are using a version of the Framework having the OAuth2 updates (frappe#33188) then using Frappe MCP with it should be pretty straight forward. You can view this video to check out how to set up Auth on the MCP Inspector.
https://github.com/user-attachments/assets/a1783a36-7bea-4361-8c7b-bdbb9789877b
If your version does not contain these updates, you will have to register an OAuth Client on your Framework instance for the MCP client. You can check the docs for this.
Documentation
Revenyu MCP is fairly straightforward to use. Most of the MCP specific heavy lifting is handled for you.
Basic Usage
To use revenyu-mcp you first create an instance of the mcp object:
# app/app/mcp.py (same dir as hooks.py)
import revenyu_mcp
mcp = revenyu_mcp.MCP("your-app-mcp")
Each instance of an MCP object can be used to register a single MCP endpoint.
You can create multiple of these objects if you need to serve multiple MCP endpoints for instance to group functionality.
Register tools with @mcp.tool
You use the instaniated object i.e. mcp to register tools:
# app/app/tools/tools.py
from app.mcp import mcp
@mcp.tool()
def tool_name(a: int, b: str):
"""Description of what the tool does
Args:
a: Description for arg `a`.
b: Description for arg `b`.
"""
... # tool body
return value
[!TIP]
Using Google style docstrings and type annotations like in the example above allows Revenyu MCP to extract the
inputSchemafor the tool without any additional configuration.
If needed, you can manually provide the inputSchema and other meta data like annotations.
Check the Tools section for more details.
Register endpoint using @mcp.register
You use the instantiated object to mark a function as the entry point to your MCP server, i.e. the function endpoint will be where your MCP server is served from.
# app/app/mcp.py
@mcp.register()
def handle_mcp():
import app.tools.tools # ensures that your tools are registered
Once this is done, your MCP server should be serving at the REST endpoint for the method (docs).
In this case the endpoint when running locally would be:
http://<SITE_NAME:PORT>/api/method/app.mcp.handle_mcp
[!WARNING]
The function body's only purpose is to import files containing your tools. If this is not done your tools will not be loaded as Revenyu MCP does not know where your tools are located.
If your tools are in the same file, or have been imported globally, you can leave the function body empty.
Tools
You can register tools, in the following ways:
- Using the
@mcp.tooldecorator - Using the
mcp.add_toolmethod
@mcp.tool decorator
The @mcp.tool decorator registers a function as a tool that can be used by an LLM.
The decorator accepts the following optional arguments:
name(optionalstr): The name of the tool. If not provided, the function's__name__will be used.description(optionalstr): A description of what the tool does. If not provided, it will be extracted from the function's docstring.input_schema(optionaldict): The JSON schema for the tool's input. If not provided, it will be inferred from the function's signature and docstring.use_entire_docstring(optionalbool): IfTrue, the entire docstring will be used as the tool's description. Otherwise, only the first section is used (i.e. noArgs). Defaults toFalse.annotations(optionaldict): Additional context about the tool, such as validation information or examples of how to use it. This should be a dictionary conforming to theToolAnnotationsTypedDictstructure.
Example:
from revenyu_mcp import ToolAnnotations, MCP
mcp = MCP()
annotations = ToolAnnotations(
title="Get Current Weather",
readOnlyHint=True,
)
@mcp.tool(annotations=annotations)
def get_current_weather(location: str, unit: str = "celsius"):
'''Get the current weather in a given location.'''
# ... implementation ...
mcp.add_tool method
The mcp.add_tool method allows manually defining a tool, serving as an alternative to the @mcp.tool decorator.
It takes a Tool object as an arg.
Example:
from revenyu_mcp import Tool, MCP
mcp = MCP()
def get_current_weather(location: str, unit: str = "celsius"):
'''Get the current weather in a given location.'''
# ... implementation ...
# Create a tool object
weather_tool = Tool(
name="get_current_weather",
description="...",
input_schema={'type':'object', 'properties':{ ... }},
output_schema=None,
annotations=None,
fn=get_current_weather,
)
# Add the tool to the MCP instance
mcp.add_tool(weather_tool)
Tool Annotations
The ToolAnnotations can be used to provide additional tool annotations
defined by the MCP spec
(reference).
class ToolAnnotations(TypedDict, total=False):
title: str | None
readOnlyHint: bool | None
destructiveHint: bool | None
idempotentHint: bool | None
openWorldHint: bool | None
Tool Definition
The Tool object that is used when manually defining and registering a tool
using mcp.add_tool.
class Tool(TypedDict):
name: str
description: str
input_schema: dict[str, Any]
output_schema: dict[str, Any] | None
annotations: ToolAnnotations | None
fn: Callable
Input Schema
Input schema refers to the JSON Schema definition that describes a tool's parameters.
The following tool:
@mcp.tool()
def tool_name(a: int, b: str = "default"):
"""Description of what the tool does
Args:
a: Description for arg `a`.
b: Description for arg `b`.
"""
... # tool body
return value
will have this input schema:
{
"type": "object",
"properties": {
"a": {
"type": "integer",
"description": "Description for arg `a`."
},
"b": {
"type": "string",
"description": "Description for arg `b`."
}
},
"required": ["a"]
}
This input schema is generated from the tool body automatically when using the decorator.
MCP
The MCP class is the main class for creating an MCP server.
This class orchestrates the handling of JSON-RPC requests, manages a registry of available tools, and integrates with a WSGI server (like Frappe Framework) to expose MCP functionality.
In a Frappe application, you would typically create a single instance of this
class and use the @mcp.register() decorator on an API endpoint. Tools can be
added using the @mcp.tool() decorator.
For use in other Werkzeug-based servers, you can use the mcp.handle() method
directly.
mcp.register decorator
This decorator is used in Frappe applications to designate a function as the entry point for MCP requests. It wraps the function with the necessary logic to handle JSON-RPC messages, including initializing the tool registry and routing requests to the appropriate handlers.
The decorator accepts the following optional arguments:
allow_guest(optionalbool): IfTrue, allows unauthenticated access to the endpoint. Defaults toFalse.xss_safe(optionalbool): IfTrue, response will not be sanitized for XSS. Defaults toFalse.
Example:
# In app/mcp.py
from revenyu_mcp import MCP
mcp = MCP(name="my-mcp-server")
@mcp.register()
def handle_mcp():
'''The entry point for MCP requests.'''
# This function body is executed before request handling.
# It's a good place to import modules that register tools.
import app.tools
mcp.handle method
This method directly processes a werkzeug.Request and returns a
werkzeug.Response. It's the core request handling logic.
This method can be used to integrate the MCP server into any Werkzeug-based application i.e. even if you're not using Frappe Framework, you can use this to handle MCP endpoints in your server.
It accepts the following arguments:
request: Thewerkzeug.Requestobject containing the MCP request.response: Awerkzeug.Responseobject to be populated with the MCP response.
It returns the populated werkzeug.Response object.
CLI
Revenyu MCP comes with a handy CLI tool to help you verify that your MCP server is set up correctly.
<img width="436" alt="check" src="https://github.com/user-attachments/assets/a8e1481a-5388-4976-9728-404677381a07" />
Its check command inspects your Frappe apps to ensure that revenyu_mcp is being used correctly. This is also the default command, so you can run it with revenyu-mcp or revenyu-mcp check.
It performs the following checks:
- Verifies that it's running within a Frappe environment.
- Finds all apps that are potentially using
revenyu_mcp. - For each app, it discovers MCP handlers.
- It then checks the handlers and their tools for correctness.
Options:
--app,-a': Check only a specific app.--verbose,-v: Show detailed information such as the input schema.
Usage:
# After installing revenyu-mcp and using it in your app
# In your frappe bench dir so that you can use the cli
source ./env/bin/activate
# Check all apps that might be using Revenyu MCP
revenyu-mcp
# Check specific app with verbose output
revenyu-mcp check --app app_name --verbose
Testing against Inspector
You can use the official inspector tool to verify if your MCP endpoints are being served correctly.
Make sure to:
- Set Transport to Streamable HTTP.
- Set URL to your MCP endpoint (you can use the CLI command
revenyu-mcp checkto get it). - Navigate to Auth Settings then click on Quick OAuth Flow
After this you'll be prompted to login and authorize the client after which you can use it to test out your MCP server.
[!NOTE]
You may skip the final step by setting the
allow_guestsflag, i.e:@mcp.register(allow_guests=True) def handle_mcp(): ...This bypasses auth, so make sure you don't do this in production.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。