mcp-craft 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mcp_craft-0.1.0/.gitignore +218 -0
- mcp_craft-0.1.0/LICENSE +21 -0
- mcp_craft-0.1.0/PKG-INFO +220 -0
- mcp_craft-0.1.0/README.md +189 -0
- mcp_craft-0.1.0/example.py +63 -0
- mcp_craft-0.1.0/initial_setup.md +446 -0
- mcp_craft-0.1.0/pyproject.toml +56 -0
- mcp_craft-0.1.0/src/mcp_craft/__init__.py +36 -0
- mcp_craft-0.1.0/src/mcp_craft/application.py +345 -0
- mcp_craft-0.1.0/src/mcp_craft/authentication.py +69 -0
- mcp_craft-0.1.0/src/mcp_craft/config.py +10 -0
- mcp_craft-0.1.0/src/mcp_craft/context.py +35 -0
- mcp_craft-0.1.0/src/mcp_craft/decorators.py +62 -0
- mcp_craft-0.1.0/src/mcp_craft/dependency.py +58 -0
- mcp_craft-0.1.0/src/mcp_craft/integrations/__init__.py +1 -0
- mcp_craft-0.1.0/src/mcp_craft/integrations/django.py +15 -0
- mcp_craft-0.1.0/src/mcp_craft/integrations/fastapi.py +25 -0
- mcp_craft-0.1.0/src/mcp_craft/integrations/flask.py +22 -0
- mcp_craft-0.1.0/src/mcp_craft/middleware.py +174 -0
- mcp_craft-0.1.0/src/mcp_craft/plugins/__init__.py +14 -0
- mcp_craft-0.1.0/src/mcp_craft/testing/__init__.py +56 -0
- mcp_craft-0.1.0/tests/test_mcp_craft.py +289 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[codz]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py.cover
|
|
50
|
+
.hypothesis/
|
|
51
|
+
.pytest_cache/
|
|
52
|
+
cover/
|
|
53
|
+
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
*.pot
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
local_settings.py
|
|
61
|
+
db.sqlite3
|
|
62
|
+
db.sqlite3-journal
|
|
63
|
+
|
|
64
|
+
# Flask stuff:
|
|
65
|
+
instance/
|
|
66
|
+
.webassets-cache
|
|
67
|
+
|
|
68
|
+
# Scrapy stuff:
|
|
69
|
+
.scrapy
|
|
70
|
+
|
|
71
|
+
# Sphinx documentation
|
|
72
|
+
docs/_build/
|
|
73
|
+
|
|
74
|
+
# PyBuilder
|
|
75
|
+
.pybuilder/
|
|
76
|
+
target/
|
|
77
|
+
|
|
78
|
+
# Jupyter Notebook
|
|
79
|
+
.ipynb_checkpoints
|
|
80
|
+
|
|
81
|
+
# IPython
|
|
82
|
+
profile_default/
|
|
83
|
+
ipython_config.py
|
|
84
|
+
|
|
85
|
+
# pyenv
|
|
86
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
87
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
88
|
+
# .python-version
|
|
89
|
+
|
|
90
|
+
# pipenv
|
|
91
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
92
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
93
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
94
|
+
# install all needed dependencies.
|
|
95
|
+
# Pipfile.lock
|
|
96
|
+
|
|
97
|
+
# UV
|
|
98
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
99
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
100
|
+
# commonly ignored for libraries.
|
|
101
|
+
# uv.lock
|
|
102
|
+
|
|
103
|
+
# poetry
|
|
104
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
105
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
106
|
+
# commonly ignored for libraries.
|
|
107
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
108
|
+
# poetry.lock
|
|
109
|
+
# poetry.toml
|
|
110
|
+
|
|
111
|
+
# pdm
|
|
112
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
113
|
+
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
|
114
|
+
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
|
115
|
+
# pdm.lock
|
|
116
|
+
# pdm.toml
|
|
117
|
+
.pdm-python
|
|
118
|
+
.pdm-build/
|
|
119
|
+
|
|
120
|
+
# pixi
|
|
121
|
+
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
|
122
|
+
# pixi.lock
|
|
123
|
+
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
|
124
|
+
# in the .venv directory. It is recommended not to include this directory in version control.
|
|
125
|
+
.pixi
|
|
126
|
+
|
|
127
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
128
|
+
__pypackages__/
|
|
129
|
+
|
|
130
|
+
# Celery stuff
|
|
131
|
+
celerybeat-schedule
|
|
132
|
+
celerybeat.pid
|
|
133
|
+
|
|
134
|
+
# Redis
|
|
135
|
+
*.rdb
|
|
136
|
+
*.aof
|
|
137
|
+
*.pid
|
|
138
|
+
|
|
139
|
+
# RabbitMQ
|
|
140
|
+
mnesia/
|
|
141
|
+
rabbitmq/
|
|
142
|
+
rabbitmq-data/
|
|
143
|
+
|
|
144
|
+
# ActiveMQ
|
|
145
|
+
activemq-data/
|
|
146
|
+
|
|
147
|
+
# SageMath parsed files
|
|
148
|
+
*.sage.py
|
|
149
|
+
|
|
150
|
+
# Environments
|
|
151
|
+
.env
|
|
152
|
+
.envrc
|
|
153
|
+
.venv
|
|
154
|
+
env/
|
|
155
|
+
venv/
|
|
156
|
+
ENV/
|
|
157
|
+
env.bak/
|
|
158
|
+
venv.bak/
|
|
159
|
+
|
|
160
|
+
# Spyder project settings
|
|
161
|
+
.spyderproject
|
|
162
|
+
.spyproject
|
|
163
|
+
|
|
164
|
+
# Rope project settings
|
|
165
|
+
.ropeproject
|
|
166
|
+
|
|
167
|
+
# mkdocs documentation
|
|
168
|
+
/site
|
|
169
|
+
|
|
170
|
+
# mypy
|
|
171
|
+
.mypy_cache/
|
|
172
|
+
.dmypy.json
|
|
173
|
+
dmypy.json
|
|
174
|
+
|
|
175
|
+
# Pyre type checker
|
|
176
|
+
.pyre/
|
|
177
|
+
|
|
178
|
+
# pytype static type analyzer
|
|
179
|
+
.pytype/
|
|
180
|
+
|
|
181
|
+
# Cython debug symbols
|
|
182
|
+
cython_debug/
|
|
183
|
+
|
|
184
|
+
# PyCharm
|
|
185
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
186
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
187
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
188
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
189
|
+
# .idea/
|
|
190
|
+
|
|
191
|
+
# Abstra
|
|
192
|
+
# Abstra is an AI-powered process automation framework.
|
|
193
|
+
# Ignore directories containing user credentials, local state, and settings.
|
|
194
|
+
# Learn more at https://abstra.io/docs
|
|
195
|
+
.abstra/
|
|
196
|
+
|
|
197
|
+
# Visual Studio Code
|
|
198
|
+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
|
199
|
+
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
|
200
|
+
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
|
201
|
+
# you could uncomment the following to ignore the entire vscode folder
|
|
202
|
+
# .vscode/
|
|
203
|
+
# Temporary file for partial code execution
|
|
204
|
+
tempCodeRunnerFile.py
|
|
205
|
+
|
|
206
|
+
# Ruff stuff:
|
|
207
|
+
.ruff_cache/
|
|
208
|
+
|
|
209
|
+
# PyPI configuration file
|
|
210
|
+
.pypirc
|
|
211
|
+
|
|
212
|
+
# Marimo
|
|
213
|
+
marimo/_static/
|
|
214
|
+
marimo/_lsp/
|
|
215
|
+
__marimo__/
|
|
216
|
+
|
|
217
|
+
# Streamlit
|
|
218
|
+
.streamlit/secrets.toml
|
mcp_craft-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Rishav
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
mcp_craft-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mcp-craft
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A lightweight, production-ready developer framework for building Model Context Protocol (MCP) servers with a FastAPI-like experience.
|
|
5
|
+
Author: Rishav Sharma
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Classifier: Development Status :: 4 - Beta
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
+
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
15
|
+
Requires-Python: >=3.11
|
|
16
|
+
Requires-Dist: mcp>=1.0.0
|
|
17
|
+
Requires-Dist: pydantic>=2.0.0
|
|
18
|
+
Provides-Extra: dev
|
|
19
|
+
Requires-Dist: pytest-asyncio>=0.20.0; extra == 'dev'
|
|
20
|
+
Requires-Dist: pytest>=7.0.0; extra == 'dev'
|
|
21
|
+
Requires-Dist: ruff>=0.1.0; extra == 'dev'
|
|
22
|
+
Provides-Extra: django
|
|
23
|
+
Requires-Dist: django>=4.0.0; extra == 'django'
|
|
24
|
+
Provides-Extra: fastapi
|
|
25
|
+
Requires-Dist: fastapi>=0.100.0; extra == 'fastapi'
|
|
26
|
+
Requires-Dist: starlette>=0.27.0; extra == 'fastapi'
|
|
27
|
+
Requires-Dist: uvicorn>=0.20.0; extra == 'fastapi'
|
|
28
|
+
Provides-Extra: flask
|
|
29
|
+
Requires-Dist: flask>=2.0.0; extra == 'flask'
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
|
|
32
|
+
# mcp-craft
|
|
33
|
+
|
|
34
|
+
A modern, lightweight, production-ready framework for building **Model Context Protocol (MCP)** servers in Python with a developer experience inspired by **FastAPI**.
|
|
35
|
+
|
|
36
|
+
`mcp-craft` wraps the official MCP Python SDK to offer tool discovery, dependency injection, middleware chains, authentication, schema validation, and framework integrations (FastAPI, Flask, Django) without reimplementing the protocol.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Features
|
|
41
|
+
|
|
42
|
+
- **Declarative Tool Registration**: Decorators (`@app.tool`) on functions and classes with automatic Pydantic-based schema generation and validation.
|
|
43
|
+
- **FastAPI-like Dependency Injection**: A lightweight, standalone DI engine supporting sync and async dependencies (`Depends`).
|
|
44
|
+
- **Flexible Middleware Pipeline**: Starlette-like async middleware with support for custom interceptors and built-in middlewares (`LoggingMiddleware`, `AuthenticationMiddleware`, `MetricsMiddleware`, `RateLimitMiddleware`).
|
|
45
|
+
- **Structured Authentication Helpers**: Easily secure your MCP server with API Keys, Bearer tokens, or custom callback functions.
|
|
46
|
+
- **Rich Context Object**: Expose execution context (`context.user`, `context.request`, `context.logger`, `context.metadata`, `context.storage`) to all tools automatically.
|
|
47
|
+
- **Error Handling**: Standard exception mapping that sanitizes tracebacks and converts Python errors to protocol-compliant MCP errors.
|
|
48
|
+
- **Extensible Plugin System**: Install reusable plugins (`app.install(plugin)`) to hook into startup, shutdown, middlewares, or register dependencies and tools.
|
|
49
|
+
- **Integrations**: Standalone runtime (via stdio) and first-class FastAPI, Flask, and Django integrations.
|
|
50
|
+
- **Testing Utilities**: A dedicated `TestMCPClient` and mocking helpers for rapid tool testing without network layers.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Installation
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
pip install mcp-craft
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Or install with integrations:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
# For FastAPI / Starlette support
|
|
64
|
+
pip install "mcp-craft[fastapi]"
|
|
65
|
+
|
|
66
|
+
# For development / testing
|
|
67
|
+
pip install "mcp-craft[dev]"
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Quick Start (Standalone / Stdio)
|
|
73
|
+
|
|
74
|
+
Creating a server is as simple as defining your application and registering your tools.
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
# server.py
|
|
78
|
+
import asyncio
|
|
79
|
+
from mcp_craft import MCPApplication
|
|
80
|
+
|
|
81
|
+
app = MCPApplication(
|
|
82
|
+
name="Math Server",
|
|
83
|
+
version="1.0.0",
|
|
84
|
+
description="Exposes basic mathematical tools"
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
@app.tool()
|
|
88
|
+
async def add(a: int, b: int) -> int:
|
|
89
|
+
"""Add two numbers together."""
|
|
90
|
+
return a + b
|
|
91
|
+
|
|
92
|
+
if __name__ == "__main__":
|
|
93
|
+
app.run()
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Run it locally with:
|
|
97
|
+
```bash
|
|
98
|
+
python server.py
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## Dependency Injection
|
|
104
|
+
|
|
105
|
+
Inject shared components or services using `Depends`:
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
from mcp_craft import MCPApplication, Depends
|
|
109
|
+
|
|
110
|
+
app = MCPApplication(name="DI Server")
|
|
111
|
+
|
|
112
|
+
def get_db():
|
|
113
|
+
# Setup database connection
|
|
114
|
+
return {"conn": "active"}
|
|
115
|
+
|
|
116
|
+
async def get_current_user(context):
|
|
117
|
+
# Retrieve user from the execution context
|
|
118
|
+
return context.user
|
|
119
|
+
|
|
120
|
+
@app.tool()
|
|
121
|
+
async def fetch_profile(
|
|
122
|
+
user = Depends(get_current_user),
|
|
123
|
+
db = Depends(get_db)
|
|
124
|
+
):
|
|
125
|
+
"""Retrieve profile for the authenticated user."""
|
|
126
|
+
return {"username": user.get("name"), "status": "active", "db": db}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Middleware & Authentication
|
|
132
|
+
|
|
133
|
+
Add global logic like logging, metrics, rate-limiting, and authentication:
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
from mcp_craft import MCPApplication, APIKeyAuthentication
|
|
137
|
+
from mcp_craft.middleware import LoggingMiddleware, AuthenticationMiddleware, RateLimitMiddleware
|
|
138
|
+
|
|
139
|
+
app = MCPApplication(name="Secure Server")
|
|
140
|
+
|
|
141
|
+
# Configure authentication helper
|
|
142
|
+
auth_helper = APIKeyAuthentication(key="secret-token-123", header_name="x-api-key")
|
|
143
|
+
|
|
144
|
+
# Register middleware in order of execution
|
|
145
|
+
app.add_middleware(LoggingMiddleware)
|
|
146
|
+
app.add_middleware(AuthenticationMiddleware, auth_handler=auth_helper)
|
|
147
|
+
app.add_middleware(RateLimitMiddleware, limit=30, period=60.0) # 30 calls per minute
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## FastAPI Integration
|
|
153
|
+
|
|
154
|
+
Mount your MCP server onto a FastAPI app using the Server-Sent Events (SSE) protocol:
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
from fastapi import FastAPI
|
|
158
|
+
from mcp_craft import MCPApplication
|
|
159
|
+
from mcp_craft.integrations.fastapi import mount_mcp
|
|
160
|
+
import uvicorn
|
|
161
|
+
|
|
162
|
+
mcp_app = MCPApplication(name="Mounted Server")
|
|
163
|
+
|
|
164
|
+
@mcp_app.tool()
|
|
165
|
+
def hello(name: str) -> str:
|
|
166
|
+
"""Say hello."""
|
|
167
|
+
return f"Hello, {name}!"
|
|
168
|
+
|
|
169
|
+
app = FastAPI()
|
|
170
|
+
# This registers:
|
|
171
|
+
# GET /mcp/sse - SSE connection route
|
|
172
|
+
# POST /mcp/messages - Client message gateway
|
|
173
|
+
mount_mcp(app, mcp_app, path="/mcp")
|
|
174
|
+
|
|
175
|
+
if __name__ == "__main__":
|
|
176
|
+
uvicorn.run(app, host="127.0.0.1", port=8000)
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## Testing Tools
|
|
182
|
+
|
|
183
|
+
Unit test your tools cleanly without running servers or network loops:
|
|
184
|
+
|
|
185
|
+
```python
|
|
186
|
+
import pytest
|
|
187
|
+
from mcp_craft import MCPApplication
|
|
188
|
+
from mcp_craft.testing import TestMCPClient
|
|
189
|
+
|
|
190
|
+
app = MCPApplication(name="Test App")
|
|
191
|
+
|
|
192
|
+
@app.tool()
|
|
193
|
+
def square(x: int) -> int:
|
|
194
|
+
return x * x
|
|
195
|
+
|
|
196
|
+
@pytest.mark.asyncio
|
|
197
|
+
async def test_square():
|
|
198
|
+
client = TestMCPClient(app)
|
|
199
|
+
result = await client.call_tool("square", {"x": 5})
|
|
200
|
+
assert result.content[0].text == "25"
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## Development & Publishing
|
|
206
|
+
|
|
207
|
+
To package and build:
|
|
208
|
+
```bash
|
|
209
|
+
# Install packaging tool
|
|
210
|
+
pip install build hatch
|
|
211
|
+
|
|
212
|
+
# Build source distribution and wheel
|
|
213
|
+
python3 -m build
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## License
|
|
219
|
+
|
|
220
|
+
This project is licensed under the MIT License - see the LICENSE file for details.
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# mcp-craft
|
|
2
|
+
|
|
3
|
+
A modern, lightweight, production-ready framework for building **Model Context Protocol (MCP)** servers in Python with a developer experience inspired by **FastAPI**.
|
|
4
|
+
|
|
5
|
+
`mcp-craft` wraps the official MCP Python SDK to offer tool discovery, dependency injection, middleware chains, authentication, schema validation, and framework integrations (FastAPI, Flask, Django) without reimplementing the protocol.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- **Declarative Tool Registration**: Decorators (`@app.tool`) on functions and classes with automatic Pydantic-based schema generation and validation.
|
|
12
|
+
- **FastAPI-like Dependency Injection**: A lightweight, standalone DI engine supporting sync and async dependencies (`Depends`).
|
|
13
|
+
- **Flexible Middleware Pipeline**: Starlette-like async middleware with support for custom interceptors and built-in middlewares (`LoggingMiddleware`, `AuthenticationMiddleware`, `MetricsMiddleware`, `RateLimitMiddleware`).
|
|
14
|
+
- **Structured Authentication Helpers**: Easily secure your MCP server with API Keys, Bearer tokens, or custom callback functions.
|
|
15
|
+
- **Rich Context Object**: Expose execution context (`context.user`, `context.request`, `context.logger`, `context.metadata`, `context.storage`) to all tools automatically.
|
|
16
|
+
- **Error Handling**: Standard exception mapping that sanitizes tracebacks and converts Python errors to protocol-compliant MCP errors.
|
|
17
|
+
- **Extensible Plugin System**: Install reusable plugins (`app.install(plugin)`) to hook into startup, shutdown, middlewares, or register dependencies and tools.
|
|
18
|
+
- **Integrations**: Standalone runtime (via stdio) and first-class FastAPI, Flask, and Django integrations.
|
|
19
|
+
- **Testing Utilities**: A dedicated `TestMCPClient` and mocking helpers for rapid tool testing without network layers.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pip install mcp-craft
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Or install with integrations:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# For FastAPI / Starlette support
|
|
33
|
+
pip install "mcp-craft[fastapi]"
|
|
34
|
+
|
|
35
|
+
# For development / testing
|
|
36
|
+
pip install "mcp-craft[dev]"
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Quick Start (Standalone / Stdio)
|
|
42
|
+
|
|
43
|
+
Creating a server is as simple as defining your application and registering your tools.
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
# server.py
|
|
47
|
+
import asyncio
|
|
48
|
+
from mcp_craft import MCPApplication
|
|
49
|
+
|
|
50
|
+
app = MCPApplication(
|
|
51
|
+
name="Math Server",
|
|
52
|
+
version="1.0.0",
|
|
53
|
+
description="Exposes basic mathematical tools"
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
@app.tool()
|
|
57
|
+
async def add(a: int, b: int) -> int:
|
|
58
|
+
"""Add two numbers together."""
|
|
59
|
+
return a + b
|
|
60
|
+
|
|
61
|
+
if __name__ == "__main__":
|
|
62
|
+
app.run()
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Run it locally with:
|
|
66
|
+
```bash
|
|
67
|
+
python server.py
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Dependency Injection
|
|
73
|
+
|
|
74
|
+
Inject shared components or services using `Depends`:
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
from mcp_craft import MCPApplication, Depends
|
|
78
|
+
|
|
79
|
+
app = MCPApplication(name="DI Server")
|
|
80
|
+
|
|
81
|
+
def get_db():
|
|
82
|
+
# Setup database connection
|
|
83
|
+
return {"conn": "active"}
|
|
84
|
+
|
|
85
|
+
async def get_current_user(context):
|
|
86
|
+
# Retrieve user from the execution context
|
|
87
|
+
return context.user
|
|
88
|
+
|
|
89
|
+
@app.tool()
|
|
90
|
+
async def fetch_profile(
|
|
91
|
+
user = Depends(get_current_user),
|
|
92
|
+
db = Depends(get_db)
|
|
93
|
+
):
|
|
94
|
+
"""Retrieve profile for the authenticated user."""
|
|
95
|
+
return {"username": user.get("name"), "status": "active", "db": db}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Middleware & Authentication
|
|
101
|
+
|
|
102
|
+
Add global logic like logging, metrics, rate-limiting, and authentication:
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
from mcp_craft import MCPApplication, APIKeyAuthentication
|
|
106
|
+
from mcp_craft.middleware import LoggingMiddleware, AuthenticationMiddleware, RateLimitMiddleware
|
|
107
|
+
|
|
108
|
+
app = MCPApplication(name="Secure Server")
|
|
109
|
+
|
|
110
|
+
# Configure authentication helper
|
|
111
|
+
auth_helper = APIKeyAuthentication(key="secret-token-123", header_name="x-api-key")
|
|
112
|
+
|
|
113
|
+
# Register middleware in order of execution
|
|
114
|
+
app.add_middleware(LoggingMiddleware)
|
|
115
|
+
app.add_middleware(AuthenticationMiddleware, auth_handler=auth_helper)
|
|
116
|
+
app.add_middleware(RateLimitMiddleware, limit=30, period=60.0) # 30 calls per minute
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## FastAPI Integration
|
|
122
|
+
|
|
123
|
+
Mount your MCP server onto a FastAPI app using the Server-Sent Events (SSE) protocol:
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
from fastapi import FastAPI
|
|
127
|
+
from mcp_craft import MCPApplication
|
|
128
|
+
from mcp_craft.integrations.fastapi import mount_mcp
|
|
129
|
+
import uvicorn
|
|
130
|
+
|
|
131
|
+
mcp_app = MCPApplication(name="Mounted Server")
|
|
132
|
+
|
|
133
|
+
@mcp_app.tool()
|
|
134
|
+
def hello(name: str) -> str:
|
|
135
|
+
"""Say hello."""
|
|
136
|
+
return f"Hello, {name}!"
|
|
137
|
+
|
|
138
|
+
app = FastAPI()
|
|
139
|
+
# This registers:
|
|
140
|
+
# GET /mcp/sse - SSE connection route
|
|
141
|
+
# POST /mcp/messages - Client message gateway
|
|
142
|
+
mount_mcp(app, mcp_app, path="/mcp")
|
|
143
|
+
|
|
144
|
+
if __name__ == "__main__":
|
|
145
|
+
uvicorn.run(app, host="127.0.0.1", port=8000)
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## Testing Tools
|
|
151
|
+
|
|
152
|
+
Unit test your tools cleanly without running servers or network loops:
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
import pytest
|
|
156
|
+
from mcp_craft import MCPApplication
|
|
157
|
+
from mcp_craft.testing import TestMCPClient
|
|
158
|
+
|
|
159
|
+
app = MCPApplication(name="Test App")
|
|
160
|
+
|
|
161
|
+
@app.tool()
|
|
162
|
+
def square(x: int) -> int:
|
|
163
|
+
return x * x
|
|
164
|
+
|
|
165
|
+
@pytest.mark.asyncio
|
|
166
|
+
async def test_square():
|
|
167
|
+
client = TestMCPClient(app)
|
|
168
|
+
result = await client.call_tool("square", {"x": 5})
|
|
169
|
+
assert result.content[0].text == "25"
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## Development & Publishing
|
|
175
|
+
|
|
176
|
+
To package and build:
|
|
177
|
+
```bash
|
|
178
|
+
# Install packaging tool
|
|
179
|
+
pip install build hatch
|
|
180
|
+
|
|
181
|
+
# Build source distribution and wheel
|
|
182
|
+
python3 -m build
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## License
|
|
188
|
+
|
|
189
|
+
This project is licensed under the MIT License - see the LICENSE file for details.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
from typing import Dict, Any
|
|
2
|
+
from mcp_craft import MCPApplication, Depends, Context, APIKeyAuthentication
|
|
3
|
+
from mcp_craft.middleware import LoggingMiddleware, AuthenticationMiddleware, RateLimitMiddleware
|
|
4
|
+
|
|
5
|
+
# 1. Initialize the Application
|
|
6
|
+
app = MCPApplication(
|
|
7
|
+
name="MCP Demo Server",
|
|
8
|
+
version="1.0.0",
|
|
9
|
+
description="A server demonstrating the capabilities of the mcp_craft framework"
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
# 2. Configure Authentication and Middleware
|
|
13
|
+
auth_helper = APIKeyAuthentication(key="secret-token-123", header_name="x-api-key")
|
|
14
|
+
|
|
15
|
+
app.add_middleware(LoggingMiddleware)
|
|
16
|
+
# app.add_middleware(AuthenticationMiddleware, auth_handler=auth_helper)
|
|
17
|
+
app.add_middleware(RateLimitMiddleware, limit=10, period=60.0) # Limit 10 calls per minute
|
|
18
|
+
|
|
19
|
+
# 3. Setup Lifecycle Hooks
|
|
20
|
+
@app.on_startup
|
|
21
|
+
def on_startup():
|
|
22
|
+
print("[Lifecycle] MCP server starting up...")
|
|
23
|
+
|
|
24
|
+
@app.on_shutdown
|
|
25
|
+
async def on_shutdown():
|
|
26
|
+
print("[Lifecycle] MCP server shutting down...")
|
|
27
|
+
|
|
28
|
+
# 4. Dependency Provider
|
|
29
|
+
def get_system_status():
|
|
30
|
+
return {"status": "operational", "load": "low"}
|
|
31
|
+
|
|
32
|
+
async def get_user_identity(context: Context):
|
|
33
|
+
# Retrieve the user object populated by AuthenticationMiddleware
|
|
34
|
+
return context.user
|
|
35
|
+
|
|
36
|
+
# 5. Register Tools
|
|
37
|
+
@app.tool()
|
|
38
|
+
async def get_status(
|
|
39
|
+
status: Dict[str, Any] = Depends(get_system_status),
|
|
40
|
+
user: Dict[str, Any] = Depends(get_user_identity)
|
|
41
|
+
) -> Dict[str, Any]:
|
|
42
|
+
"""
|
|
43
|
+
Get the current system status.
|
|
44
|
+
This tool uses FastAPI-style dependency injection and inherits execution context.
|
|
45
|
+
"""
|
|
46
|
+
return {
|
|
47
|
+
"status": status,
|
|
48
|
+
"caller": user.get("identity") if user else "anonymous"
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@app.tool()
|
|
52
|
+
def hello(name: str) -> str:
|
|
53
|
+
"""
|
|
54
|
+
Say hello to the caller.
|
|
55
|
+
"""
|
|
56
|
+
return f"Hello, {name}!"
|
|
57
|
+
|
|
58
|
+
# 6. Run the application
|
|
59
|
+
if __name__ == "__main__":
|
|
60
|
+
# To run this example locally:
|
|
61
|
+
# 1. Use an MCP client (such as Claude Desktop) pointing to this file.
|
|
62
|
+
# 2. Provide the 'x-api-key: secret-token-123' metadata during connection or request headers.
|
|
63
|
+
app.run()
|