Files
Pietro Zullo be9942a4d4 feat/python/server (#559)
* feat: server first implementation

* docs: docs for server and images

* chore: locked langchain dependencies in pyproject.toml (#321)

* python/release/1.3.13

* catch uvicorn logs && log session id

* fix debug logic

* docs: logging update

* fix: icon of elicitation

* fix installation paths for local clones

* docs: minor restructuring

* docs: reorganization, killed non existing pages

* docs: beta tag on server

* chore: example servers

* feat: inspector autopopulated and client only

* feat: enhance MCP logging system with customizable formats and improved middleware

* feat: implement weather alert and forecast tools in FastMCP example server

* mcp path configurable

* fix inspector

* export as FastMCP too

* chore: update fastmcp migration example

* lock inspector version

* type runner

* unhide docs and remove dangers

* migration diff

* docs and context example

* export context

* logging improvement

* pretty JSON RPC

* autoConnect url param in inspecotr

* move logging

* type check

* Server Router

* show response logs

* fallback tool name to function name in router registration

* compatibility export

* Introduced ty and prek as type checker and precommit engine respectively - moved ruff config in pyproject toml

* add ty check

* ty runs only on staged files

* ty configuration fix

* move startup info in logging

* remove bug from action

* update claude rules

* fix resource serialization

* comments on inspector errors

* abstracted runner transports

* remove redunant call for network

* fix ip getter

* add inspector mcp path

* response limit logging

* disable ty check in action

* moved transport type to types

* support sse

* show transport type in logs

* change default transport

* refactored tests to use mcp server

* removed fastmcp

* file restructuring

* test table

* sampling, elicitation, tool disabling enabling

* deprecte sse server side

* simplified report

* fix action

* remove sse deprecated test

* move tests files to allow for server integration tests

* docs

* reorg integration test files

* api-reference-doxs

* fix broken links

* telemetry

* remove feature useage from scarf
2025-12-01 14:12:16 +01:00

174 lines
6.1 KiB
Python

from typing import TYPE_CHECKING, Any
from mcp import Resource, ServerCapabilities, Tool
from mcp.server.lowlevel.server import NotificationOptions
from mcp.types import Prompt, ResourceTemplate
from starlette.responses import JSONResponse
if TYPE_CHECKING:
from mcp_use.server.server import MCPServer
class OpenMCPInfo:
"""OpenMCP server info structure."""
def __init__(self, title: str, version: str | None = None, description: str | None = None):
self.title = title
self.version = version or "0.0.0"
self.description = description
class OpenMCPResponse:
"""Strongly typed OpenMCP response structure."""
def __init__(
self,
info: OpenMCPInfo,
capabilities: ServerCapabilities,
tools: list[Tool],
resources: list[Resource],
resources_templates: list[ResourceTemplate],
prompts: list[Prompt],
):
self.openmcp = "1.0"
self.info = {
"title": info.title,
"version": info.version,
"description": info.description,
}
self.capabilities = capabilities
self.tools = tools
self.resources = resources
self.resources_templates = resources_templates
self.prompts = prompts
def to_dict(self) -> dict[str, Any]:
"""Convert to dictionary for JSON serialization."""
return {
"openmcp": self.openmcp,
"info": self.info,
"capabilities": self.capabilities.model_dump(mode="json"),
"tools": [tool.model_dump(mode="json") for tool in self.tools],
"resources": [resource.model_dump(mode="json") for resource in self.resources],
"resources_templates": [
resource_template.model_dump(mode="json") for resource_template in self.resources_templates
],
"prompts": [prompt.model_dump(mode="json") for prompt in self.prompts],
}
async def get_openmcp_json(server: "MCPServer") -> JSONResponse:
"""
Generate OpenMCP JSON response for a FastMCP server.
Args:
server: The FastMCP server instance
Returns:
JSONResponse containing the OpenMCP server description
"""
import logging
logger = logging.getLogger(__name__)
try:
# Gather server information
logger.debug("Gathering server information for OpenMCP JSON response")
try:
tools = await server.list_tools()
logger.debug(f"Successfully retrieved {len(tools)} tools")
except Exception as e:
logger.error(f"Failed to retrieve tools: {e}")
tools = []
try:
resources = await server.list_resources()
logger.debug(f"Successfully retrieved {len(resources)} resources")
except Exception as e:
logger.error(f"Failed to retrieve resources: {e}")
resources = []
try:
resources_templates = await server.list_resource_templates()
logger.debug(f"Successfully retrieved {len(resources_templates)} resource templates")
except Exception as e:
logger.error(f"Failed to retrieve resource templates: {e}")
resources_templates = []
try:
capabilities = server._mcp_server.get_capabilities(NotificationOptions(), experimental_capabilities={})
logger.debug("Successfully retrieved server capabilities")
except Exception as e:
logger.error(f"Failed to retrieve capabilities: {e}")
capabilities = ServerCapabilities()
try:
prompts = await server.list_prompts()
logger.debug(f"Successfully retrieved {len(prompts)} prompts")
except Exception as e:
logger.error(f"Failed to retrieve prompts: {e}")
prompts = []
# Create server info
try:
info = OpenMCPInfo(title=server.name, version=server._mcp_server.version, description=server.instructions)
logger.debug("Successfully created server info")
except Exception as e:
logger.error(f"Failed to create server info: {e}")
info = OpenMCPInfo(title="Unknown", version="0.0.0", description=None)
# Build the response
try:
response = OpenMCPResponse(
info=info,
capabilities=capabilities,
tools=tools,
resources=resources,
resources_templates=resources_templates,
prompts=prompts,
)
logger.debug("Successfully created OpenMCP response")
except Exception as e:
logger.error(f"Failed to create OpenMCP response: {e}")
# Fallback response
response = OpenMCPResponse(
info=info,
capabilities=ServerCapabilities(),
tools=[],
resources=[],
resources_templates=[],
prompts=[],
)
try:
result = JSONResponse(response.to_dict())
logger.debug("Successfully generated JSON response")
return result
except Exception as e:
logger.error(f"Failed to generate JSON response: {e}")
# Return minimal fallback response
fallback_response = {
"openmcp": "1.0",
"info": {"title": "Error", "version": "0.0.0", "description": "Failed to generate response"},
"capabilities": {},
"tools": [],
"resources": [],
"resources_templates": [],
"prompts": [],
}
return JSONResponse(fallback_response)
except Exception as e:
logger.error(f"Unexpected error in get_openmcp_json: {e}")
# Return minimal error response
error_response = {
"openmcp": "1.0",
"info": {"title": "Error", "version": "0.0.0", "description": "Server error occurred"},
"capabilities": {},
"tools": [],
"resources": [],
"resources_templates": [],
"prompts": [],
}
return JSONResponse(error_response)