|
- import asyncio
- import json
- import logging
- from datetime import datetime
- from typing import Any, Dict, Optional
-
- from mcp import PromptsCapability, ResourcesCapability, ServerCapabilities, ToolsCapability
- from mcp.server import Server
- from mcp.server.models import InitializationOptions
- from mcp.server.stdio import stdio_server
- from mcp.types import (
- GetPromptResult,
- Prompt,
- PromptArgument,
- PromptMessage,
- Resource,
- TextContent,
- Tool,
- )
- from pydantic import AnyUrl
-
- # Configure logging
- logging.basicConfig(level=logging.INFO)
- logger = logging.getLogger(__name__)
-
- # Sample data for demonstration
- SAMPLE_DATA = {
- "users": [
- {"id": 1, "name": "Alice", "email": "alice@example.com", "department": "Engineering"},
- {"id": 2, "name": "Bob", "email": "bob@example.com", "department": "Sales"},
- {"id": 3, "name": "Charlie", "email": "charlie@example.com", "department": "Marketing"},
- ],
- "projects": [
- {"id": 1, "name": "Project Alpha", "status": "active", "team_size": 5},
- {"id": 2, "name": "Project Beta", "status": "completed", "team_size": 3},
- {"id": 3, "name": "Project Gamma", "status": "planning", "team_size": 2},
- ],
- }
-
-
- class SimpleMcpServer:
- """A simple MCP server demonstrating basic functionality."""
-
- def __init__(self) -> None:
- self.server: Server[object] = Server("simple-mcp-server")
- self.register_handlers() # type: ignore[no-untyped-call]
-
- def register_handlers(self) -> None:
- """Register all MCP handlers."""
-
- # Prompts
- @self.server.list_prompts() # type: ignore[no-untyped-call,misc]
- async def list_prompts() -> list[Prompt]: # pyright: ignore[reportUnusedFunction]
- """List available prompts."""
- return [
- Prompt(
- name="code_review",
- description="Generate a comprehensive code review for a given piece of code",
- arguments=[
- PromptArgument(
- name="code",
- description="The code to review",
- required=True,
- ),
- PromptArgument(
- name="language",
- description="Programming language of the code",
- required=True,
- ),
- ],
- ),
- Prompt(
- name="documentation",
- description="Generate documentation for code or APIs",
- arguments=[
- PromptArgument(
- name="content",
- description="The content to document",
- required=True,
- ),
- ],
- ),
- ]
-
- @self.server.get_prompt() # type: ignore[no-untyped-call,misc]
- async def get_prompt(name: str, arguments: Optional[Dict[str, str]] = None) -> GetPromptResult: # pyright: ignore[reportUnusedFunction]
- """Get a specific prompt with arguments."""
- if not arguments:
- arguments = {}
-
- if name == "code_review":
- code = arguments.get("code", "// No code provided")
- language = arguments.get("language", "unknown")
-
- return GetPromptResult(
- description=f"Code review for {language} code",
- messages=[
- PromptMessage(
- role="user",
- content=TextContent(
- type="text",
- text=f"Please review this {language} code:\n\n```{language}\n{code}\n```",
- ),
- ),
- ],
- )
-
- elif name == "documentation":
- content = arguments.get("content", "No content provided")
-
- return GetPromptResult(
- description="Documentation generation",
- messages=[
- PromptMessage(
- role="user",
- content=TextContent(
- type="text",
- text=f"Please generate documentation for:\n\n{content}",
- ),
- ),
- ],
- )
-
- else:
- raise ValueError(f"Unknown prompt: {name}")
-
- # Resources
- @self.server.list_resources() # type: ignore[no-untyped-call,misc]
- async def list_resources() -> list[Resource]: # pyright: ignore[reportUnusedFunction]
- """List available resources."""
- return [
- Resource(
- uri=AnyUrl("file:///company/users.json"),
- name="Company Users",
- description="List of all company users",
- mimeType="application/json",
- ),
- Resource(
- uri=AnyUrl("file:///company/projects.json"),
- name="Active Projects",
- description="Current projects",
- mimeType="application/json",
- ),
- ]
-
- @self.server.read_resource() # type: ignore[no-untyped-call,misc]
- async def read_resource(uri: AnyUrl) -> str: # pyright: ignore[reportUnusedFunction]
- """Read a specific resource."""
- uri_str = str(uri)
-
- if uri_str == "file:///company/users.json":
- return json.dumps(SAMPLE_DATA["users"], indent=2)
-
- elif uri_str == "file:///company/projects.json":
- return json.dumps(SAMPLE_DATA["projects"], indent=2)
-
- else:
- raise ValueError(f"Unknown resource: {uri_str}")
-
- # Tools
- @self.server.list_tools() # type: ignore[no-untyped-call,misc]
- async def list_tools() -> list[Tool]: # pyright: ignore[reportUnusedFunction]
- """List available tools."""
- return [
- Tool(
- name="echo",
- description="Echo back the input text",
- inputSchema={
- "type": "object",
- "properties": {
- "text": {
- "type": "string",
- "description": "Text to echo back",
- }
- },
- "required": ["text"],
- },
- ),
- Tool(
- name="get_time",
- description="Get the current time",
- inputSchema={
- "type": "object",
- "properties": {},
- },
- ),
- ]
-
- @self.server.call_tool() # type: ignore[no-untyped-call,misc]
- async def call_tool(name: str, arguments: Optional[Dict[str, Any]] = None) -> list[TextContent]: # pyright: ignore[reportUnusedFunction]
- """Call a specific tool."""
- if not arguments:
- arguments = {}
-
- if name == "echo":
- text = arguments.get("text", "")
- return [
- TextContent(
- type="text",
- text=f"Echo: {text}",
- )
- ]
-
- elif name == "get_time":
- current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
- return [
- TextContent(
- type="text",
- text=f"Current time: {current_time}",
- )
- ]
-
- else:
- raise ValueError(f"Unknown tool: {name}")
-
- async def run(self) -> None:
- """Run the MCP server."""
- # Server capabilities
- init_options = InitializationOptions(
- server_name="simple-mcp-server",
- server_version="1.0.0",
- capabilities=ServerCapabilities(
- prompts=PromptsCapability(listChanged=True),
- resources=ResourcesCapability(
- subscribe=True,
- listChanged=True,
- ),
- tools=ToolsCapability(listChanged=True),
- ),
- )
-
- # Run the server
- async with stdio_server() as (read_stream, write_stream):
- await self.server.run(
- read_stream,
- write_stream,
- init_options,
- )
-
-
- async def main() -> None:
- """Main entry point."""
- server: SimpleMcpServer = SimpleMcpServer()
- await server.run() # type: ignore[no-untyped-call]
-
-
- if __name__ == "__main__":
- asyncio.run(main()) # type: ignore[no-untyped-call]
|