You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

mcp_server_comprehensive.py 9.1 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. import asyncio
  2. import json
  3. import logging
  4. from datetime import datetime
  5. from typing import Any, Dict, Optional
  6. from mcp import PromptsCapability, ResourcesCapability, ServerCapabilities, ToolsCapability
  7. from mcp.server import Server
  8. from mcp.server.models import InitializationOptions
  9. from mcp.server.stdio import stdio_server
  10. from mcp.types import (
  11. GetPromptResult,
  12. Prompt,
  13. PromptArgument,
  14. PromptMessage,
  15. Resource,
  16. TextContent,
  17. Tool,
  18. )
  19. from pydantic import AnyUrl
  20. # Configure logging
  21. logging.basicConfig(level=logging.INFO)
  22. logger = logging.getLogger(__name__)
  23. # Sample data for demonstration
  24. SAMPLE_DATA = {
  25. "users": [
  26. {"id": 1, "name": "Alice", "email": "alice@example.com", "department": "Engineering"},
  27. {"id": 2, "name": "Bob", "email": "bob@example.com", "department": "Sales"},
  28. {"id": 3, "name": "Charlie", "email": "charlie@example.com", "department": "Marketing"},
  29. ],
  30. "projects": [
  31. {"id": 1, "name": "Project Alpha", "status": "active", "team_size": 5},
  32. {"id": 2, "name": "Project Beta", "status": "completed", "team_size": 3},
  33. {"id": 3, "name": "Project Gamma", "status": "planning", "team_size": 2},
  34. ],
  35. }
  36. class SimpleMcpServer:
  37. """A simple MCP server demonstrating basic functionality."""
  38. def __init__(self) -> None:
  39. self.server: Server[object] = Server("simple-mcp-server")
  40. self.register_handlers() # type: ignore[no-untyped-call]
  41. def register_handlers(self) -> None:
  42. """Register all MCP handlers."""
  43. # Prompts
  44. @self.server.list_prompts() # type: ignore[no-untyped-call,misc]
  45. async def list_prompts() -> list[Prompt]: # pyright: ignore[reportUnusedFunction]
  46. """List available prompts."""
  47. return [
  48. Prompt(
  49. name="code_review",
  50. description="Generate a comprehensive code review for a given piece of code",
  51. arguments=[
  52. PromptArgument(
  53. name="code",
  54. description="The code to review",
  55. required=True,
  56. ),
  57. PromptArgument(
  58. name="language",
  59. description="Programming language of the code",
  60. required=True,
  61. ),
  62. ],
  63. ),
  64. Prompt(
  65. name="documentation",
  66. description="Generate documentation for code or APIs",
  67. arguments=[
  68. PromptArgument(
  69. name="content",
  70. description="The content to document",
  71. required=True,
  72. ),
  73. ],
  74. ),
  75. ]
  76. @self.server.get_prompt() # type: ignore[no-untyped-call,misc]
  77. async def get_prompt(name: str, arguments: Optional[Dict[str, str]] = None) -> GetPromptResult: # pyright: ignore[reportUnusedFunction]
  78. """Get a specific prompt with arguments."""
  79. if not arguments:
  80. arguments = {}
  81. if name == "code_review":
  82. code = arguments.get("code", "// No code provided")
  83. language = arguments.get("language", "unknown")
  84. return GetPromptResult(
  85. description=f"Code review for {language} code",
  86. messages=[
  87. PromptMessage(
  88. role="user",
  89. content=TextContent(
  90. type="text",
  91. text=f"Please review this {language} code:\n\n```{language}\n{code}\n```",
  92. ),
  93. ),
  94. ],
  95. )
  96. elif name == "documentation":
  97. content = arguments.get("content", "No content provided")
  98. return GetPromptResult(
  99. description="Documentation generation",
  100. messages=[
  101. PromptMessage(
  102. role="user",
  103. content=TextContent(
  104. type="text",
  105. text=f"Please generate documentation for:\n\n{content}",
  106. ),
  107. ),
  108. ],
  109. )
  110. else:
  111. raise ValueError(f"Unknown prompt: {name}")
  112. # Resources
  113. @self.server.list_resources() # type: ignore[no-untyped-call,misc]
  114. async def list_resources() -> list[Resource]: # pyright: ignore[reportUnusedFunction]
  115. """List available resources."""
  116. return [
  117. Resource(
  118. uri=AnyUrl("file:///company/users.json"),
  119. name="Company Users",
  120. description="List of all company users",
  121. mimeType="application/json",
  122. ),
  123. Resource(
  124. uri=AnyUrl("file:///company/projects.json"),
  125. name="Active Projects",
  126. description="Current projects",
  127. mimeType="application/json",
  128. ),
  129. ]
  130. @self.server.read_resource() # type: ignore[no-untyped-call,misc]
  131. async def read_resource(uri: AnyUrl) -> str: # pyright: ignore[reportUnusedFunction]
  132. """Read a specific resource."""
  133. uri_str = str(uri)
  134. if uri_str == "file:///company/users.json":
  135. return json.dumps(SAMPLE_DATA["users"], indent=2)
  136. elif uri_str == "file:///company/projects.json":
  137. return json.dumps(SAMPLE_DATA["projects"], indent=2)
  138. else:
  139. raise ValueError(f"Unknown resource: {uri_str}")
  140. # Tools
  141. @self.server.list_tools() # type: ignore[no-untyped-call,misc]
  142. async def list_tools() -> list[Tool]: # pyright: ignore[reportUnusedFunction]
  143. """List available tools."""
  144. return [
  145. Tool(
  146. name="echo",
  147. description="Echo back the input text",
  148. inputSchema={
  149. "type": "object",
  150. "properties": {
  151. "text": {
  152. "type": "string",
  153. "description": "Text to echo back",
  154. }
  155. },
  156. "required": ["text"],
  157. },
  158. ),
  159. Tool(
  160. name="get_time",
  161. description="Get the current time",
  162. inputSchema={
  163. "type": "object",
  164. "properties": {},
  165. },
  166. ),
  167. ]
  168. @self.server.call_tool() # type: ignore[no-untyped-call,misc]
  169. async def call_tool(name: str, arguments: Optional[Dict[str, Any]] = None) -> list[TextContent]: # pyright: ignore[reportUnusedFunction]
  170. """Call a specific tool."""
  171. if not arguments:
  172. arguments = {}
  173. if name == "echo":
  174. text = arguments.get("text", "")
  175. return [
  176. TextContent(
  177. type="text",
  178. text=f"Echo: {text}",
  179. )
  180. ]
  181. elif name == "get_time":
  182. current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
  183. return [
  184. TextContent(
  185. type="text",
  186. text=f"Current time: {current_time}",
  187. )
  188. ]
  189. else:
  190. raise ValueError(f"Unknown tool: {name}")
  191. async def run(self) -> None:
  192. """Run the MCP server."""
  193. # Server capabilities
  194. init_options = InitializationOptions(
  195. server_name="simple-mcp-server",
  196. server_version="1.0.0",
  197. capabilities=ServerCapabilities(
  198. prompts=PromptsCapability(listChanged=True),
  199. resources=ResourcesCapability(
  200. subscribe=True,
  201. listChanged=True,
  202. ),
  203. tools=ToolsCapability(listChanged=True),
  204. ),
  205. )
  206. # Run the server
  207. async with stdio_server() as (read_stream, write_stream):
  208. await self.server.run(
  209. read_stream,
  210. write_stream,
  211. init_options,
  212. )
  213. async def main() -> None:
  214. """Main entry point."""
  215. server: SimpleMcpServer = SimpleMcpServer()
  216. await server.run() # type: ignore[no-untyped-call]
  217. if __name__ == "__main__":
  218. asyncio.run(main()) # type: ignore[no-untyped-call]