Compare commits
6 changed files with 163 additions and 276 deletions
|
|
@ -1,43 +0,0 @@
|
|||
# Contributing
|
||||
|
||||
Thank you for your interest in contributing to `create-mcp-server`! This tool helps developers quickly scaffold new MCP (Model Context Protocol) servers.
|
||||
|
||||
## Getting Started
|
||||
|
||||
1. Fork the repository
|
||||
2. Clone your fork: `git clone https://github.com/YOUR_USERNAME/create-python-server.git`
|
||||
3. Install dependencies: `uv sync --dev --all-extras`
|
||||
|
||||
## Development
|
||||
|
||||
- Make your changes in the `src` directory
|
||||
- Test your changes by creating a new server: `uv run -m create_mcp_server test-server`
|
||||
- Run type checking: `uv run pyright`
|
||||
- Run linting: `uv run ruff check .`
|
||||
|
||||
## Pull Requests
|
||||
|
||||
1. Create a new branch for your changes
|
||||
2. Make your changes
|
||||
3. Ensure type checking and linting pass
|
||||
4. Test server creation using your changes
|
||||
5. Submit a pull request with a clear description of your changes
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
This project follows our [Code of Conduct](CODE_OF_CONDUCT.md). Please review it before contributing.
|
||||
|
||||
## Reporting Issues
|
||||
|
||||
- Use the [GitHub issue tracker](https://github.com/modelcontextprotocol/create-python-server/issues)
|
||||
- Provide clear reproduction steps
|
||||
- Include relevant system information
|
||||
- Specify the version you're using
|
||||
|
||||
## Security Issues
|
||||
|
||||
Please review our [Security Policy](SECURITY.md) for reporting security vulnerabilities.
|
||||
|
||||
## License
|
||||
|
||||
By contributing, you agree that your contributions will be licensed under the MIT License.
|
||||
|
|
@ -1,22 +1,9 @@
|
|||
[project]
|
||||
name = "create-mcp-server"
|
||||
version = "1.0.6.dev0"
|
||||
description = "Create an Model Context Protocol server project from a template."
|
||||
version = "1.0.2"
|
||||
description = "Create an MCP server project from a template."
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10"
|
||||
authors = [{ name = "Anthropic, PBC." }]
|
||||
maintainers = [
|
||||
{ name = "David Soria Parra", email = "davidsp@anthropic.com" },
|
||||
{ name = "Justin Spahr-Summers", email = "justin@anthropic.com" },
|
||||
]
|
||||
license = { text = "MIT" }
|
||||
classifiers = [
|
||||
"Development Status :: 4 - Beta",
|
||||
"Intended Audience :: Developers",
|
||||
"License :: OSI Approved :: MIT License",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
]
|
||||
dependencies = [
|
||||
"click>=8.1.7",
|
||||
"jinja2>=3.1.4",
|
||||
|
|
@ -27,10 +14,6 @@ dependencies = [
|
|||
[project.scripts]
|
||||
create-mcp-server = "create_mcp_server:main"
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://modelcontextprotocol.io"
|
||||
Repository = "https://github.com/modelcontextprotocol/create-python-server"
|
||||
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
|
|
@ -39,4 +22,7 @@ build-backend = "hatchling.build"
|
|||
artifacts = ["src/create_mcp_server/template"]
|
||||
|
||||
[tool.uv]
|
||||
dev-dependencies = ["pyright>=1.1.389", "ruff>=0.7.4"]
|
||||
dev-dependencies = [
|
||||
"pyright>=1.1.389",
|
||||
"ruff>=0.7.4",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -5,26 +5,11 @@ import sys
|
|||
from pathlib import Path
|
||||
|
||||
import click
|
||||
import toml
|
||||
from packaging.version import parse
|
||||
|
||||
MIN_UV_VERSION = "0.4.10"
|
||||
|
||||
|
||||
class PyProject:
|
||||
def __init__(self, path: Path):
|
||||
self.data = toml.load(path)
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
return self.data["project"]["name"]
|
||||
|
||||
@property
|
||||
def first_binary(self) -> str | None:
|
||||
scripts = self.data["project"].get("scripts", {})
|
||||
return next(iter(scripts.keys()), None)
|
||||
|
||||
|
||||
def check_uv_version(required_version: str) -> str | None:
|
||||
"""Check if uv is installed and has minimum version"""
|
||||
try:
|
||||
|
|
@ -138,11 +123,7 @@ def copy_template(
|
|||
("README.md.jinja2", "README.md", path),
|
||||
]
|
||||
|
||||
pyproject = PyProject(path / "pyproject.toml")
|
||||
bin_name = pyproject.first_binary
|
||||
|
||||
template_vars = {
|
||||
"binary_name": bin_name,
|
||||
"server_name": name,
|
||||
"server_version": version,
|
||||
"server_description": description,
|
||||
|
|
@ -340,8 +321,6 @@ def main(
|
|||
click.echo("❌ Error: Invalid path. Project creation aborted.", err=True)
|
||||
return 1
|
||||
|
||||
project_path = project_path.resolve()
|
||||
|
||||
create_project(project_path, name, description, version, claudeapp)
|
||||
update_pyproject_settings(project_path, version, description)
|
||||
|
||||
|
|
|
|||
|
|
@ -24,21 +24,13 @@ The server implements one tool:
|
|||
- Takes "name" and "content" as required string arguments
|
||||
- Updates server state and notifies clients of resource changes
|
||||
|
||||
## Configuration
|
||||
## Install
|
||||
|
||||
[TODO: Add configuration details specific to your implementation]
|
||||
|
||||
## Quickstart
|
||||
|
||||
### Install
|
||||
|
||||
#### Claude Desktop
|
||||
### Claude Desktop
|
||||
|
||||
On MacOS: `~/Library/Application\ Support/Claude/claude_desktop_config.json`
|
||||
On Windows: `%APPDATA%/Claude/claude_desktop_config.json`
|
||||
|
||||
<details>
|
||||
<summary>Development/Unpublished Servers Configuration</summary>
|
||||
```
|
||||
"mcpServers": {
|
||||
"{{server_name}}": {
|
||||
|
|
@ -52,68 +44,16 @@ On Windows: `%APPDATA%/Claude/claude_desktop_config.json`
|
|||
}
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Published Servers Configuration</summary>
|
||||
```
|
||||
"mcpServers": {
|
||||
"{{server_name}}": {
|
||||
"command": "uvx",
|
||||
"args": [
|
||||
"{{server_name}}"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
## Development
|
||||
|
||||
### Building and Publishing
|
||||
|
||||
To prepare the package for distribution:
|
||||
|
||||
1. Sync dependencies and update lockfile:
|
||||
```bash
|
||||
uv sync
|
||||
```
|
||||
|
||||
2. Build package distributions:
|
||||
```bash
|
||||
uv build
|
||||
```
|
||||
|
||||
This will create source and wheel distributions in the `dist/` directory.
|
||||
|
||||
3. Publish to PyPI:
|
||||
```bash
|
||||
uv publish
|
||||
```
|
||||
|
||||
Note: You'll need to set PyPI credentials via environment variables or command flags:
|
||||
- Token: `--token` or `UV_PUBLISH_TOKEN`
|
||||
- Or username/password: `--username`/`UV_PUBLISH_USERNAME` and `--password`/`UV_PUBLISH_PASSWORD`
|
||||
|
||||
### Debugging
|
||||
## Develop and Debug
|
||||
|
||||
Since MCP servers run over stdio, debugging can be challenging. For the best debugging
|
||||
experience, we strongly recommend using the [MCP Inspector](https://github.com/modelcontextprotocol/inspector).
|
||||
|
||||
{% if binary_name %}
|
||||
You can launch the MCP Inspector via [`npm`](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) with this command:
|
||||
|
||||
```bash
|
||||
npx @modelcontextprotocol/inspector uv --directory {{server_directory}} run {{binary_name}}
|
||||
npx @modelcontextprotocol/inspector uv --directory {{server_directory}} run {{server_name}}
|
||||
```
|
||||
{% else %}
|
||||
This project does not contain a script to start. However, you can still use the MCP Inspector for debugging. To start the inspector, use the following general command structure:
|
||||
|
||||
```bash
|
||||
npx @modelcontextprotocol/inspector <command-to-run-your-server>
|
||||
```
|
||||
|
||||
Replace `<command-to-run-your-server>` with the specific command you use to start your MCP server.
|
||||
{% endif %}
|
||||
|
||||
Upon launching, the Inspector will display a URL that you can access in your browser to begin debugging.
|
||||
|
|
|
|||
|
|
@ -6,12 +6,30 @@ from mcp.server import NotificationOptions, Server
|
|||
from pydantic import AnyUrl
|
||||
import mcp.server.stdio
|
||||
|
||||
|
||||
class McpServer(Server):
|
||||
"""
|
||||
Example MCP server demonstrating the three core primitives:
|
||||
1. Resources - Simple key-value notes storage accessed via custom URIs
|
||||
2. Prompts - Configurable summarization prompt for notes
|
||||
3. Tools - Ability to add new notes programmatically
|
||||
|
||||
This server shows how to:
|
||||
- Implement resource listing and reading with custom URI schemes
|
||||
- Create parameterized prompts that can access server state
|
||||
- Define tools with JSON schema validation
|
||||
- Send notifications when server state changes
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__("{{server_name}}")
|
||||
|
||||
# Store notes as a simple key-value dict to demonstrate state management
|
||||
notes: dict[str, str] = {}
|
||||
self.notes: dict[str, str] = {}
|
||||
|
||||
server = Server("{{server_name}}")
|
||||
|
||||
@server.list_resources()
|
||||
# RESOURCE HANDLERS
|
||||
# Resources allow clients to discover and read content from the server
|
||||
@self.list_resources()
|
||||
async def handle_list_resources() -> list[types.Resource]:
|
||||
"""
|
||||
List available note resources.
|
||||
|
|
@ -19,15 +37,15 @@ async def handle_list_resources() -> list[types.Resource]:
|
|||
"""
|
||||
return [
|
||||
types.Resource(
|
||||
uri=AnyUrl(f"note://internal/{name}"),
|
||||
uri=AnyUrl(f"note:///{name}"),
|
||||
name=f"Note: {name}",
|
||||
description=f"A simple note named {name}",
|
||||
mimeType="text/plain",
|
||||
)
|
||||
for name in notes
|
||||
for name in self.notes
|
||||
]
|
||||
|
||||
@server.read_resource()
|
||||
@self.read_resource()
|
||||
async def handle_read_resource(uri: AnyUrl) -> str:
|
||||
"""
|
||||
Read a specific note's content by its URI.
|
||||
|
|
@ -39,10 +57,12 @@ async def handle_read_resource(uri: AnyUrl) -> str:
|
|||
name = uri.path
|
||||
if name is not None:
|
||||
name = name.lstrip("/")
|
||||
return notes[name]
|
||||
return self.notes[name]
|
||||
raise ValueError(f"Note not found: {name}")
|
||||
|
||||
@server.list_prompts()
|
||||
# PROMPT HANDLERS
|
||||
# Prompts define templates that can be filled with server state
|
||||
@self.list_prompts()
|
||||
async def handle_list_prompts() -> list[types.Prompt]:
|
||||
"""
|
||||
List available prompts.
|
||||
|
|
@ -62,7 +82,7 @@ async def handle_list_prompts() -> list[types.Prompt]:
|
|||
)
|
||||
]
|
||||
|
||||
@server.get_prompt()
|
||||
@self.get_prompt()
|
||||
async def handle_get_prompt(
|
||||
name: str, arguments: dict[str, str] | None
|
||||
) -> types.GetPromptResult:
|
||||
|
|
@ -86,14 +106,16 @@ async def handle_get_prompt(
|
|||
text=f"Here are the current notes to summarize:{detail_prompt}\n\n"
|
||||
+ "\n".join(
|
||||
f"- {name}: {content}"
|
||||
for name, content in notes.items()
|
||||
for name, content in self.notes.items()
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
@server.list_tools()
|
||||
# TOOL HANDLERS
|
||||
# Tools allow clients to modify server state in controlled ways
|
||||
@self.list_tools()
|
||||
async def handle_list_tools() -> list[types.Tool]:
|
||||
"""
|
||||
List available tools.
|
||||
|
|
@ -114,7 +136,7 @@ async def handle_list_tools() -> list[types.Tool]:
|
|||
)
|
||||
]
|
||||
|
||||
@server.call_tool()
|
||||
@self.call_tool()
|
||||
async def handle_call_tool(
|
||||
name: str, arguments: dict | None
|
||||
) -> list[types.TextContent | types.ImageContent | types.EmbeddedResource]:
|
||||
|
|
@ -135,10 +157,10 @@ async def handle_call_tool(
|
|||
raise ValueError("Missing name or content")
|
||||
|
||||
# Update server state
|
||||
notes[note_name] = content
|
||||
self.notes[note_name] = content
|
||||
|
||||
# Notify clients that resources have changed
|
||||
await server.request_context.session.send_resource_list_changed()
|
||||
await self.request_context.session.send_resource_list_changed()
|
||||
|
||||
return [
|
||||
types.TextContent(
|
||||
|
|
@ -147,7 +169,10 @@ async def handle_call_tool(
|
|||
)
|
||||
]
|
||||
|
||||
|
||||
async def main():
|
||||
server = McpServer()
|
||||
|
||||
# Run the server using stdin/stdout streams
|
||||
async with mcp.server.stdio.stdio_server() as (read_stream, write_stream):
|
||||
await server.run(
|
||||
|
|
|
|||
2
uv.lock
generated
2
uv.lock
generated
|
|
@ -24,7 +24,7 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "create-mcp-server"
|
||||
version = "1.0.4.dev0"
|
||||
version = "1.0.1.dev0"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "click" },
|
||||
|
|
|
|||
Loading…
Reference in a new issue