chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 13:31:35 +08:00
commit c275ba2868
13613 changed files with 2980806 additions and 0 deletions
@@ -0,0 +1,37 @@
# Run sample
This sample starts an MCP Server with a middleware that checks for a valid Authorization header.
## Install dependencies
```bash
pip install "mcp[cli]"
```
## Start server
```bash
python server.py
```
start the client in another terminal
```bash
python client.py
```
You should see a result similar to:
```text
2025-09-30 13:25:54 - mcp_client - INFO - Tool result: meta=None content=[TextContent(type='text', text='{\n "current_time": "2025-09-30T13:25:54.311900",\n "timezone": "UTC",\n "timestamp": 1759238754.3119,\n "formatted": "2025-09-30 13:25:54"\n}', annotations=None, meta=None)] structuredContent={'current_time': '2025-09-30T13:25:54.311900', 'timezone': 'UTC', 'timestamp': 1759238754.3119, 'formatted': '2025-09-30 13:25:54'} isError=False
```
this means the credential being sent through is being allowed.
Try changing the credential in `client.py` to "secret-token2", then you should see this text as part of the response:
```text
2025-09-30 13:27:44 - httpx - INFO - HTTP Request: POST http://localhost:8000/mcp "HTTP/1.1 403 Forbidden"
```
this means you were authenticated (you had a credential), but it was invalid.
@@ -0,0 +1,97 @@
# client.py
from mcp.client.streamable_http import streamablehttp_client
from mcp import ClientSession
import asyncio
import mcp.types as types
from mcp.shared.session import RequestResponder
import requests
import logging
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
logger = logging.getLogger('mcp_client')
class LoggingCollector:
def __init__(self):
self.log_messages: list[types.LoggingMessageNotificationParams] = []
async def __call__(self, params: types.LoggingMessageNotificationParams) -> None:
self.log_messages.append(params)
logger.info("MCP Log: %s - %s", params.level, params.data)
logging_collector = LoggingCollector()
port = 8000
async def message_handler(
message: RequestResponder[types.ServerRequest, types.ClientResult]
| types.ServerNotification
| Exception,
) -> None:
logger.info("Received message: %s", message)
if isinstance(message, Exception):
logger.error("Exception received!")
raise message
elif isinstance(message, types.ServerNotification):
logger.info("NOTIFICATION: %s", message)
elif isinstance(message, RequestResponder):
logger.info("REQUEST_RESPONDER: %s", message)
else:
logger.info("SERVER_MESSAGE: %s", message)
async def main():
logger.info("Starting client...")
async with streamablehttp_client(
url = f"http://localhost:{port}/mcp",
headers = {"Authorization": "Bearer secret-token2"}
) as (
read_stream,
write_stream,
session_callback,
):
async with ClientSession(
read_stream,
write_stream,
logging_callback=logging_collector,
message_handler=message_handler
) as session:
id_before = session_callback()
logger.info("Session ID before init: %s", id_before)
await session.initialize()
id_after = session_callback()
logger.info("Session ID after init: %s", id_after)
logger.info("Session initialized, ready to call tools.")
tool_result = await session.call_tool("get_time", {})
logger.info("Tool result: %s", tool_result)
if logging_collector.log_messages:
logger.info("Collected log messages:")
for log in logging_collector.log_messages:
logger.info("Log: %s", log)
def stream_progress(message="hello", url="http://localhost:8000/stream"):
params = {"message": message}
logger.info("Connecting to %s with message: %s", url, message)
try:
with requests.get(url, params=params, stream=True, timeout=10) as r:
r.raise_for_status()
logger.info("--- Streaming Progress ---")
for line in r.iter_lines():
if line:
# Still print the streamed content to stdout for visibility
decoded_line = line.decode().strip()
print(decoded_line)
logger.debug("Stream content: %s", decoded_line)
logger.info("--- Stream Ended ---")
except requests.RequestException as e:
logger.error("Error during streaming: %s", e)
if __name__ == "__main__":
import sys
logger.info("Running MCP client...")
asyncio.run(main())
# Don't run both by default, let the user choose the mode
@@ -0,0 +1,102 @@
from pydantic import AnyHttpUrl
from pydantic_settings import BaseSettings, SettingsConfigDict
from mcp.server.auth.settings import AuthSettings
from mcp.server.fastmcp.server import FastMCP
from typing import Any, Literal
from starlette.middleware import Middleware
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.responses import Response
from starlette.applications import Starlette
from starlette.routing import Mount
import asyncio
import datetime
settings = {
"host": "localhost",
"port": 8000,
"auth_server_url": AnyHttpUrl("http://localhost:8001"),
"mcp_scope": "mcp:read",
"server_url": AnyHttpUrl("http://localhost:8000"),
}
def valid_token(token: str) -> bool:
# remove the "Bearer " prefix
if token.startswith("Bearer "):
token = token[7:]
return token == "secret-token"
return False
class CustomHeaderMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request, call_next):
has_header = request.headers.get("Authorization")
if not has_header:
print("-> Missing Authorization header!")
return Response(status_code=401, content="Unauthorized")
if not valid_token(has_header):
print("-> Invalid token!")
return Response(status_code=403, content="Forbidden")
print("Valid token, proceeding...")
print(f"-> Received {request.method} {request.url}")
response = await call_next(request)
response.headers['Custom'] = 'Example'
return response
app = FastMCP(
name="MCP Resource Server",
instructions="Resource Server that validates tokens via Authorization Server introspection",
host=settings["host"],
port=settings["port"],
debug=True
)
@app.tool()
async def get_time() -> dict[str, Any]:
"""
Get the current server time.
This tool demonstrates that system information can be protected
by OAuth authentication. User must be authenticated to access it.
"""
now = datetime.datetime.now()
return {
"current_time": now.isoformat(),
"timezone": "UTC", # Simplified for demo
"timestamp": now.timestamp(),
"formatted": now.strftime("%Y-%m-%d %H:%M:%S"),
}
async def setup(app) -> None:
"""Run the server using StreamableHTTP transport."""
starlette_app = app.streamable_http_app()
return starlette_app
async def run(starlette_app):
import uvicorn
config = uvicorn.Config(
starlette_app,
host=app.settings.host,
port=app.settings.port,
log_level=app.settings.log_level.lower(),
)
server = uvicorn.Server(config)
await server.serve()
async def main():
print("Running MCP Resource Server...")
starlette_app = await setup(app)
print("Adding custom middleware...")
starlette_app.add_middleware(CustomHeaderMiddleware)
await run(starlette_app)
if __name__ == "__main__":
asyncio.run(main())