Skip to content
This repository was archived by the owner on Mar 24, 2025. It is now read-only.

refactor: simplify template with empty stubs #12

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 3 additions & 11 deletions src/create_mcp_server/template/README.md.jinja2
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,15 @@

### Resources

The server implements a simple note storage system with:
- Custom note:// URI scheme for accessing individual notes
- Each note resource has a name, description and text/plain mimetype
Implement the `handle_list_resources()` and `handle_read_resource()` methods to expose and read resources.

### Prompts

The server provides a single prompt:
- summarize-notes: Creates summaries of all stored notes
- Optional "style" argument to control detail level (brief/detailed)
- Generates prompt combining all current notes with style preference
Implement the `handle_list_prompts()` and `handle_get_prompt()` methods to expose and generate prompts.

### Tools

The server implements one tool:
- add-note: Adds a new note to the server
- Takes "name" and "content" as required string arguments
- Updates server state and notifies clients of resource changes
Implement the `handle_list_tools()` and `handle_call_tool()` methods to expose and execute tools.

## Configuration

Expand Down
116 changes: 14 additions & 102 deletions src/create_mcp_server/template/server.py.jinja2
Original file line number Diff line number Diff line change
Expand Up @@ -6,149 +6,61 @@ from mcp.server import NotificationOptions, Server
from pydantic import AnyUrl
import mcp.server.stdio

# Store notes as a simple key-value dict to demonstrate state management
notes: dict[str, str] = {}

server = Server("{{server_name}}")

@server.list_resources()
async def handle_list_resources() -> list[types.Resource]:
"""
List available note resources.
Each note is exposed as a resource with a custom note:// URI scheme.
List available resources.
Implement this method to expose your server's resources.
"""
return [
types.Resource(
uri=AnyUrl(f"note://internal/{name}"),
name=f"Note: {name}",
description=f"A simple note named {name}",
mimeType="text/plain",
)
for name in notes
]
return []

@server.read_resource()
async def handle_read_resource(uri: AnyUrl) -> str:
"""
Read a specific note's content by its URI.
The note name is extracted from the URI host component.
Read a specific resource's content by its URI.
Implement this method to return the content of a resource.
"""
if uri.scheme != "note":
raise ValueError(f"Unsupported URI scheme: {uri.scheme}")

name = uri.path
if name is not None:
name = name.lstrip("/")
return notes[name]
raise ValueError(f"Note not found: {name}")
raise NotImplementedError("Resource reading not implemented")

@server.list_prompts()
async def handle_list_prompts() -> list[types.Prompt]:
"""
List available prompts.
Each prompt can have optional arguments to customize its behavior.
Implement this method to expose your server's prompts.
"""
return [
types.Prompt(
name="summarize-notes",
description="Creates a summary of all notes",
arguments=[
types.PromptArgument(
name="style",
description="Style of the summary (brief/detailed)",
required=False,
)
],
)
]
return []

@server.get_prompt()
async def handle_get_prompt(
name: str, arguments: dict[str, str] | None
) -> types.GetPromptResult:
"""
Generate a prompt by combining arguments with server state.
The prompt includes all current notes and can be customized via arguments.
Implement this method to generate prompts based on the given name and arguments.
"""
if name != "summarize-notes":
raise ValueError(f"Unknown prompt: {name}")

style = (arguments or {}).get("style", "brief")
detail_prompt = " Give extensive details." if style == "detailed" else ""

return types.GetPromptResult(
description="Summarize the current notes",
messages=[
types.PromptMessage(
role="user",
content=types.TextContent(
type="text",
text=f"Here are the current notes to summarize:{detail_prompt}\n\n"
+ "\n".join(
f"- {name}: {content}"
for name, content in notes.items()
),
),
)
],
)
raise NotImplementedError("Prompt generation not implemented")

@server.list_tools()
async def handle_list_tools() -> list[types.Tool]:
"""
List available tools.
Each tool specifies its arguments using JSON Schema validation.
Implement this method to expose your server's tools.
"""
return [
types.Tool(
name="add-note",
description="Add a new note",
inputSchema={
"type": "object",
"properties": {
"name": {"type": "string"},
"content": {"type": "string"},
},
"required": ["name", "content"],
},
)
]
return []

@server.call_tool()
async def handle_call_tool(
name: str, arguments: dict | None
) -> list[types.TextContent | types.ImageContent | types.EmbeddedResource]:
"""
Handle tool execution requests.
Tools can modify server state and notify clients of changes.
Implement this method to handle tool calls with the given name and arguments.
"""
if name != "add-note":
raise ValueError(f"Unknown tool: {name}")

if not arguments:
raise ValueError("Missing arguments")

note_name = arguments.get("name")
content = arguments.get("content")

if not note_name or not content:
raise ValueError("Missing name or content")

# Update server state
notes[note_name] = content

# Notify clients that resources have changed
await server.request_context.session.send_resource_list_changed()

return [
types.TextContent(
type="text",
text=f"Added note '{note_name}' with content: {content}",
)
]
raise NotImplementedError("Tool execution not implemented")

async def main():
# Run the server using stdin/stdout streams
async with mcp.server.stdio.stdio_server() as (read_stream, write_stream):
await server.run(
read_stream,
Expand Down
Loading