Skip to content

Commit c101a50

Browse files
Merge pull request #9 from Agent-Hellboy/issue#7
Refactor codebase and configuration capabilities
2 parents 8da6e51 + 07e9b2d commit c101a50

File tree

12 files changed

+426
-93
lines changed

12 files changed

+426
-93
lines changed

README.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,48 @@ logging.basicConfig(level=logging.DEBUG) # or INFO, WARNING, etc.
8484

8585
This allows you to control the verbosity and destination of log messages from both your code and the framework.
8686

87+
## Middleware Configuration
88+
89+
You can now configure middleware for your MCP server using a dedicated `config.py` file and the `MiddlewareConfig` class. This allows you to easily customize CORS, logging, compression, and add your own middleware.
90+
91+
### Example: config.py
92+
93+
```python
94+
from pymcp.middleware import MiddlewareConfig
95+
96+
middleware_config = MiddlewareConfig(
97+
cors={
98+
"allow_origins": ["https://myapp.com"],
99+
"allow_methods": ["GET", "POST"],
100+
"allow_headers": ["*"],
101+
"allow_credentials": True,
102+
},
103+
logging={
104+
"level": "DEBUG",
105+
"format": "%(asctime)s %(levelname)s %(message)s",
106+
},
107+
compression={
108+
"enabled": True
109+
},
110+
custom=[
111+
# Add custom middleware classes here if needed
112+
]
113+
)
114+
```
115+
116+
### Example: run_server.py
117+
118+
```python
119+
from config import middleware_config
120+
from pymcp.applications import create_app
121+
122+
app = create_app(middleware_config=middleware_config)
123+
```
124+
125+
This approach keeps your configuration clean and separated from your application logic.
126+
127+
For more details and advanced configuration options, see [guide.md](./guide.md).
128+
87129

88130

89131

example/config.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from pymcp.middleware import MiddlewareConfig
2+
3+
middleware_config = MiddlewareConfig(
4+
cors={
5+
"allow_origins": ["https://myapp.com"],
6+
"allow_methods": ["GET", "POST"],
7+
"allow_headers": ["*"],
8+
"allow_credentials": True,
9+
},
10+
logging={
11+
"level": "DEBUG",
12+
"format": "%(asctime)s %(levelname)s %(message)s",
13+
},
14+
compression={"enabled": True},
15+
custom=[
16+
# Add custom middleware classes here if needed
17+
],
18+
)

example/run_server.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import logging
22

3+
from config import middleware_config
4+
from pymcp.applications import create_app
35
from pymcp.registry import tool_registry
4-
from pymcp.server import app
56

67
logging.basicConfig(level=logging.DEBUG)
78

9+
810
@tool_registry.register
911
def addNumbersTool(a: float, b: float) -> str:
1012
"""Adds two numbers 'a' and 'b' and returns their sum."""
@@ -38,7 +40,8 @@ def promptEchoTool(prompt: str) -> str:
3840
return f"You said: {prompt}"
3941

4042

41-
4243
if __name__ == "__main__":
4344
import uvicorn
45+
46+
app = create_app(middleware_config=middleware_config)
4447
uvicorn.run(app, host="0.0.0.0", port=8088)

example/web-based/run_mcp_with_flask_call.py renamed to example/web-based/run_mcp_with_api_call.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import requests
22

3+
from pymcp.applications import create_app
34
from pymcp.registry import tool_registry
4-
from pymcp.server import app
55

66

77
@tool_registry.register
@@ -22,4 +22,4 @@ def callFlaskHelloTool() -> str:
2222
print(
2323
"[INFO] Make sure to start the Flask API server (flask_api_server.py) before running this example."
2424
)
25-
uvicorn.run(app, host="0.0.0.0", port=8088)
25+
uvicorn.run(create_app(middleware_config=None), host="0.0.0.0", port=8088)

guide.md

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
# Framework Middleware Configuration Guide
2+
3+
This guide explains how to configure middleware in your framework using the provided `MiddlewareConfig` class or via keyword arguments.
4+
5+
## Default Middleware
6+
7+
The framework includes the following middleware by default:
8+
9+
- **CORS**: Allows cross-origin requests (default: all origins, all methods, all headers).
10+
- **Logging**: Basic request/response logging (default: INFO level).
11+
- **Error Handling**: Basic error handling to prevent leaking internal details.
12+
13+
Optional middleware you can enable/configure:
14+
- **Compression**: GZip compression for responses.
15+
- **Custom Middleware**: Add your own FastAPI-compatible middleware.
16+
17+
---
18+
19+
## Configuration Methods
20+
21+
You can configure middleware in two ways:
22+
23+
### 1. Using `MiddlewareConfig`
24+
25+
```python
26+
from pymcp.server import create_app, MiddlewareConfig
27+
28+
config = MiddlewareConfig(
29+
cors={
30+
"allow_origins": ["https://myapp.com"],
31+
"allow_methods": ["GET", "POST"],
32+
"allow_headers": ["*"],
33+
"allow_credentials": True,
34+
},
35+
logging={
36+
"level": "DEBUG",
37+
"format": "%(asctime)s %(levelname)s %(message)s",
38+
},
39+
error_handling={
40+
# Add custom error handlers if needed
41+
},
42+
compression={
43+
"enabled": True
44+
},
45+
custom=[
46+
# List of custom middleware classes
47+
]
48+
)
49+
50+
app = create_app(middleware_config=config)
51+
```
52+
53+
### 2. Using Keyword Arguments
54+
55+
```python
56+
from pymcp.server import create_app
57+
58+
app = create_app(
59+
cors={
60+
"allow_origins": ["https://myapp.com"],
61+
"allow_methods": ["GET", "POST"],
62+
"allow_headers": ["*"],
63+
"allow_credentials": True,
64+
},
65+
logging={
66+
"level": "DEBUG",
67+
"format": "%(asctime)s %(levelname)s %(message)s",
68+
},
69+
compression={
70+
"enabled": True
71+
},
72+
custom=[
73+
# List of custom middleware classes
74+
]
75+
)
76+
```
77+
78+
---
79+
80+
## Middleware Options
81+
82+
### CORS
83+
- `allow_origins`: List of allowed origins (default: `["*"]`)
84+
- `allow_methods`: List of allowed HTTP methods (default: `["*"]`)
85+
- `allow_headers`: List of allowed headers (default: `["*"]`)
86+
- `allow_credentials`: Allow credentials (default: `True`)
87+
88+
### Logging
89+
- `level`: Log level (default: `"INFO"`)
90+
- `format`: Log format string
91+
92+
### Error Handling
93+
- `custom_handler`: (Optional) Your custom error handler function
94+
95+
### Compression
96+
- `enabled`: Enable GZip compression (default: `False`)
97+
98+
### Custom Middleware
99+
- `custom`: List of FastAPI-compatible middleware classes to add
100+
101+
---
102+
103+
## Example: Adding Custom Middleware
104+
105+
```python
106+
from starlette.middleware.base import BaseHTTPMiddleware
107+
108+
class MyCustomMiddleware(BaseHTTPMiddleware):
109+
async def dispatch(self, request, call_next):
110+
# Custom logic here
111+
response = await call_next(request)
112+
return response
113+
114+
config = MiddlewareConfig(custom=[MyCustomMiddleware])
115+
app = create_app(middleware_config=config)
116+
```
117+
118+
---
119+
120+
## Best Practices
121+
- Restrict CORS origins in production.
122+
- Use appropriate log levels for your environment.
123+
- Add custom error handlers for user-friendly error messages.
124+
- Enable compression for large responses or slow networks.
125+
- Add custom middleware for authentication, metrics, etc.
126+
127+
---
128+
129+
## Recommended Usage: config.py
130+
131+
For best practice, create a `config.py` file in your project root to define your middleware configuration. Then, import this config in your server entry point (e.g., `run_server.py`).
132+
133+
### Example: config.py
134+
135+
```python
136+
137+
from pymcp.applications import create_app
138+
from pymcp.middleware import MiddlewareConfig
139+
140+
middleware_config = MiddlewareConfig(
141+
cors={
142+
"allow_origins": ["https://myapp.com"],
143+
"allow_methods": ["GET", "POST"],
144+
"allow_headers": ["*"],
145+
"allow_credentials": True,
146+
},
147+
logging={
148+
"level": "DEBUG",
149+
"format": "%(asctime)s %(levelname)s %(message)s",
150+
},
151+
compression={
152+
"enabled": True
153+
},
154+
custom=[
155+
# Add custom middleware classes here if needed
156+
]
157+
)
158+
```
159+
160+
### Example: run_server.py
161+
162+
```python
163+
from config import middleware_config
164+
from pymcp.server import create_app
165+
166+
app = create_app(middleware_config=middleware_config)
167+
```
168+
169+
This approach keeps your configuration clean and separated from your application logic.
170+
171+
For more details, see the framework documentation or contact the maintainers.

pymcp/applications.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
"""
2+
App factory for the MCP framework.
3+
Provides the create_app function to instantiate and configure the FastAPI app.
4+
"""
5+
6+
from typing import Optional
7+
8+
from fastapi import FastAPI
9+
10+
from .middleware import MiddlewareConfig, setup_middleware
11+
from .server import router
12+
13+
14+
def create_app(middleware_config: Optional[MiddlewareConfig] = None, **kwargs):
15+
app = FastAPI()
16+
config = middleware_config or MiddlewareConfig(
17+
cors=kwargs.get("cors"),
18+
logging=kwargs.get("logging"),
19+
error_handling=kwargs.get("error_handling"),
20+
compression=kwargs.get("compression"),
21+
custom=kwargs.get("custom"),
22+
)
23+
setup_middleware(app, config)
24+
app.include_router(router)
25+
return app

pymcp/middleware.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
"""
2+
Middleware configuration and setup for the MCP framework.
3+
"""
4+
5+
from typing import Any, Dict, Optional
6+
7+
from fastapi.middleware.cors import CORSMiddleware
8+
from fastapi.middleware.gzip import GZipMiddleware
9+
10+
11+
class MiddlewareConfig:
12+
"""
13+
Configuration class for setting up middleware in the MCP server.
14+
"""
15+
16+
def __init__(
17+
self,
18+
cors: Optional[Dict[str, Any]] = None,
19+
logging: Optional[Dict[str, Any]] = None,
20+
error_handling: Optional[Dict[str, Any]] = None,
21+
compression: Optional[Dict[str, Any]] = None,
22+
custom: Optional[list] = None,
23+
):
24+
self.cors = cors or {
25+
"allow_origins": ["*"],
26+
"allow_credentials": True,
27+
"allow_methods": ["*"],
28+
"allow_headers": ["*"],
29+
}
30+
self.logging = logging or {
31+
"level": "INFO",
32+
"format": "%(asctime)s %(levelname)s %(message)s",
33+
}
34+
self.error_handling = error_handling or {}
35+
self.compression = compression or {"enabled": False}
36+
self.custom = custom or []
37+
38+
39+
def setup_middleware(app, config: MiddlewareConfig):
40+
"""
41+
Apply middleware to the FastAPI app based on the provided MiddlewareConfig.
42+
"""
43+
# CORS
44+
app.add_middleware(
45+
CORSMiddleware,
46+
allow_origins=config.cors["allow_origins"],
47+
allow_credentials=config.cors["allow_credentials"],
48+
allow_methods=config.cors["allow_methods"],
49+
allow_headers=config.cors["allow_headers"],
50+
)
51+
52+
# Compression
53+
if config.compression.get("enabled", False):
54+
app.add_middleware(GZipMiddleware)
55+
# Custom middleware
56+
for mw in config.custom:
57+
if not callable(mw):
58+
raise ValueError(f"Custom middleware {mw} is not callable")
59+
app.add_middleware(mw)
60+
# Error handling can be added here as needed

0 commit comments

Comments
 (0)