Compare commits

...
Author SHA1 Message Date
Justin Spahr-Summers
b07744de4f
Merge pull request #4 from modelcontextprotocol/justin/contributing
Some checks failed
Main branch checks / checks (push) Has been cancelled
Create CONTRIBUTING.md
2024-11-25 09:22:24 -06:00
Justin Spahr-Summers
40083e7301
Create CONTRIBUTING.md 2024-11-25 09:10:40 -06:00
David Soria Parra
7d921b9302
bump 2024-11-24 21:04:36 +00:00
David Soria Parra
446072a2c0
1.0.5 2024-11-24 21:04:03 +00:00
David Soria Parra
35730e4a16
Move template to flat app pattern 2024-11-24 21:02:02 +00:00
David Soria Parra
e5c6c63923
update readme 2024-11-21 21:28:06 +00:00
David Soria Parra
a40b42b534
bump 2024-11-21 14:50:56 +00:00
David Soria Parra
b56d4c5186
v1.0.4 2024-11-21 14:50:23 +00:00
David Soria Parra
6559948d34
Resolve the path before using it 2024-11-21 14:50:08 +00:00
David Soria Parra
6e43668dba
bump 2024-11-21 12:00:36 +00:00
6 changed files with 228 additions and 159 deletions

43
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,43 @@
# 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.

View file

@ -1,6 +1,6 @@
[project] [project]
name = "create-mcp-server" name = "create-mcp-server"
version = "1.0.3" version = "1.0.6.dev0"
description = "Create an Model Context Protocol server project from a template." description = "Create an Model Context Protocol server project from a template."
readme = "README.md" readme = "README.md"
requires-python = ">=3.10" requires-python = ">=3.10"

View file

@ -24,6 +24,7 @@ class PyProject:
scripts = self.data["project"].get("scripts", {}) scripts = self.data["project"].get("scripts", {})
return next(iter(scripts.keys()), None) return next(iter(scripts.keys()), None)
def check_uv_version(required_version: str) -> str | None: def check_uv_version(required_version: str) -> str | None:
"""Check if uv is installed and has minimum version""" """Check if uv is installed and has minimum version"""
try: try:
@ -255,8 +256,6 @@ def check_package_name(name: str) -> bool:
return True return True
@click.command() @click.command()
@click.option( @click.option(
"--path", "--path",
@ -341,6 +340,8 @@ def main(
click.echo("❌ Error: Invalid path. Project creation aborted.", err=True) click.echo("❌ Error: Invalid path. Project creation aborted.", err=True)
return 1 return 1
project_path = project_path.resolve()
create_project(project_path, name, description, version, claudeapp) create_project(project_path, name, description, version, claudeapp)
update_pyproject_settings(project_path, version, description) update_pyproject_settings(project_path, version, description)

View file

@ -24,14 +24,22 @@ The server implements one tool:
- Takes "name" and "content" as required string arguments - Takes "name" and "content" as required string arguments
- Updates server state and notifies clients of resource changes - Updates server state and notifies clients of resource changes
## Install ## Configuration
### Claude Desktop [TODO: Add configuration details specific to your implementation]
## Quickstart
### Install
#### Claude Desktop
On MacOS: `~/Library/Application\ Support/Claude/claude_desktop_config.json` On MacOS: `~/Library/Application\ Support/Claude/claude_desktop_config.json`
On Windows: `%APPDATA%/Claude/claude_desktop_config.json` On Windows: `%APPDATA%/Claude/claude_desktop_config.json`
``` <details>
<summary>Development/Unpublished Servers Configuration</summary>
```
"mcpServers": { "mcpServers": {
"{{server_name}}": { "{{server_name}}": {
"command": "uv", "command": "uv",
@ -43,9 +51,51 @@ 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
``` ```
## Develop and Debug 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
Since MCP servers run over stdio, debugging can be challenging. For the best debugging 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). experience, we strongly recommend using the [MCP Inspector](https://github.com/modelcontextprotocol/inspector).

View file

@ -6,47 +6,29 @@ from mcp.server import NotificationOptions, Server
from pydantic import AnyUrl from pydantic import AnyUrl
import mcp.server.stdio import mcp.server.stdio
# Store notes as a simple key-value dict to demonstrate state management
notes: dict[str, str] = {}
class McpServer(Server): server = Server("{{server_name}}")
"""
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: @server.list_resources()
- Implement resource listing and reading with custom URI schemes async def handle_list_resources() -> list[types.Resource]:
- 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
self.notes: dict[str, str] = {}
# 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. List available note resources.
Each note is exposed as a resource with a custom note:// URI scheme. Each note is exposed as a resource with a custom note:// URI scheme.
""" """
return [ return [
types.Resource( types.Resource(
uri=AnyUrl(f"note:///{name}"), uri=AnyUrl(f"note://internal/{name}"),
name=f"Note: {name}", name=f"Note: {name}",
description=f"A simple note named {name}", description=f"A simple note named {name}",
mimeType="text/plain", mimeType="text/plain",
) )
for name in self.notes for name in notes
] ]
@self.read_resource() @server.read_resource()
async def handle_read_resource(uri: AnyUrl) -> str: async def handle_read_resource(uri: AnyUrl) -> str:
""" """
Read a specific note's content by its URI. Read a specific note's content by its URI.
The note name is extracted from the URI host component. The note name is extracted from the URI host component.
@ -57,13 +39,11 @@ class McpServer(Server):
name = uri.path name = uri.path
if name is not None: if name is not None:
name = name.lstrip("/") name = name.lstrip("/")
return self.notes[name] return notes[name]
raise ValueError(f"Note not found: {name}") raise ValueError(f"Note not found: {name}")
# PROMPT HANDLERS @server.list_prompts()
# Prompts define templates that can be filled with server state async def handle_list_prompts() -> list[types.Prompt]:
@self.list_prompts()
async def handle_list_prompts() -> list[types.Prompt]:
""" """
List available prompts. List available prompts.
Each prompt can have optional arguments to customize its behavior. Each prompt can have optional arguments to customize its behavior.
@ -82,10 +62,10 @@ class McpServer(Server):
) )
] ]
@self.get_prompt() @server.get_prompt()
async def handle_get_prompt( async def handle_get_prompt(
name: str, arguments: dict[str, str] | None name: str, arguments: dict[str, str] | None
) -> types.GetPromptResult: ) -> types.GetPromptResult:
""" """
Generate a prompt by combining arguments with server state. Generate a prompt by combining arguments with server state.
The prompt includes all current notes and can be customized via arguments. The prompt includes all current notes and can be customized via arguments.
@ -106,17 +86,15 @@ class McpServer(Server):
text=f"Here are the current notes to summarize:{detail_prompt}\n\n" text=f"Here are the current notes to summarize:{detail_prompt}\n\n"
+ "\n".join( + "\n".join(
f"- {name}: {content}" f"- {name}: {content}"
for name, content in self.notes.items() for name, content in notes.items()
), ),
), ),
) )
], ],
) )
# TOOL HANDLERS @server.list_tools()
# Tools allow clients to modify server state in controlled ways async def handle_list_tools() -> list[types.Tool]:
@self.list_tools()
async def handle_list_tools() -> list[types.Tool]:
""" """
List available tools. List available tools.
Each tool specifies its arguments using JSON Schema validation. Each tool specifies its arguments using JSON Schema validation.
@ -136,10 +114,10 @@ class McpServer(Server):
) )
] ]
@self.call_tool() @server.call_tool()
async def handle_call_tool( async def handle_call_tool(
name: str, arguments: dict | None name: str, arguments: dict | None
) -> list[types.TextContent | types.ImageContent | types.EmbeddedResource]: ) -> list[types.TextContent | types.ImageContent | types.EmbeddedResource]:
""" """
Handle tool execution requests. Handle tool execution requests.
Tools can modify server state and notify clients of changes. Tools can modify server state and notify clients of changes.
@ -157,10 +135,10 @@ class McpServer(Server):
raise ValueError("Missing name or content") raise ValueError("Missing name or content")
# Update server state # Update server state
self.notes[note_name] = content notes[note_name] = content
# Notify clients that resources have changed # Notify clients that resources have changed
await self.request_context.session.send_resource_list_changed() await server.request_context.session.send_resource_list_changed()
return [ return [
types.TextContent( types.TextContent(
@ -169,10 +147,7 @@ class McpServer(Server):
) )
] ]
async def main(): async def main():
server = McpServer()
# Run the server using stdin/stdout streams # Run the server using stdin/stdout streams
async with mcp.server.stdio.stdio_server() as (read_stream, write_stream): async with mcp.server.stdio.stdio_server() as (read_stream, write_stream):
await server.run( await server.run(

2
uv.lock generated
View file

@ -24,7 +24,7 @@ wheels = [
[[package]] [[package]]
name = "create-mcp-server" name = "create-mcp-server"
version = "1.0.3.dev0" version = "1.0.4.dev0"
source = { editable = "." } source = { editable = "." }
dependencies = [ dependencies = [
{ name = "click" }, { name = "click" },