MOHAN 2ee93048e1 feat: Add tools for managing server scripts, client scripts, translations, assignment rules, user permissions, webhooks, API keys, and workflows
- Implemented server and client script management tools in `frappe_mcp/tools/scripts.py`
- Added translation and user permission management tools in `frappe_mcp/tools/translations.py`
- Created user and role management tools in `frappe_mcp/tools/users.py`
- Developed webhook and API key management tools in `frappe_mcp/tools/webhooks.py`
- Introduced workflow management tools in `frappe_mcp/tools/workflow_tools.py`
- Added `pyproject.toml` for project metadata and dependencies
2026-04-21 20:26:45 +05:30

104 lines
3.2 KiB
Python

"""
Frappe MCP Server — entry point.
Modules can be individually enabled/disabled via ENABLED_MODULES in .env.
Transports:
(default) stdio — for local Claude Desktop use
--sse HTTP/SSE — for VPS hosting (requires starlette + uvicorn)
"""
import asyncio
import os
import sys
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import Tool, TextContent
from frappe_mcp.module_registry import get_enabled_tools, get_enabled_handlers
app = Server("frappe-mcp")
@app.list_tools()
async def list_tools() -> list[Tool]:
return get_enabled_tools()
@app.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
handlers = get_enabled_handlers()
handler = handlers.get(name)
if not handler:
return [TextContent(type="text", text=f"Unknown tool: {name}")]
try:
result = await handler(arguments)
return [TextContent(type="text", text=result)]
except Exception as e:
return [TextContent(type="text", text=f"Error: {e}")]
def main():
if "--test-connection" in sys.argv:
from frappe_mcp.healthcheck import run_health_check
ok = asyncio.run(run_health_check())
sys.exit(0 if ok else 1)
if "--list-modules" in sys.argv:
from frappe_mcp.module_registry import print_module_status
print_module_status()
sys.exit(0)
if "--sse" in sys.argv:
_run_sse()
return
asyncio.run(_run_stdio())
async def _run_stdio():
async with stdio_server() as (read_stream, write_stream):
await app.run(read_stream, write_stream, app.create_initialization_options())
def _run_sse():
try:
import uvicorn
from starlette.applications import Starlette
from starlette.middleware import Middleware
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request
from starlette.responses import Response
from starlette.routing import Mount, Route
from mcp.server.sse import SseServerTransport
except ImportError:
print("SSE dependencies missing. Run: pip install 'frappe-mcp[sse]'", file=sys.stderr)
sys.exit(1)
host = os.environ.get("MCP_HOST", "0.0.0.0")
port = int(os.environ.get("MCP_PORT", "8001"))
bearer_token = os.environ.get("MCP_BEARER_TOKEN", "")
sse = SseServerTransport("/messages/")
async def handle_sse(request: Request) -> Response:
if bearer_token:
auth = request.headers.get("Authorization", "")
if auth != f"Bearer {bearer_token}":
return Response("Unauthorized", status_code=401)
async with sse.connect_sse(
request.scope, request.receive, request._send
) as streams:
await app.run(streams[0], streams[1], app.create_initialization_options())
return Response()
starlette_app = Starlette(
routes=[
Route("/sse", endpoint=handle_sse),
Mount("/messages/", app=sse.handle_post_message),
]
)
print(f"Frappe MCP SSE server starting on {host}:{port}")
uvicorn.run(starlette_app, host=host, port=port)
if __name__ == "__main__":
main()