1
+ import asyncio
2
+
3
+ from mcp .server .models import InitializationOptions
4
+ import mcp .types as types
5
+ from mcp .server import NotificationOptions , Server
6
+ from pydantic import AnyUrl
7
+ import mcp .server .stdio
8
+
9
+ # Store notes as a simple key-value dict to demonstrate state management
10
+ notes : dict [str , str ] = {}
11
+
12
+ server = Server ("mcp-server-rag-web-browser" )
13
+
14
+ @server .list_resources ()
15
+ async def handle_list_resources () -> list [types .Resource ]:
16
+ """
17
+ List available note resources.
18
+ Each note is exposed as a resource with a custom note:// URI scheme.
19
+ """
20
+ return [
21
+ types .Resource (
22
+ uri = AnyUrl (f"note://internal/{ name } " ),
23
+ name = f"Note: { name } " ,
24
+ description = f"A simple note named { name } " ,
25
+ mimeType = "text/plain" ,
26
+ )
27
+ for name in notes
28
+ ]
29
+
30
+ @server .read_resource ()
31
+ async def handle_read_resource (uri : AnyUrl ) -> str :
32
+ """
33
+ Read a specific note's content by its URI.
34
+ The note name is extracted from the URI host component.
35
+ """
36
+ if uri .scheme != "note" :
37
+ raise ValueError (f"Unsupported URI scheme: { uri .scheme } " )
38
+
39
+ name = uri .path
40
+ if name is not None :
41
+ name = name .lstrip ("/" )
42
+ return notes [name ]
43
+ raise ValueError (f"Note not found: { name } " )
44
+
45
+ @server .list_prompts ()
46
+ async def handle_list_prompts () -> list [types .Prompt ]:
47
+ """
48
+ List available prompts.
49
+ Each prompt can have optional arguments to customize its behavior.
50
+ """
51
+ return [
52
+ types .Prompt (
53
+ name = "summarize-notes" ,
54
+ description = "Creates a summary of all notes" ,
55
+ arguments = [
56
+ types .PromptArgument (
57
+ name = "style" ,
58
+ description = "Style of the summary (brief/detailed)" ,
59
+ required = False ,
60
+ )
61
+ ],
62
+ )
63
+ ]
64
+
65
+ @server .get_prompt ()
66
+ async def handle_get_prompt (
67
+ name : str , arguments : dict [str , str ] | None
68
+ ) -> types .GetPromptResult :
69
+ """
70
+ Generate a prompt by combining arguments with server state.
71
+ The prompt includes all current notes and can be customized via arguments.
72
+ """
73
+ if name != "summarize-notes" :
74
+ raise ValueError (f"Unknown prompt: { name } " )
75
+
76
+ style = (arguments or {}).get ("style" , "brief" )
77
+ detail_prompt = " Give extensive details." if style == "detailed" else ""
78
+
79
+ return types .GetPromptResult (
80
+ description = "Summarize the current notes" ,
81
+ messages = [
82
+ types .PromptMessage (
83
+ role = "user" ,
84
+ content = types .TextContent (
85
+ type = "text" ,
86
+ text = f"Here are the current notes to summarize:{ detail_prompt } \n \n "
87
+ + "\n " .join (
88
+ f"- { name } : { content } "
89
+ for name , content in notes .items ()
90
+ ),
91
+ ),
92
+ )
93
+ ],
94
+ )
95
+
96
+ @server .list_tools ()
97
+ async def handle_list_tools () -> list [types .Tool ]:
98
+ """
99
+ List available tools.
100
+ Each tool specifies its arguments using JSON Schema validation.
101
+ """
102
+ return [
103
+ types .Tool (
104
+ name = "add-note" ,
105
+ description = "Add a new note" ,
106
+ inputSchema = {
107
+ "type" : "object" ,
108
+ "properties" : {
109
+ "name" : {"type" : "string" },
110
+ "content" : {"type" : "string" },
111
+ },
112
+ "required" : ["name" , "content" ],
113
+ },
114
+ )
115
+ ]
116
+
117
+ @server .call_tool ()
118
+ async def handle_call_tool (
119
+ name : str , arguments : dict | None
120
+ ) -> list [types .TextContent | types .ImageContent | types .EmbeddedResource ]:
121
+ """
122
+ Handle tool execution requests.
123
+ Tools can modify server state and notify clients of changes.
124
+ """
125
+ if name != "add-note" :
126
+ raise ValueError (f"Unknown tool: { name } " )
127
+
128
+ if not arguments :
129
+ raise ValueError ("Missing arguments" )
130
+
131
+ note_name = arguments .get ("name" )
132
+ content = arguments .get ("content" )
133
+
134
+ if not note_name or not content :
135
+ raise ValueError ("Missing name or content" )
136
+
137
+ # Update server state
138
+ notes [note_name ] = content
139
+
140
+ # Notify clients that resources have changed
141
+ await server .request_context .session .send_resource_list_changed ()
142
+
143
+ return [
144
+ types .TextContent (
145
+ type = "text" ,
146
+ text = f"Added note '{ note_name } ' with content: { content } " ,
147
+ )
148
+ ]
149
+
150
+ async def main ():
151
+ # Run the server using stdin/stdout streams
152
+ async with mcp .server .stdio .stdio_server () as (read_stream , write_stream ):
153
+ await server .run (
154
+ read_stream ,
155
+ write_stream ,
156
+ InitializationOptions (
157
+ server_name = "mcp-server-rag-web-browser" ,
158
+ server_version = "0.1.0" ,
159
+ capabilities = server .get_capabilities (
160
+ notification_options = NotificationOptions (),
161
+ experimental_capabilities = {},
162
+ ),
163
+ ),
164
+ )
0 commit comments