initial mcp server setup
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
"""
|
||||
Server Resources Module
|
||||
"""
|
||||
from .base_resource import BaseServerResource
|
||||
from .resource_registry import ServerResourceRegistry
|
||||
|
||||
__all__ = ['BaseServerResource', 'ServerResourceRegistry']
|
||||
@@ -0,0 +1,41 @@
|
||||
"""
|
||||
Base Server Resource Class
|
||||
"""
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Any, Dict, Optional, Union
|
||||
|
||||
|
||||
class BaseServerResource(ABC):
|
||||
"""Base class for server resources that can be registered with FastMCP"""
|
||||
|
||||
def __init__(self, uri: str, name: str, description: str, mime_type: str = "text/plain"):
|
||||
self.uri = uri
|
||||
self.name = name
|
||||
self.description = description
|
||||
self.mime_type = mime_type
|
||||
|
||||
@abstractmethod
|
||||
async def get_content(self, **kwargs) -> Union[str, bytes]:
|
||||
"""Get the resource content with the provided arguments"""
|
||||
pass
|
||||
|
||||
def get_resource_definition(self) -> Dict[str, Any]:
|
||||
"""Get the resource definition for FastMCP registration"""
|
||||
return {
|
||||
"uri": self.uri,
|
||||
"name": self.name,
|
||||
"description": self.description,
|
||||
"mime_type": self.mime_type
|
||||
}
|
||||
|
||||
def create_fastmcp_resource(self, mcp_server):
|
||||
"""Create a FastMCP resource decorator for this resource"""
|
||||
@mcp_server.resource(self.uri)
|
||||
async def resource_wrapper(**kwargs):
|
||||
return await self.get_content(**kwargs)
|
||||
|
||||
# Set metadata
|
||||
resource_wrapper.__name__ = self.name
|
||||
resource_wrapper.__doc__ = self.description
|
||||
|
||||
return resource_wrapper
|
||||
@@ -0,0 +1,37 @@
|
||||
"""
|
||||
Configuration Resource Example
|
||||
"""
|
||||
from .base_resource import BaseServerResource
|
||||
|
||||
|
||||
class ConfigResource(BaseServerResource):
|
||||
"""A configuration resource that provides server settings"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
uri="config://settings",
|
||||
name="Server Configuration",
|
||||
description="Get the current server configuration settings",
|
||||
mime_type="application/json"
|
||||
)
|
||||
|
||||
async def get_content(self) -> str:
|
||||
"""Get the configuration content"""
|
||||
import json
|
||||
config = {
|
||||
"server_name": "MCP Template Server",
|
||||
"version": "1.0.0",
|
||||
"features": [
|
||||
"tools",
|
||||
"prompts",
|
||||
"resources"
|
||||
],
|
||||
"transport": {
|
||||
"supported": ["stdio", "sse"],
|
||||
"default": "stdio"
|
||||
},
|
||||
"tools_count": 2,
|
||||
"prompts_count": 1,
|
||||
"resources_count": 1
|
||||
}
|
||||
return json.dumps(config, indent=2)
|
||||
@@ -0,0 +1,28 @@
|
||||
"""
|
||||
Dynamic Resource Example
|
||||
"""
|
||||
from .base_resource import BaseServerResource
|
||||
|
||||
|
||||
class DynamicResource(BaseServerResource):
|
||||
"""A dynamic resource that accepts parameters"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
uri="dynamic://greeting/{name}",
|
||||
name="Dynamic Greeting",
|
||||
description="Get a personalized greeting resource",
|
||||
mime_type="text/plain"
|
||||
)
|
||||
|
||||
async def get_content(self, name: str) -> str:
|
||||
"""Get the dynamic greeting content"""
|
||||
return f"Hello, {name}! This is a dynamic resource that was generated just for you.\n\n" \
|
||||
f"Resource URI: dynamic://greeting/{name}\n" \
|
||||
f"Generated at: {self._get_timestamp()}\n" \
|
||||
f"Personalized for: {name}"
|
||||
|
||||
def _get_timestamp(self) -> str:
|
||||
"""Get current timestamp"""
|
||||
from datetime import datetime
|
||||
return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
@@ -0,0 +1,100 @@
|
||||
"""
|
||||
Server Resource Registry for dynamic resource registration
|
||||
"""
|
||||
import os
|
||||
import importlib
|
||||
import inspect
|
||||
from typing import List, Dict, Any, Type,Optional
|
||||
from pathlib import Path
|
||||
from .base_resource import BaseServerResource
|
||||
|
||||
|
||||
class ServerResourceRegistry:
|
||||
"""Registry for managing server resources from files"""
|
||||
|
||||
def __init__(self, resources_directory: str = None):
|
||||
self.resources_directory = resources_directory or os.path.dirname(__file__)
|
||||
self._registered_resources: Dict[str, BaseServerResource] = {}
|
||||
self._resource_files: List[str] = []
|
||||
|
||||
@property
|
||||
def directory(self):
|
||||
"""Alias for resources_directory for backward compatibility"""
|
||||
return self.resources_directory
|
||||
|
||||
@property
|
||||
def _resources(self):
|
||||
"""Alias for _registered_resources for backward compatibility"""
|
||||
return self._registered_resources
|
||||
|
||||
def discover_resources(self) -> List[BaseServerResource]:
|
||||
"""Discover all resources in the resources directory"""
|
||||
resources = []
|
||||
resources_dir = Path(self.resources_directory)
|
||||
|
||||
if not resources_dir.exists():
|
||||
return resources
|
||||
|
||||
# Find all Python files in the resources directory
|
||||
for file_path in resources_dir.glob("*.py"):
|
||||
if file_path.name.startswith("__"):
|
||||
continue
|
||||
|
||||
module_name = file_path.stem
|
||||
try:
|
||||
# Import the module
|
||||
spec = importlib.util.spec_from_file_location(module_name, file_path)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
|
||||
# Find all BaseServerResource subclasses in the module
|
||||
for name, obj in inspect.getmembers(module, inspect.isclass):
|
||||
if (issubclass(obj, BaseServerResource) and
|
||||
obj != BaseServerResource and
|
||||
not inspect.isabstract(obj)):
|
||||
try:
|
||||
resource_instance = obj()
|
||||
resources.append(resource_instance)
|
||||
self._registered_resources[resource_instance.uri] = resource_instance
|
||||
except Exception as e:
|
||||
print(f"Warning: Could not instantiate resource {name}: {e}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Warning: Could not load resource file {file_path}: {e}")
|
||||
|
||||
return resources
|
||||
|
||||
def register_resource(self, resource: BaseServerResource) -> None:
|
||||
"""Register a single resource"""
|
||||
self._registered_resources[resource.uri] = resource
|
||||
|
||||
def register_resources(self, resources: List[BaseServerResource]) -> None:
|
||||
"""Register multiple resources"""
|
||||
for resource in resources:
|
||||
self.register_resource(resource)
|
||||
|
||||
def get_resource(self, uri: str) -> Optional[BaseServerResource]:
|
||||
"""Get a resource by URI"""
|
||||
return self._registered_resources.get(uri)
|
||||
|
||||
def get_all_resources(self) -> List[BaseServerResource]:
|
||||
"""Get all registered resources"""
|
||||
return list(self._registered_resources.values())
|
||||
|
||||
def get_resource_uris(self) -> List[str]:
|
||||
"""Get all registered resource URIs"""
|
||||
return list(self._registered_resources.keys())
|
||||
|
||||
def register_resources_with_server(self, mcp_server) -> None:
|
||||
"""Register all discovered resources with a FastMCP server"""
|
||||
# First discover resources if not already done
|
||||
if not self._registered_resources:
|
||||
self.discover_resources()
|
||||
|
||||
# Register each resource with the server
|
||||
for resource in self._registered_resources.values():
|
||||
resource.create_fastmcp_resource(mcp_server)
|
||||
|
||||
def clear_resources(self) -> None:
|
||||
"""Clear all registered resources"""
|
||||
self._registered_resources.clear()
|
||||
Reference in New Issue
Block a user