feat: Add Smithery CLI deployment support
- Add smithery package as dependency - Create smithery_server.py with @smithery.server() decorator - Add SmitheryConfigSchema for session config (nextcloud_url, username, app_password) - Add [tool.smithery] section to pyproject.toml - Remove manual .well-known/mcp-config endpoint (Smithery handles this) Smithery CLI will automatically: - Extract config schema from the decorated function - Handle session config parsing from query parameters - Make config accessible via ctx.session_config in tools 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1407,51 +1407,6 @@ def get_app(transport: str = "sse", enabled_apps: list[str] | None = None):
|
||||
routes.append(Route("/health/ready", health_ready, methods=["GET"]))
|
||||
logger.info("Health check endpoints enabled: /health/live, /health/ready")
|
||||
|
||||
# ADR-016: MCP config discovery endpoint for Smithery
|
||||
# Returns the session configuration schema that Smithery uses to render the config UI
|
||||
def mcp_config(request):
|
||||
"""MCP configuration discovery endpoint.
|
||||
|
||||
Returns the JSON schema for session configuration parameters.
|
||||
Used by Smithery to render the configuration form for users.
|
||||
"""
|
||||
return JSONResponse(
|
||||
{
|
||||
"configSchema": {
|
||||
"type": "object",
|
||||
"required": ["nextcloud_url", "username", "app_password"],
|
||||
"properties": {
|
||||
"nextcloud_url": {
|
||||
"type": "string",
|
||||
"title": "Nextcloud URL",
|
||||
"description": "Your Nextcloud instance URL (e.g., https://cloud.example.com). Must be publicly accessible.",
|
||||
"pattern": "^https?://.+",
|
||||
},
|
||||
"username": {
|
||||
"type": "string",
|
||||
"title": "Username",
|
||||
"description": "Your Nextcloud username",
|
||||
"minLength": 1,
|
||||
},
|
||||
"app_password": {
|
||||
"type": "string",
|
||||
"title": "App Password",
|
||||
"description": "Nextcloud app password. Generate at Settings > Security > App passwords. Do NOT use your main password.",
|
||||
"minLength": 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
"exampleConfig": {
|
||||
"nextcloud_url": "https://cloud.example.com",
|
||||
"username": "alice",
|
||||
"app_password": "xxxxx-xxxxx-xxxxx-xxxxx-xxxxx",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
routes.append(Route("/.well-known/mcp-config", mcp_config, methods=["GET"]))
|
||||
logger.info("MCP config discovery endpoint enabled: /.well-known/mcp-config")
|
||||
|
||||
# Add test webhook endpoint (for development/testing)
|
||||
routes.append(
|
||||
Route("/webhooks/nextcloud", handle_nextcloud_webhook, methods=["POST"])
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
"""Smithery server factory for stateless deployment.
|
||||
|
||||
ADR-016: This module provides a server factory function decorated with
|
||||
@smithery.server() for Smithery CLI deployment. Session configuration
|
||||
is automatically handled by Smithery and accessible via ctx.session_config.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
|
||||
from mcp.server.fastmcp import FastMCP
|
||||
from pydantic import BaseModel, Field
|
||||
from smithery.decorators import smithery
|
||||
|
||||
from nextcloud_mcp_server.server import (
|
||||
configure_calendar_tools,
|
||||
configure_contacts_tools,
|
||||
configure_cookbook_tools,
|
||||
configure_deck_tools,
|
||||
configure_notes_tools,
|
||||
configure_sharing_tools,
|
||||
configure_tables_tools,
|
||||
configure_webdav_tools,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SmitheryConfigSchema(BaseModel):
|
||||
"""Configuration schema for Smithery session.
|
||||
|
||||
These fields are collected by Smithery's configuration UI and passed
|
||||
to the server with each request as session_config.
|
||||
"""
|
||||
|
||||
nextcloud_url: str = Field(
|
||||
...,
|
||||
description="Your Nextcloud instance URL (e.g., https://cloud.example.com)",
|
||||
)
|
||||
username: str = Field(
|
||||
...,
|
||||
description="Your Nextcloud username",
|
||||
)
|
||||
app_password: str = Field(
|
||||
...,
|
||||
description="Nextcloud app password (Settings > Security > App passwords)",
|
||||
)
|
||||
|
||||
|
||||
@smithery.server(config_schema=SmitheryConfigSchema)
|
||||
def create_server():
|
||||
"""Create and return a FastMCP server instance for Smithery deployment.
|
||||
|
||||
This function is called by Smithery CLI to create the server.
|
||||
Session configuration is automatically handled by Smithery and
|
||||
accessible via ctx.session_config in tool handlers.
|
||||
"""
|
||||
# Force Smithery mode
|
||||
os.environ["SMITHERY_DEPLOYMENT"] = "true"
|
||||
os.environ["VECTOR_SYNC_ENABLED"] = "false"
|
||||
|
||||
logger.info("Creating Nextcloud MCP Server for Smithery deployment")
|
||||
|
||||
# Import lifespan after setting env vars
|
||||
from nextcloud_mcp_server.app import app_lifespan_smithery
|
||||
|
||||
# Create FastMCP server with Smithery lifespan
|
||||
mcp = FastMCP("Nextcloud MCP", lifespan=app_lifespan_smithery)
|
||||
|
||||
# Register all core tools (semantic search is skipped in Smithery mode)
|
||||
configure_notes_tools(mcp)
|
||||
configure_tables_tools(mcp)
|
||||
configure_webdav_tools(mcp)
|
||||
configure_sharing_tools(mcp)
|
||||
configure_calendar_tools(mcp)
|
||||
configure_contacts_tools(mcp)
|
||||
configure_cookbook_tools(mcp)
|
||||
configure_deck_tools(mcp)
|
||||
|
||||
logger.info("Smithery server configured with core Nextcloud tools")
|
||||
|
||||
return mcp
|
||||
Reference in New Issue
Block a user