11"""Example MCP server with test tools"""
22import time
33import argparse
4+ from urllib .parse import urlparse
45from typing import Annotated , Optional , TypedDict , NotRequired
56from zeromcp import McpToolError , McpServer
67
78mcp = McpServer ("example" )
89
9- class SystemInfo (TypedDict ):
10- platform : Annotated [str , "Operating system platform" ]
11- python_version : Annotated [str , "Python version" ]
12- machine : Annotated [str , "Machine architecture" ]
13- timestamp : Annotated [float , "Current timestamp" ]
14-
15- class GreetingResponse (TypedDict ):
16- message : Annotated [str , "Greeting message" ]
17- name : Annotated [str , "Name that was greeted" ]
18- age : Annotated [NotRequired [int ], "Age if provided" ]
19-
2010@mcp .tool
2111def divide (
2212 numerator : Annotated [float , "Numerator" ],
@@ -25,6 +15,11 @@ def divide(
2515 """Divide two numbers (no zero check - tests natural exceptions)"""
2616 return numerator / denominator
2717
18+ class GreetingResponse (TypedDict ):
19+ message : Annotated [str , "Greeting message" ]
20+ name : Annotated [str , "Name that was greeted" ]
21+ age : Annotated [NotRequired [int ], "Age if provided" ]
22+
2823@mcp .tool
2924def greet (
3025 name : Annotated [str , "Name to greet" ],
@@ -42,6 +37,12 @@ def greet(
4237 "name" : name
4338 }
4439
40+ class SystemInfo (TypedDict ):
41+ platform : Annotated [str , "Operating system platform" ]
42+ python_version : Annotated [str , "Python version" ]
43+ machine : Annotated [str , "Machine architecture" ]
44+ timestamp : Annotated [float , "Current timestamp" ]
45+
4546@mcp .tool
4647def get_system_info () -> SystemInfo :
4748 """Get system information"""
@@ -78,6 +79,16 @@ def struct_get(
7879 for name in (names if isinstance (names , list ) else [names ])
7980 ]
8081
82+ @mcp .tool
83+ def random_dict (param : dict [str , int ] | None ) -> dict :
84+ """Return a random dictionary for testing serialization"""
85+ return {
86+ ** (param or {}),
87+ "x" : 42 ,
88+ "y" : 7 ,
89+ "z" : 99 ,
90+ }
91+
8192@mcp .resource ("example://system_info" )
8293def system_info_resource () -> SystemInfo :
8394 """Resource providing system information"""
@@ -90,28 +101,32 @@ def greeting_resource(
90101 """Resource providing greeting message"""
91102 return greet (name )
92103
104+ @mcp .resource ("example://error" )
105+ def error_resource () -> None :
106+ """Resource that always fails (for testing error handling)"""
107+ raise McpToolError ("This is a resource error for testing purposes." )
108+
93109if __name__ == "__main__" :
94110 parser = argparse .ArgumentParser (description = "MCP Example Server" )
95- parser .add_argument ("--stdio " , action = "store_true" , help = "Run MCP server over stdio " )
111+ parser .add_argument ("--transport " , help = "Transport (stdio or http://host:port)" , default = "http://127.0.0.1:5001 " )
96112 args = parser .parse_args ()
97- if args .stdio :
113+ if args .transport == " stdio" :
98114 mcp .stdio ()
99115 else :
116+ url = urlparse (args .transport )
117+ if url .hostname is None or url .port is None :
118+ raise Exception (f"Invalid transport URL: { args .transport } " )
119+
100120 print ("Starting MCP Example Server..." )
101121 print ("\n Available tools:" )
102122 for name in mcp ._tools .methods .keys ():
103123 func = mcp ._tools .methods [name ]
104124 print (f" - { name } : { func .__doc__ } " )
105125
106- mcp .serve ("127.0.0.1" , 5001 )
107-
108- print ("\n " + "=" * 60 )
109- print ("Server is running. Press Ctrl+C to stop." )
110- print ("=" * 60 )
126+ mcp .serve (url .hostname , url .port )
111127
112128 try :
113- while True :
114- time .sleep (1 )
115- except KeyboardInterrupt :
129+ input ("Server is running, press Enter or Ctrl+C to stop." )
130+ except (KeyboardInterrupt , EOFError ):
116131 print ("\n \n Stopping server..." )
117132 mcp .stop ()
0 commit comments