Skip to content

Commit b1d41bb

Browse files
authored
Merge pull request #48 from modelcontextprotocol/davidsp/example
Example server to expose a fetch tool
2 parents 3d670a3 + 10881fb commit b1d41bb

File tree

17 files changed

+1339
-8
lines changed

17 files changed

+1339
-8
lines changed

examples/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Python SDK Examples
2+
3+
This folders aims to provide simple examples of using the Python SDK. Please refer to the
4+
[example-servers repository](https://github.com/modelcontextprotocol/example-servers)
5+
for real-world servers.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3.11
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# MCP Simple Prompt
2+
3+
A simple MCP server that exposes a customizable prompt template with optional context and topic parameters.
4+
5+
## Usage
6+
7+
Start the server using either stdio (default) or SSE transport:
8+
9+
```bash
10+
# Using stdio transport (default)
11+
mcp-simple-prompt
12+
13+
# Using SSE transport on custom port
14+
mcp-simple-prompt --transport sse --port 8000
15+
```
16+
17+
The server exposes a prompt named "simple" that accepts two optional arguments:
18+
19+
- `context`: Additional context to consider
20+
- `topic`: Specific topic to focus on
21+
22+
## Example
23+
24+
Using the MCP client, you can retrieve the prompt like this:
25+
26+
```python
27+
from mcp.client import ClientSession
28+
29+
async with ClientSession() as session:
30+
await session.initialize()
31+
32+
# List available prompts
33+
prompts = await session.list_prompts()
34+
print(prompts)
35+
36+
# Get the prompt with arguments
37+
prompt = await session.get_prompt("simple", {
38+
"context": "User is a software developer",
39+
"topic": "Python async programming"
40+
})
41+
print(prompt)
42+
```
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import sys
2+
3+
from server import main
4+
5+
sys.exit(main())
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import anyio
2+
import click
3+
import mcp.types as types
4+
from mcp.server import Server
5+
6+
7+
@click.group()
8+
def cli():
9+
pass
10+
11+
12+
@cli.command()
13+
@click.option("--port", default=8000, help="Port to listen on for SSE")
14+
@click.option(
15+
"--transport",
16+
type=click.Choice(["stdio", "sse"]),
17+
default="stdio",
18+
help="Transport type",
19+
)
20+
def main(port: int, transport: str) -> int:
21+
return anyio.run(_amain, port, transport)
22+
23+
24+
def create_messages(
25+
context: str | None = None, topic: str | None = None
26+
) -> list[types.PromptMessage]:
27+
"""Create the messages for the prompt."""
28+
messages = []
29+
30+
# Add context if provided
31+
if context:
32+
messages.append(
33+
types.PromptMessage(
34+
role="user",
35+
content=types.TextContent(
36+
type="text", text=f"Here is some relevant context: {context}"
37+
),
38+
)
39+
)
40+
41+
# Add the main prompt
42+
prompt = "Please help me with "
43+
if topic:
44+
prompt += f"the following topic: {topic}"
45+
else:
46+
prompt += "whatever questions I may have."
47+
48+
messages.append(
49+
types.PromptMessage(
50+
role="user", content=types.TextContent(type="text", text=prompt)
51+
)
52+
)
53+
54+
return messages
55+
56+
57+
async def _amain(port: int, transport: str) -> int:
58+
app = Server("mcp-simple-prompt")
59+
60+
@app.list_prompts()
61+
async def list_prompts() -> list[types.Prompt]:
62+
return [
63+
types.Prompt(
64+
name="simple",
65+
description="A simple prompt that can take optional context and topic "
66+
"arguments",
67+
arguments=[
68+
types.PromptArgument(
69+
name="context",
70+
description="Additional context to consider",
71+
required=False,
72+
),
73+
types.PromptArgument(
74+
name="topic",
75+
description="Specific topic to focus on",
76+
required=False,
77+
),
78+
],
79+
)
80+
]
81+
82+
@app.get_prompt()
83+
async def get_prompt(
84+
name: str, arguments: dict[str, str] | None = None
85+
) -> types.GetPromptResult:
86+
if name != "simple":
87+
raise ValueError(f"Unknown prompt: {name}")
88+
89+
if arguments is None:
90+
arguments = {}
91+
92+
return types.GetPromptResult(
93+
messages=create_messages(
94+
context=arguments.get("context"), topic=arguments.get("topic")
95+
),
96+
description="A simple prompt with optional context and topic arguments",
97+
)
98+
99+
if transport == "sse":
100+
from mcp.server.sse import SseServerTransport
101+
from starlette.applications import Starlette
102+
from starlette.routing import Route
103+
104+
sse = SseServerTransport("/messages")
105+
106+
async def handle_sse(scope, receive, send):
107+
async with sse.connect_sse(scope, receive, send) as streams:
108+
await app.run(
109+
streams[0], streams[1], app.create_initialization_options()
110+
)
111+
112+
async def handle_messages(scope, receive, send):
113+
await sse.handle_post_message(scope, receive, send)
114+
115+
starlette_app = Starlette(
116+
debug=True,
117+
routes=[
118+
Route("/sse", endpoint=handle_sse),
119+
Route("/messages", endpoint=handle_messages, methods=["POST"]),
120+
],
121+
)
122+
123+
import uvicorn
124+
125+
uvicorn.run(starlette_app, host="0.0.0.0", port=port)
126+
else:
127+
from mcp.server.stdio import stdio_server
128+
129+
async with stdio_server() as streams:
130+
await app.run(streams[0], streams[1], app.create_initialization_options())
131+
132+
return 0
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
[project]
2+
name = "mcp-simple-prompt"
3+
version = "0.1.0"
4+
description = "A simple MCP server exposing a customizable prompt"
5+
readme = "README.md"
6+
requires-python = ">=3.10"
7+
authors = [{ name = "Anthropic, PBC." }]
8+
maintainers = [
9+
{ name = "David Soria Parra", email = "davidsp@anthropic.com" },
10+
{ name = "Justin Spahr-Summers", email = "justin@anthropic.com" },
11+
]
12+
keywords = ["mcp", "llm", "automation", "web", "fetch"]
13+
license = { text = "MIT" }
14+
classifiers = [
15+
"Development Status :: 4 - Beta",
16+
"Intended Audience :: Developers",
17+
"License :: OSI Approved :: MIT License",
18+
"Programming Language :: Python :: 3",
19+
"Programming Language :: Python :: 3.10",
20+
]
21+
dependencies = ["anyio>=4.6.2.post1", "click>=8.1.7", "httpx>=0.27.2", "mcp"]
22+
23+
[project.scripts]
24+
mcp-simple-prompt = "mcp_simple_prompt.server:main"
25+
26+
[build-system]
27+
requires = ["hatchling"]
28+
build-backend = "hatchling.build"
29+
30+
[tool.hatch.build.targets.wheel]
31+
packages = ["mcp_simple_prompt"]
32+
33+
[tool.pyright]
34+
include = ["mcp_simple_prompt"]
35+
venvPath = "."
36+
venv = ".venv"
37+
38+
[tool.ruff.lint]
39+
select = ["E", "F", "I"]
40+
ignore = []
41+
42+
[tool.ruff]
43+
line-length = 88
44+
target-version = "py310"
45+
46+
[tool.uv]
47+
dev-dependencies = ["pyright>=1.1.378", "pytest>=8.3.3", "ruff>=0.6.9"]

0 commit comments

Comments
 (0)