Skip to content

Commit 84fecf2

Browse files
committed
Add example
1 parent c9f31d5 commit 84fecf2

File tree

5 files changed

+1127
-0
lines changed

5 files changed

+1127
-0
lines changed
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# Simple Streamable Private Gateway Example
2+
3+
A demonstration of how to use the MCP Python SDK as a streamable private gateway without authentication over streamable HTTP or SSE transport.
4+
5+
## Features
6+
7+
- No authentication required
8+
- Support StreamableHTTP
9+
- Interactive command-line interface
10+
- Tool calling
11+
12+
## Installation
13+
14+
```bash
15+
cd examples/clients/simple-streamable-private-gateway
16+
uv sync --reinstall
17+
```
18+
19+
## Usage
20+
21+
### 1. Start an MCP server without authentication
22+
23+
You can use any MCP server that doesn't require authentication. For example:
24+
25+
```bash
26+
# Example with a simple tool server
27+
cd examples/servers/simple-tool
28+
uv run mcp-simple-tool --transport streamable-http --port 8000
29+
30+
# Or use any of the other example servers
31+
cd examples/servers/simple-resource
32+
uv run simple-resource --transport streamable-http --port 8000
33+
```
34+
35+
### 2. Run the client
36+
37+
```bash
38+
uv run mcp-simple-streamable-private-gateway
39+
40+
# Or with custom server port
41+
MCP_SERVER_PORT=8000 uv run mcp-simple-streamable-private-gateway
42+
```
43+
44+
### 3. Use the interactive interface
45+
46+
The client provides several commands:
47+
48+
- `list` - List available tools
49+
- `call <tool_name> [args]` - Call a tool with optional JSON arguments
50+
- `quit` - Exit
51+
52+
## Examples
53+
54+
### Basic tool usage
55+
56+
```
57+
🚀 Simple Streamable Private Gateway
58+
Connecting to: https://localhost:8000/mcp
59+
📡 Opening StreamableHTTP transport connection...
60+
🤝 Initializing MCP session...
61+
⚡ Starting session initialization...
62+
✨ Session initialization complete!
63+
64+
✅ Connected to MCP server at https://localhost:8000/mcp
65+
66+
🎯 Interactive MCP Client
67+
Commands:
68+
list - List available tools
69+
call <tool_name> [args] - Call a tool
70+
quit - Exit the client
71+
72+
mcp> list
73+
📋 Available tools:
74+
1. echo
75+
Description: Echo back the input text
76+
77+
mcp> call echo {"text": "Hello, world!"}
78+
🔧 Tool 'echo' result:
79+
Hello, world!
80+
81+
mcp> quit
82+
👋 Goodbye!
83+
```
84+
85+
## Configuration
86+
87+
- `MCP_SERVER_PORT` - Server port (default: 8000)
88+
89+
## Compatible Servers
90+
91+
This client works with any MCP server that doesn't require authentication, including:
92+
93+
- `examples/servers/simple-tool` - Basic tool server
94+
- `examples/servers/simple-resource` - Resource server
95+
- `examples/servers/simple-prompt` - Prompt server
96+
- Any custom MCP server without auth requirements
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"""Simple MCP streamable private gateway client example without authentication."""
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Simple MCP streamable private gateway client example without authentication.
4+
5+
This client connects to an MCP server using streamable HTTP or SSE transport.
6+
7+
"""
8+
9+
import asyncio
10+
import os
11+
from datetime import timedelta
12+
from typing import Any
13+
14+
from mcp.client.session import ClientSession
15+
from mcp.client.streamable_http import streamablehttp_client
16+
17+
18+
class SimpleStreamablePrivateGateway:
19+
"""Simple MCP streamable private gateway client without authentication."""
20+
21+
def __init__(self, server_url: str, transport_type: str = "streamable-http"):
22+
self.server_url = server_url
23+
self.transport_type = transport_type
24+
self.session: ClientSession | None = None
25+
26+
async def connect(self):
27+
"""Connect to the MCP server."""
28+
print(f"🔗 Attempting to connect to {self.server_url}...")
29+
30+
try:
31+
print("📡 Opening StreamableHTTP transport connection...")
32+
async with streamablehttp_client(
33+
url=self.server_url,
34+
headers={"Host": "mcp.deepwiki.com"},
35+
extensions={"sni_hostname": "mcp.deepwiki.com"},
36+
timeout=timedelta(seconds=60),
37+
) as (read_stream, write_stream, get_session_id):
38+
await self._run_session(read_stream, write_stream, get_session_id)
39+
40+
except Exception as e:
41+
print(f"❌ Failed to connect: {e}")
42+
import traceback
43+
44+
traceback.print_exc()
45+
46+
async def _run_session(self, read_stream, write_stream, get_session_id):
47+
"""Run the MCP session with the given streams."""
48+
print("🤝 Initializing MCP session...")
49+
async with ClientSession(read_stream, write_stream) as session:
50+
self.session = session
51+
print("⚡ Starting session initialization...")
52+
await session.initialize()
53+
print("✨ Session initialization complete!")
54+
55+
print(f"\n✅ Connected to MCP server at {self.server_url}")
56+
if get_session_id:
57+
session_id = get_session_id()
58+
if session_id:
59+
print(f"Session ID: {session_id}")
60+
61+
# Run interactive loop
62+
await self.interactive_loop()
63+
64+
async def list_tools(self):
65+
"""List available tools from the server."""
66+
if not self.session:
67+
print("❌ Not connected to server")
68+
return
69+
70+
try:
71+
result = await self.session.list_tools()
72+
if hasattr(result, "tools") and result.tools:
73+
print("\n📋 Available tools:")
74+
for i, tool in enumerate(result.tools, 1):
75+
print(f"{i}. {tool.name}")
76+
if tool.description:
77+
print(f" Description: {tool.description}")
78+
print()
79+
else:
80+
print("No tools available")
81+
except Exception as e:
82+
print(f"❌ Failed to list tools: {e}")
83+
84+
async def call_tool(self, tool_name: str, arguments: dict[str, Any] | None = None):
85+
"""Call a specific tool."""
86+
if not self.session:
87+
print("❌ Not connected to server")
88+
return
89+
90+
try:
91+
result = await self.session.call_tool(tool_name, arguments or {})
92+
print(f"\n🔧 Tool '{tool_name}' result:")
93+
if hasattr(result, "content"):
94+
for content in result.content:
95+
if content.type == "text":
96+
print(content.text)
97+
else:
98+
print(content)
99+
else:
100+
print(result)
101+
except Exception as e:
102+
print(f"❌ Failed to call tool '{tool_name}': {e}")
103+
104+
async def interactive_loop(self):
105+
"""Run interactive command loop."""
106+
print("\n🎯 Interactive Streamable Private Gateway")
107+
print("Commands:")
108+
print(" list - List available tools")
109+
print(" call <tool_name> [args] - Call a tool")
110+
print(" quit - Exit the client")
111+
print()
112+
113+
while True:
114+
try:
115+
command = input("mcp> ").strip()
116+
117+
if not command:
118+
continue
119+
120+
if command == "quit":
121+
break
122+
123+
elif command == "list":
124+
await self.list_tools()
125+
126+
elif command.startswith("call "):
127+
parts = command.split(maxsplit=2)
128+
tool_name = parts[1] if len(parts) > 1 else ""
129+
130+
if not tool_name:
131+
print("❌ Please specify a tool name")
132+
continue
133+
134+
# Parse arguments (simple JSON-like format)
135+
arguments = {}
136+
if len(parts) > 2:
137+
import json
138+
139+
try:
140+
arguments = json.loads(parts[2])
141+
except json.JSONDecodeError:
142+
print("❌ Invalid arguments format (expected JSON)")
143+
continue
144+
145+
await self.call_tool(tool_name, arguments)
146+
147+
else:
148+
print("❌ Unknown command. Try 'list', 'call <tool_name>', or 'quit'")
149+
150+
except KeyboardInterrupt:
151+
print("\n\n👋 Goodbye!")
152+
break
153+
except EOFError:
154+
break
155+
156+
157+
async def main():
158+
"""Main entry point."""
159+
# Default server URL - can be overridden with environment variable
160+
# Most MCP streamable HTTP servers use /mcp as the endpoint
161+
server_port = os.getenv("MCP_SERVER_PORT", "8000")
162+
transport_type = "streamable-http"
163+
server_url = f"https://localhost:{server_port}/mcp"
164+
165+
print("🚀 Simple Streamable Private Gateway")
166+
print(f"Connecting to: {server_url}")
167+
print(f"Transport type: {transport_type}")
168+
169+
# Start connection flow
170+
client = SimpleStreamablePrivateGateway(server_url, transport_type)
171+
await client.connect()
172+
173+
174+
def cli():
175+
"""CLI entry point for uv script."""
176+
asyncio.run(main())
177+
178+
179+
if __name__ == "__main__":
180+
cli()
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
[project]
2+
name = "mcp-simple-streamable-private-gateway"
3+
version = "0.1.0"
4+
description = "A simple streamable private gateway client for MCP servers without authentication"
5+
readme = "README.md"
6+
requires-python = ">=3.10"
7+
authors = [{ name = "Anthropic" }]
8+
keywords = ["mcp", "client", "streamable", "private", "gateway"]
9+
license = { text = "MIT" }
10+
classifiers = [
11+
"Development Status :: 4 - Beta",
12+
"Intended Audience :: Developers",
13+
"License :: OSI Approved :: MIT License",
14+
"Programming Language :: Python :: 3",
15+
"Programming Language :: Python :: 3.10",
16+
]
17+
dependencies = [
18+
"click>=8.2.0",
19+
"mcp>=1.0.0",
20+
]
21+
22+
[project.scripts]
23+
mcp-simple-streamable-private-gateway = "mcp_simple_streamable_private_gateway.main:cli"
24+
25+
[build-system]
26+
requires = ["hatchling"]
27+
build-backend = "hatchling.build"
28+
29+
[tool.hatch.build.targets.wheel]
30+
packages = ["mcp_simple_streamable_private_gateway"]
31+
32+
[tool.pyright]
33+
include = ["mcp_simple_streamable_private_gateway"]
34+
venvPath = "."
35+
venv = ".venv"
36+
37+
[tool.ruff.lint]
38+
select = ["E", "F", "I"]
39+
ignore = []
40+
41+
[tool.ruff]
42+
line-length = 120
43+
target-version = "py310"
44+
45+
[tool.uv]
46+
dev-dependencies = ["pyright>=1.1.379", "pytest>=8.3.3", "ruff>=0.6.9"]
47+
48+
[tool.uv.sources]
49+
mcp = { path = "../../../" }
50+
51+
[[tool.uv.index]]
52+
url = "https://pypi.org/simple"

0 commit comments

Comments
 (0)