initial mcp server setup

This commit is contained in:
OwusuBlessing
2025-09-11 23:13:58 +01:00
commit 20f96c0f30
141 changed files with 14444 additions and 0 deletions
@@ -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()