create-mcp 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.
- create_mcp-0.1.0/.gitignore +21 -0
- create_mcp-0.1.0/LICENSE +21 -0
- create_mcp-0.1.0/PKG-INFO +157 -0
- create_mcp-0.1.0/README.md +131 -0
- create_mcp-0.1.0/pyproject.toml +85 -0
- create_mcp-0.1.0/src/create_mcp/__init__.py +10 -0
- create_mcp-0.1.0/src/create_mcp/__main__.py +8 -0
- create_mcp-0.1.0/src/create_mcp/cli.py +193 -0
- create_mcp-0.1.0/src/create_mcp/generator.py +178 -0
- create_mcp-0.1.0/src/create_mcp/presets.py +57 -0
- create_mcp-0.1.0/src/create_mcp/templates/__init__.py +7 -0
- create_mcp-0.1.0/src/create_mcp/templates/auth/src/__pkg__/auth.py.jinja +34 -0
- create_mcp-0.1.0/src/create_mcp/templates/auth/tests/test_auth.py.jinja +44 -0
- create_mcp-0.1.0/src/create_mcp/templates/base/Dockerfile.jinja +32 -0
- create_mcp-0.1.0/src/create_mcp/templates/base/LICENSE.jinja +21 -0
- create_mcp-0.1.0/src/create_mcp/templates/base/README.md.jinja +122 -0
- create_mcp-0.1.0/src/create_mcp/templates/base/dot-dockerignore.jinja +13 -0
- create_mcp-0.1.0/src/create_mcp/templates/base/dot-env.example.jinja +21 -0
- create_mcp-0.1.0/src/create_mcp/templates/base/dot-github/workflows/ci.yml.jinja +32 -0
- create_mcp-0.1.0/src/create_mcp/templates/base/dot-gitignore.jinja +29 -0
- create_mcp-0.1.0/src/create_mcp/templates/base/dot-pre-commit-config.yaml.jinja +15 -0
- create_mcp-0.1.0/src/create_mcp/templates/base/pyproject.toml.jinja +58 -0
- create_mcp-0.1.0/src/create_mcp/templates/base/src/__pkg__/__init__.py.jinja +3 -0
- create_mcp-0.1.0/src/create_mcp/templates/base/src/__pkg__/__main__.py.jinja +17 -0
- create_mcp-0.1.0/src/create_mcp/templates/base/src/__pkg__/app.py.jinja +31 -0
- create_mcp-0.1.0/src/create_mcp/templates/base/src/__pkg__/server.py.jinja +30 -0
- create_mcp-0.1.0/src/create_mcp/templates/base/src/__pkg__/settings.py.jinja +30 -0
- create_mcp-0.1.0/src/create_mcp/templates/base/tests/__init__.py.jinja +0 -0
- create_mcp-0.1.0/src/create_mcp/templates/base/tests/conftest.py.jinja +18 -0
- create_mcp-0.1.0/src/create_mcp/templates/base/tests/test_server.py.jinja +19 -0
- create_mcp-0.1.0/src/create_mcp/templates/presets/agent_tools/src/__pkg__/tools.py.jinja +73 -0
- create_mcp-0.1.0/src/create_mcp/templates/presets/agent_tools/tests/test_tools.py.jinja +45 -0
- create_mcp-0.1.0/src/create_mcp/templates/presets/api_wrapper/src/__pkg__/tools.py.jinja +41 -0
- create_mcp-0.1.0/src/create_mcp/templates/presets/api_wrapper/tests/test_tools.py.jinja +29 -0
- create_mcp-0.1.0/src/create_mcp/templates/presets/db/src/__pkg__/tools.py.jinja +79 -0
- create_mcp-0.1.0/src/create_mcp/templates/presets/db/tests/test_tools.py.jinja +29 -0
- create_mcp-0.1.0/src/create_mcp/templates/presets/minimal/src/__pkg__/tools.py.jinja +40 -0
- create_mcp-0.1.0/src/create_mcp/templates/presets/minimal/tests/test_tools.py.jinja +25 -0
- create_mcp-0.1.0/tests/__init__.py +0 -0
- create_mcp-0.1.0/tests/test_cli.py +47 -0
- create_mcp-0.1.0/tests/test_generate.py +95 -0
create_mcp-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Blaze (https://blaze.uz)
|
|
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.
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: create-mcp
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: The create-next-app for production MCP servers — scaffold a typed, tested, auth-ready Python MCP server in one command.
|
|
5
|
+
Project-URL: Homepage, https://github.com/blaze-uz/create-mcp
|
|
6
|
+
Project-URL: Repository, https://github.com/blaze-uz/create-mcp
|
|
7
|
+
Project-URL: Issues, https://github.com/blaze-uz/create-mcp/issues
|
|
8
|
+
Author-email: Blaze <hello@blaze.uz>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: agents,ai,cli,fastapi,fastmcp,generator,mcp,model-context-protocol,oauth,scaffold
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Software Development :: Code Generators
|
|
20
|
+
Classifier: Typing :: Typed
|
|
21
|
+
Requires-Python: >=3.11
|
|
22
|
+
Requires-Dist: jinja2>=3.1
|
|
23
|
+
Requires-Dist: rich>=13.7
|
|
24
|
+
Requires-Dist: typer>=0.15
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
<div align="center">
|
|
28
|
+
|
|
29
|
+
# create-mcp
|
|
30
|
+
|
|
31
|
+
**The `create-next-app` for production MCP servers.**
|
|
32
|
+
|
|
33
|
+
One command scaffolds a typed, tested, auth-ready Python [MCP](https://modelcontextprotocol.io) server you can *ship* — not just run.
|
|
34
|
+
|
|
35
|
+
[](https://github.com/blaze-uz/create-mcp/actions/workflows/ci.yml)
|
|
36
|
+
[](https://pypi.org/project/create-mcp/)
|
|
37
|
+
[](https://pypi.org/project/create-mcp/)
|
|
38
|
+
[](LICENSE)
|
|
39
|
+
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
uvx create-mcp my-server
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
<!-- A terminal recording (vhs / asciinema) can replace this block before launch. -->
|
|
47
|
+
|
|
48
|
+
```text
|
|
49
|
+
$ uvx create-mcp
|
|
50
|
+
Project name: pay-tools
|
|
51
|
+
Preset: minimal
|
|
52
|
+
Transport: streamable-http
|
|
53
|
+
Auth: oauth
|
|
54
|
+
|
|
55
|
+
✓ Scaffolded pay-tools (Minimal · streamable-http · auth: oauth)
|
|
56
|
+
|
|
57
|
+
cd pay-tools
|
|
58
|
+
uv sync # install dependencies
|
|
59
|
+
uv run pay_tools # start the server
|
|
60
|
+
uv run pytest # green ✓
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Why
|
|
64
|
+
|
|
65
|
+
The official MCP SDK quickstart and `create-mcp-server` hand you a single
|
|
66
|
+
hello-world tool and stop. You still have to add tests, typing, linting, CI, a
|
|
67
|
+
Dockerfile, config, and — the hard one — spec-compliant **OAuth 2.1**. Most MCP
|
|
68
|
+
servers in the wild are hello-world demos that never reach production.
|
|
69
|
+
|
|
70
|
+
`create-mcp` generates the *whole* repo, green on the first run:
|
|
71
|
+
|
|
72
|
+
- ⚡ **`uvx create-mcp` — zero install.** Same ergonomics as `create-next-app`.
|
|
73
|
+
- 🧱 **Production defaults, not a toy.** Tests, CI, Docker, ruff, mypy, pre-commit, `.env`, a real README.
|
|
74
|
+
- 🔐 **OAuth 2.1 in one flag.** `--auth oauth` scaffolds an [RFC 9728](https://datatracker.ietf.org/doc/html/rfc9728) resource server (Protected Resource Metadata + `401`/`WWW-Authenticate` discovery + JWT validation). Timed for the 2026 MCP authorization spec.
|
|
75
|
+
- 🌊 **Streamable HTTP first.** The modern transport (SSE is deprecated), plus a stdio preset for Claude Desktop / Cursor.
|
|
76
|
+
- 🧪 **Tests pass out of the box.** Generated tests use FastMCP's in-memory client — no network, milliseconds.
|
|
77
|
+
- 🎯 **Presets, not questionnaires.** Scaffold a *use case*: `minimal`, `api-wrapper`, `db`, `agent-tools`.
|
|
78
|
+
- 🐍 **Typed end-to-end.** Pydantic models for tool I/O; ruff- and mypy-clean.
|
|
79
|
+
|
|
80
|
+
Built on [FastMCP](https://gofastmcp.com) + [`uv`](https://docs.astral.sh/uv/).
|
|
81
|
+
|
|
82
|
+
## Usage
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
# Interactive
|
|
86
|
+
uvx create-mcp
|
|
87
|
+
|
|
88
|
+
# Scripted / non-interactive
|
|
89
|
+
uvx create-mcp pay-tools --preset api-wrapper --auth oauth --yes
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Then:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
cd pay-tools
|
|
96
|
+
uv sync
|
|
97
|
+
uv run pay_tools # start the server
|
|
98
|
+
uv run pytest # green ✓
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Options
|
|
102
|
+
|
|
103
|
+
| Flag | Values | Default | Description |
|
|
104
|
+
|------|--------|---------|-------------|
|
|
105
|
+
| `--preset` `-p` | `minimal`, `api-wrapper`, `db`, `agent-tools` | `minimal` | Starting set of tools/resources/prompts |
|
|
106
|
+
| `--transport` `-t` | `streamable-http`, `stdio` | `streamable-http` | MCP transport |
|
|
107
|
+
| `--auth` `-a` | `none`, `oauth` | `none` | OAuth 2.1 resource server (RFC 9728) |
|
|
108
|
+
| `--package-name` | identifier | derived | Override the Python package name |
|
|
109
|
+
| `--output-dir` `-o` | path | `.` | Where to create the project |
|
|
110
|
+
| `--no-git` / `--git` | | `--git` | Initialise a git repo + first commit |
|
|
111
|
+
| `--no-install` / `--install` | | `--install` | Run `uv sync` after scaffolding |
|
|
112
|
+
| `--no-precommit` / `--precommit` | | `--precommit` | Install pre-commit hooks |
|
|
113
|
+
| `--force` | | off | Overwrite a non-empty target directory |
|
|
114
|
+
| `--yes` `-y` | | off | Accept all defaults; never prompt (CI) |
|
|
115
|
+
|
|
116
|
+
### Presets
|
|
117
|
+
|
|
118
|
+
| Preset | What you get |
|
|
119
|
+
|--------|--------------|
|
|
120
|
+
| **minimal** | A clean typed server: one tool (structured output), a resource, a prompt. |
|
|
121
|
+
| **api-wrapper** | Wrap an HTTP/JSON API as MCP tools, with the network call isolated for easy mocking. |
|
|
122
|
+
| **db** | A SQLite-backed store exposed as CRUD tools (swap in your real DB). |
|
|
123
|
+
| **agent-tools** | A toolbox for agents: safe calculator, scratchpad memory, clock. |
|
|
124
|
+
|
|
125
|
+
## What the generated project looks like
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
my-server/
|
|
129
|
+
├── src/my_server/
|
|
130
|
+
│ ├── server.py # FastMCP instance (+ /health, + auth when enabled)
|
|
131
|
+
│ ├── tools.py # your tools, resources, prompts
|
|
132
|
+
│ ├── settings.py # typed config (pydantic-settings)
|
|
133
|
+
│ ├── app.py # FastAPI host mounting MCP at /mcp
|
|
134
|
+
│ ├── auth.py # OAuth 2.1 resource server (only with --auth oauth)
|
|
135
|
+
│ └── __main__.py # `uv run my_server`
|
|
136
|
+
├── tests/ # in-memory tests, green out of the box
|
|
137
|
+
├── Dockerfile # uv-based image
|
|
138
|
+
├── .github/workflows/ci.yml
|
|
139
|
+
├── .pre-commit-config.yaml
|
|
140
|
+
├── .env.example
|
|
141
|
+
└── pyproject.toml
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Requirements
|
|
145
|
+
|
|
146
|
+
- [`uv`](https://docs.astral.sh/uv/) (for `uvx` and the generated projects)
|
|
147
|
+
- Python 3.11+
|
|
148
|
+
|
|
149
|
+
## Contributing
|
|
150
|
+
|
|
151
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md). Every release runs the full matrix —
|
|
152
|
+
generating a project for **each preset × auth mode**, then installing, linting,
|
|
153
|
+
type-checking and testing it — so the templates can't rot silently.
|
|
154
|
+
|
|
155
|
+
## License
|
|
156
|
+
|
|
157
|
+
MIT © [Blaze](https://blaze.uz)
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# create-mcp
|
|
4
|
+
|
|
5
|
+
**The `create-next-app` for production MCP servers.**
|
|
6
|
+
|
|
7
|
+
One command scaffolds a typed, tested, auth-ready Python [MCP](https://modelcontextprotocol.io) server you can *ship* — not just run.
|
|
8
|
+
|
|
9
|
+
[](https://github.com/blaze-uz/create-mcp/actions/workflows/ci.yml)
|
|
10
|
+
[](https://pypi.org/project/create-mcp/)
|
|
11
|
+
[](https://pypi.org/project/create-mcp/)
|
|
12
|
+
[](LICENSE)
|
|
13
|
+
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
uvx create-mcp my-server
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
<!-- A terminal recording (vhs / asciinema) can replace this block before launch. -->
|
|
21
|
+
|
|
22
|
+
```text
|
|
23
|
+
$ uvx create-mcp
|
|
24
|
+
Project name: pay-tools
|
|
25
|
+
Preset: minimal
|
|
26
|
+
Transport: streamable-http
|
|
27
|
+
Auth: oauth
|
|
28
|
+
|
|
29
|
+
✓ Scaffolded pay-tools (Minimal · streamable-http · auth: oauth)
|
|
30
|
+
|
|
31
|
+
cd pay-tools
|
|
32
|
+
uv sync # install dependencies
|
|
33
|
+
uv run pay_tools # start the server
|
|
34
|
+
uv run pytest # green ✓
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Why
|
|
38
|
+
|
|
39
|
+
The official MCP SDK quickstart and `create-mcp-server` hand you a single
|
|
40
|
+
hello-world tool and stop. You still have to add tests, typing, linting, CI, a
|
|
41
|
+
Dockerfile, config, and — the hard one — spec-compliant **OAuth 2.1**. Most MCP
|
|
42
|
+
servers in the wild are hello-world demos that never reach production.
|
|
43
|
+
|
|
44
|
+
`create-mcp` generates the *whole* repo, green on the first run:
|
|
45
|
+
|
|
46
|
+
- ⚡ **`uvx create-mcp` — zero install.** Same ergonomics as `create-next-app`.
|
|
47
|
+
- 🧱 **Production defaults, not a toy.** Tests, CI, Docker, ruff, mypy, pre-commit, `.env`, a real README.
|
|
48
|
+
- 🔐 **OAuth 2.1 in one flag.** `--auth oauth` scaffolds an [RFC 9728](https://datatracker.ietf.org/doc/html/rfc9728) resource server (Protected Resource Metadata + `401`/`WWW-Authenticate` discovery + JWT validation). Timed for the 2026 MCP authorization spec.
|
|
49
|
+
- 🌊 **Streamable HTTP first.** The modern transport (SSE is deprecated), plus a stdio preset for Claude Desktop / Cursor.
|
|
50
|
+
- 🧪 **Tests pass out of the box.** Generated tests use FastMCP's in-memory client — no network, milliseconds.
|
|
51
|
+
- 🎯 **Presets, not questionnaires.** Scaffold a *use case*: `minimal`, `api-wrapper`, `db`, `agent-tools`.
|
|
52
|
+
- 🐍 **Typed end-to-end.** Pydantic models for tool I/O; ruff- and mypy-clean.
|
|
53
|
+
|
|
54
|
+
Built on [FastMCP](https://gofastmcp.com) + [`uv`](https://docs.astral.sh/uv/).
|
|
55
|
+
|
|
56
|
+
## Usage
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
# Interactive
|
|
60
|
+
uvx create-mcp
|
|
61
|
+
|
|
62
|
+
# Scripted / non-interactive
|
|
63
|
+
uvx create-mcp pay-tools --preset api-wrapper --auth oauth --yes
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Then:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
cd pay-tools
|
|
70
|
+
uv sync
|
|
71
|
+
uv run pay_tools # start the server
|
|
72
|
+
uv run pytest # green ✓
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Options
|
|
76
|
+
|
|
77
|
+
| Flag | Values | Default | Description |
|
|
78
|
+
|------|--------|---------|-------------|
|
|
79
|
+
| `--preset` `-p` | `minimal`, `api-wrapper`, `db`, `agent-tools` | `minimal` | Starting set of tools/resources/prompts |
|
|
80
|
+
| `--transport` `-t` | `streamable-http`, `stdio` | `streamable-http` | MCP transport |
|
|
81
|
+
| `--auth` `-a` | `none`, `oauth` | `none` | OAuth 2.1 resource server (RFC 9728) |
|
|
82
|
+
| `--package-name` | identifier | derived | Override the Python package name |
|
|
83
|
+
| `--output-dir` `-o` | path | `.` | Where to create the project |
|
|
84
|
+
| `--no-git` / `--git` | | `--git` | Initialise a git repo + first commit |
|
|
85
|
+
| `--no-install` / `--install` | | `--install` | Run `uv sync` after scaffolding |
|
|
86
|
+
| `--no-precommit` / `--precommit` | | `--precommit` | Install pre-commit hooks |
|
|
87
|
+
| `--force` | | off | Overwrite a non-empty target directory |
|
|
88
|
+
| `--yes` `-y` | | off | Accept all defaults; never prompt (CI) |
|
|
89
|
+
|
|
90
|
+
### Presets
|
|
91
|
+
|
|
92
|
+
| Preset | What you get |
|
|
93
|
+
|--------|--------------|
|
|
94
|
+
| **minimal** | A clean typed server: one tool (structured output), a resource, a prompt. |
|
|
95
|
+
| **api-wrapper** | Wrap an HTTP/JSON API as MCP tools, with the network call isolated for easy mocking. |
|
|
96
|
+
| **db** | A SQLite-backed store exposed as CRUD tools (swap in your real DB). |
|
|
97
|
+
| **agent-tools** | A toolbox for agents: safe calculator, scratchpad memory, clock. |
|
|
98
|
+
|
|
99
|
+
## What the generated project looks like
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
my-server/
|
|
103
|
+
├── src/my_server/
|
|
104
|
+
│ ├── server.py # FastMCP instance (+ /health, + auth when enabled)
|
|
105
|
+
│ ├── tools.py # your tools, resources, prompts
|
|
106
|
+
│ ├── settings.py # typed config (pydantic-settings)
|
|
107
|
+
│ ├── app.py # FastAPI host mounting MCP at /mcp
|
|
108
|
+
│ ├── auth.py # OAuth 2.1 resource server (only with --auth oauth)
|
|
109
|
+
│ └── __main__.py # `uv run my_server`
|
|
110
|
+
├── tests/ # in-memory tests, green out of the box
|
|
111
|
+
├── Dockerfile # uv-based image
|
|
112
|
+
├── .github/workflows/ci.yml
|
|
113
|
+
├── .pre-commit-config.yaml
|
|
114
|
+
├── .env.example
|
|
115
|
+
└── pyproject.toml
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Requirements
|
|
119
|
+
|
|
120
|
+
- [`uv`](https://docs.astral.sh/uv/) (for `uvx` and the generated projects)
|
|
121
|
+
- Python 3.11+
|
|
122
|
+
|
|
123
|
+
## Contributing
|
|
124
|
+
|
|
125
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md). Every release runs the full matrix —
|
|
126
|
+
generating a project for **each preset × auth mode**, then installing, linting,
|
|
127
|
+
type-checking and testing it — so the templates can't rot silently.
|
|
128
|
+
|
|
129
|
+
## License
|
|
130
|
+
|
|
131
|
+
MIT © [Blaze](https://blaze.uz)
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "create-mcp"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "The create-next-app for production MCP servers — scaffold a typed, tested, auth-ready Python MCP server in one command."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
authors = [{ name = "Blaze", email = "hello@blaze.uz" }]
|
|
14
|
+
keywords = [
|
|
15
|
+
"mcp",
|
|
16
|
+
"model-context-protocol",
|
|
17
|
+
"fastmcp",
|
|
18
|
+
"scaffold",
|
|
19
|
+
"generator",
|
|
20
|
+
"cli",
|
|
21
|
+
"fastapi",
|
|
22
|
+
"oauth",
|
|
23
|
+
"ai",
|
|
24
|
+
"agents",
|
|
25
|
+
]
|
|
26
|
+
classifiers = [
|
|
27
|
+
"Development Status :: 4 - Beta",
|
|
28
|
+
"Environment :: Console",
|
|
29
|
+
"Intended Audience :: Developers",
|
|
30
|
+
"License :: OSI Approved :: MIT License",
|
|
31
|
+
"Programming Language :: Python :: 3.11",
|
|
32
|
+
"Programming Language :: Python :: 3.12",
|
|
33
|
+
"Programming Language :: Python :: 3.13",
|
|
34
|
+
"Topic :: Software Development :: Code Generators",
|
|
35
|
+
"Typing :: Typed",
|
|
36
|
+
]
|
|
37
|
+
dependencies = [
|
|
38
|
+
"typer>=0.15",
|
|
39
|
+
"rich>=13.7",
|
|
40
|
+
"jinja2>=3.1",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
[project.urls]
|
|
44
|
+
Homepage = "https://github.com/blaze-uz/create-mcp"
|
|
45
|
+
Repository = "https://github.com/blaze-uz/create-mcp"
|
|
46
|
+
Issues = "https://github.com/blaze-uz/create-mcp/issues"
|
|
47
|
+
|
|
48
|
+
[project.scripts]
|
|
49
|
+
create-mcp = "create_mcp.cli:main"
|
|
50
|
+
|
|
51
|
+
[dependency-groups]
|
|
52
|
+
dev = [
|
|
53
|
+
"pytest>=8.3",
|
|
54
|
+
"ruff>=0.7",
|
|
55
|
+
"mypy>=1.13",
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
[tool.hatch.build.targets.wheel]
|
|
59
|
+
packages = ["src/create_mcp"]
|
|
60
|
+
|
|
61
|
+
# Ship every template file (including the dot-* placeholders) inside the wheel.
|
|
62
|
+
[tool.hatch.build.targets.sdist]
|
|
63
|
+
include = ["src/create_mcp", "tests", "README.md", "LICENSE"]
|
|
64
|
+
|
|
65
|
+
[tool.ruff]
|
|
66
|
+
line-length = 100
|
|
67
|
+
target-version = "py311"
|
|
68
|
+
# The templates/ tree is rendered Jinja, not importable Python — don't lint it.
|
|
69
|
+
extend-exclude = ["src/create_mcp/templates"]
|
|
70
|
+
|
|
71
|
+
[tool.ruff.lint]
|
|
72
|
+
select = ["E", "F", "I", "UP", "B", "SIM", "C4"]
|
|
73
|
+
|
|
74
|
+
[tool.ruff.lint.per-file-ignores]
|
|
75
|
+
# Typer requires typer.Option/Argument calls in parameter defaults.
|
|
76
|
+
"src/create_mcp/cli.py" = ["B008"]
|
|
77
|
+
|
|
78
|
+
[tool.pytest.ini_options]
|
|
79
|
+
testpaths = ["tests"]
|
|
80
|
+
addopts = "-q"
|
|
81
|
+
|
|
82
|
+
[tool.mypy]
|
|
83
|
+
python_version = "3.11"
|
|
84
|
+
strict = true
|
|
85
|
+
exclude = ["src/create_mcp/templates"]
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""create-mcp — scaffold production-ready Python MCP servers in one command."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
__version__ = "0.1.0"
|
|
6
|
+
|
|
7
|
+
# The FastMCP release the generated projects are pinned to / tested against.
|
|
8
|
+
FASTMCP_TARGET = "3.4"
|
|
9
|
+
|
|
10
|
+
__all__ = ["__version__", "FASTMCP_TARGET"]
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
"""The ``create-mcp`` command-line interface."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import shutil
|
|
6
|
+
import subprocess
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
import typer
|
|
10
|
+
from rich.console import Console
|
|
11
|
+
from rich.panel import Panel
|
|
12
|
+
from rich.prompt import Confirm, Prompt
|
|
13
|
+
from rich.text import Text
|
|
14
|
+
|
|
15
|
+
from . import FASTMCP_TARGET, __version__
|
|
16
|
+
from .generator import GeneratorError, ProjectConfig, generate, to_package_name
|
|
17
|
+
from .presets import DEFAULT_PRESET, PRESETS, preset_choices
|
|
18
|
+
|
|
19
|
+
console = Console()
|
|
20
|
+
err_console = Console(stderr=True)
|
|
21
|
+
|
|
22
|
+
TRANSPORTS = ["streamable-http", "stdio"]
|
|
23
|
+
AUTH_MODES = ["none", "oauth"]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _version_callback(value: bool) -> None:
|
|
27
|
+
if value:
|
|
28
|
+
console.print(f"create-mcp {__version__} [dim](targets FastMCP {FASTMCP_TARGET}.x)[/dim]")
|
|
29
|
+
raise typer.Exit()
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _run(cmd: list[str], cwd: Path) -> bool:
|
|
33
|
+
"""Run a command, streaming nothing; return True on success, False otherwise."""
|
|
34
|
+
try:
|
|
35
|
+
subprocess.run(cmd, cwd=cwd, check=True, capture_output=True)
|
|
36
|
+
return True
|
|
37
|
+
except (subprocess.CalledProcessError, FileNotFoundError):
|
|
38
|
+
return False
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _maybe_git_init(target: Path) -> None:
|
|
42
|
+
if shutil.which("git") is None:
|
|
43
|
+
console.print(" [yellow]›[/yellow] git not found — skipping repository init")
|
|
44
|
+
return
|
|
45
|
+
if _run(["git", "init", "-q"], target) and _run(["git", "add", "-A"], target):
|
|
46
|
+
_run(["git", "commit", "-q", "-m", "Initial commit (create-mcp)"], target)
|
|
47
|
+
console.print(" [green]✓[/green] initialised git repository")
|
|
48
|
+
else:
|
|
49
|
+
console.print(" [yellow]›[/yellow] could not initialise git repository")
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _maybe_uv_sync(target: Path) -> None:
|
|
53
|
+
if shutil.which("uv") is None:
|
|
54
|
+
console.print(" [yellow]›[/yellow] uv not found — skipping dependency install")
|
|
55
|
+
return
|
|
56
|
+
console.print(" [dim]…[/dim] installing dependencies with uv")
|
|
57
|
+
if _run(["uv", "sync"], target):
|
|
58
|
+
console.print(" [green]✓[/green] installed dependencies (uv sync)")
|
|
59
|
+
else:
|
|
60
|
+
console.print(" [yellow]›[/yellow] uv sync failed — run it yourself later")
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _maybe_precommit(target: Path) -> None:
|
|
64
|
+
if shutil.which("git") is None or not (target / ".git").exists():
|
|
65
|
+
return
|
|
66
|
+
if shutil.which("uv") is not None and _run(["uv", "run", "pre-commit", "install"], target):
|
|
67
|
+
console.print(" [green]✓[/green] installed pre-commit hooks")
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _prompt_choice(label: str, choices: list[str], default: str) -> str:
|
|
71
|
+
return Prompt.ask(f"[bold]{label}[/bold]", choices=choices, default=default)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def create( # noqa: C901 - the CLI orchestration is intentionally linear
|
|
75
|
+
project_name: str = typer.Argument(None, help="Name of the project / directory to create."),
|
|
76
|
+
preset: str = typer.Option(
|
|
77
|
+
None, "--preset", "-p", help=f"Preset: {', '.join(preset_choices())}."
|
|
78
|
+
),
|
|
79
|
+
transport: str = typer.Option(
|
|
80
|
+
None, "--transport", "-t", help="Transport: streamable-http or stdio."
|
|
81
|
+
),
|
|
82
|
+
auth: str = typer.Option(
|
|
83
|
+
None, "--auth", "-a", help="Auth mode: none or oauth (OAuth 2.1 resource server)."
|
|
84
|
+
),
|
|
85
|
+
package_name: str = typer.Option(
|
|
86
|
+
None, "--package-name", help="Override the derived Python package name."
|
|
87
|
+
),
|
|
88
|
+
description: str = typer.Option(None, "--description", help="One-line project description."),
|
|
89
|
+
output_dir: Path = typer.Option(
|
|
90
|
+
None, "--output-dir", "-o", help="Directory to create the project in (default: cwd)."
|
|
91
|
+
),
|
|
92
|
+
do_git: bool = typer.Option(True, "--git/--no-git", help="Initialise a git repository."),
|
|
93
|
+
do_install: bool = typer.Option(
|
|
94
|
+
True, "--install/--no-install", help="Run `uv sync` after scaffolding."
|
|
95
|
+
),
|
|
96
|
+
do_precommit: bool = typer.Option(
|
|
97
|
+
True, "--precommit/--no-precommit", help="Install pre-commit hooks."
|
|
98
|
+
),
|
|
99
|
+
force: bool = typer.Option(False, "--force", help="Overwrite a non-empty target directory."),
|
|
100
|
+
yes: bool = typer.Option(
|
|
101
|
+
False, "--yes", "-y", help="Accept all defaults; never prompt (CI / scripting)."
|
|
102
|
+
),
|
|
103
|
+
_version: bool = typer.Option(
|
|
104
|
+
None, "--version", callback=_version_callback, is_eager=True, help="Show version."
|
|
105
|
+
),
|
|
106
|
+
) -> None:
|
|
107
|
+
"""Scaffold a production-ready Python MCP server."""
|
|
108
|
+
interactive = not yes
|
|
109
|
+
output_dir = output_dir or Path.cwd()
|
|
110
|
+
|
|
111
|
+
def choose(label: str, choices: list[str], default: str, current: str | None) -> str:
|
|
112
|
+
if current is not None:
|
|
113
|
+
return current
|
|
114
|
+
return _prompt_choice(label, choices, default) if interactive else default
|
|
115
|
+
|
|
116
|
+
if not project_name:
|
|
117
|
+
if interactive:
|
|
118
|
+
project_name = Prompt.ask("[bold]Project name[/bold]", default="my-mcp-server")
|
|
119
|
+
else:
|
|
120
|
+
err_console.print("[red]error:[/red] project name is required with --yes")
|
|
121
|
+
raise typer.Exit(code=2)
|
|
122
|
+
|
|
123
|
+
preset = choose("Preset", preset_choices(), DEFAULT_PRESET, preset)
|
|
124
|
+
transport = choose("Transport", TRANSPORTS, "streamable-http", transport)
|
|
125
|
+
auth = choose("Auth", AUTH_MODES, "none", auth)
|
|
126
|
+
|
|
127
|
+
if interactive:
|
|
128
|
+
do_git = Confirm.ask("Initialise a git repository?", default=do_git)
|
|
129
|
+
do_install = Confirm.ask("Install dependencies now (uv sync)?", default=do_install)
|
|
130
|
+
if do_git:
|
|
131
|
+
do_precommit = Confirm.ask("Install pre-commit hooks?", default=do_precommit)
|
|
132
|
+
|
|
133
|
+
try:
|
|
134
|
+
config = ProjectConfig(
|
|
135
|
+
project_name=project_name,
|
|
136
|
+
preset=preset,
|
|
137
|
+
transport=transport,
|
|
138
|
+
auth=auth,
|
|
139
|
+
description=description or "",
|
|
140
|
+
)
|
|
141
|
+
if package_name:
|
|
142
|
+
# Validate + override the derived package name.
|
|
143
|
+
config.package_name = to_package_name(package_name)
|
|
144
|
+
target = output_dir / project_name
|
|
145
|
+
generate(config, target, force=force)
|
|
146
|
+
except GeneratorError as exc:
|
|
147
|
+
err_console.print(f"[red]error:[/red] {exc}")
|
|
148
|
+
raise typer.Exit(code=1) from exc
|
|
149
|
+
|
|
150
|
+
console.print()
|
|
151
|
+
console.print(
|
|
152
|
+
f"[green]✓[/green] Scaffolded [bold]{project_name}[/bold] "
|
|
153
|
+
f"[dim]({PRESETS[preset].title} · {transport} · auth: {auth})[/dim]"
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
if do_git:
|
|
157
|
+
_maybe_git_init(target)
|
|
158
|
+
if do_install:
|
|
159
|
+
_maybe_uv_sync(target)
|
|
160
|
+
if do_precommit:
|
|
161
|
+
_maybe_precommit(target)
|
|
162
|
+
|
|
163
|
+
_print_next_steps(config, target, output_dir)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def _print_next_steps(config: ProjectConfig, target: Path, output_dir: Path) -> None:
|
|
167
|
+
rel = target.relative_to(output_dir) if target.is_relative_to(output_dir) else target
|
|
168
|
+
pkg = config.package_name
|
|
169
|
+
run_cmd = f"uv run {pkg}"
|
|
170
|
+
body = Text()
|
|
171
|
+
body.append("Next steps\n\n", style="bold")
|
|
172
|
+
body.append(f" cd {rel}\n")
|
|
173
|
+
body.append(" uv sync ", style="cyan")
|
|
174
|
+
body.append("# install dependencies\n", style="dim")
|
|
175
|
+
body.append(f" {run_cmd}{' ' * max(1, 17 - len(run_cmd))}", style="cyan")
|
|
176
|
+
body.append(f"# run the server ({config.transport})\n", style="dim")
|
|
177
|
+
body.append(" uv run pytest ", style="cyan")
|
|
178
|
+
body.append("# run the test suite\n\n", style="dim")
|
|
179
|
+
body.append("Inspect it with the MCP Inspector:\n")
|
|
180
|
+
body.append(f" npx @modelcontextprotocol/inspector uv run {pkg}\n", style="cyan")
|
|
181
|
+
if config.auth_enabled:
|
|
182
|
+
body.append("\nAuth is on. ", style="bold yellow")
|
|
183
|
+
body.append("Set OAUTH_* vars in .env (see .env.example) and point them at\n")
|
|
184
|
+
body.append("your identity provider (Keycloak / WorkOS / Auth0 / Azure).")
|
|
185
|
+
console.print(Panel(body, border_style="green", expand=False))
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def main() -> None:
|
|
189
|
+
typer.run(create)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
if __name__ == "__main__":
|
|
193
|
+
main()
|