mcp-starter-kit

mcp-starter-kit

A scaffolding toolkit to generate clean architecture MCP servers with tool scaffolding and function inspection.

Category
访问服务器

README

mcp-scaffold

A Clean Architecture MCP Server Scaffolding Toolkit.

Generate production-ready MCP server projects, add new tools incrementally, and derive tool scaffolding from ordinary Python functions — while keeping your application code independent of FastMCP or any other MCP SDK.


Table of Contents

  1. Project purpose
  2. Suitable use cases
  3. Unsuitable use cases
  4. Three-minute quick start
  5. Installation
  6. CLI command reference
  7. Exit-code policy
  8. Script reference
  9. Creating the first MCP server
  10. Creating the first tool
  11. Inspect-driven generation
  12. Supported and unsupported Python types
  13. Generated template structure
  14. Generated versus handwritten files
  15. SDK adapter boundary
  16. Architecture and diagrams
  17. Testing
  18. Troubleshooting
  19. Known limitations
  20. Roadmap
  21. Contributing

Project purpose

mcp-scaffold creates and evolves MCP server projects so that you can focus on business logic instead of wiring. It generates a clean-architecture project skeleton, adds individual tool stubs, and can derive scaffolding directly from an existing Python function signature using inspect.

The generated code is directly importable, testable, lintable, and type-checkable without modification.


Suitable use cases

  • Bootstrapping a new Python MCP server from zero.
  • Adding a new tool to an existing generated project without touching unrelated files.
  • Exploring what MCP metadata an existing Python function would produce.
  • Enforcing a consistent project layout across a team.
  • Demonstrating Clean Architecture with a real, working project.

Unsuitable use cases

  • Web-based administration UI.
  • Cloud deployment automation.
  • Authentication or authorization systems.
  • Plugin marketplaces or dynamic runtime plugin installation.
  • Distributed service discovery.
  • Database-backed template registries.
  • Automatic module scanning across arbitrary directories.

Three-minute quick start

# 1. Install
pip install -e ".[dev]"

# 2. Create a project
mcp-scaffold new-project my-server

# 3. Enter the project and run its tests
cd my-server
python -m pytest tests/ -v

# 4. Add a tool
mcp-scaffold new-tool get-user-info --project-dir .

# 5. Validate the project structure
mcp-scaffold validate .

Installation

From source (development)

git clone https://github.com/j3camp/mcp-starter-kit.git
cd mcp-starter-kit
pip install -e ".[dev]"

Runtime only

pip install -e .

Required runtime dependencies

Package Purpose
pydantic>=2.0 Domain models and validation
typer>=0.12 CLI framework
fastmcp>=2.0 MCP SDK (infrastructure only)

Optional development dependencies

Package Purpose
pytest>=8.0 Test runner
pytest-asyncio>=0.23 Async test support
ruff>=0.4 Linter and formatter
mypy>=1.10 Static type checker

CLI command reference

mcp-scaffold new-project <project-name>

Create a new MCP server project.

mcp-scaffold new-project my-service
mcp-scaffold new-project my-service --output-dir /projects
mcp-scaffold new-project my-service --dry-run
mcp-scaffold new-project my-service --force
Option Description
--output-dir, -o Parent directory (default: .)
--force Overwrite existing files
--dry-run Show what would be created without writing

mcp-scaffold new-tool <tool-name>

Add a new tool stub to an existing generated project.

mcp-scaffold new-tool get-user-info
mcp-scaffold new-tool get-user-info --project-dir ./my-service
mcp-scaffold new-tool get-user-info --dry-run
mcp-scaffold new-tool get-user-info --force
Option Description
--project-dir, -p Project root (default: .)
--force Overwrite existing files
--dry-run Show what would be created without writing

Creates:

  • src/<pkg>/application/tools/<snake>.py — tool function stub
  • src/<pkg>/application/tools/<snake>_registration.py — registration helper
  • tests/test_<snake>.py — unit test stub

mcp-scaffold inspect <module:function>

Inspect a Python function and print normalized MCP tool metadata.

mcp-scaffold inspect my_module.tools:get_user_info
mcp-scaffold inspect my_module.tools:get_user_info --base-path .
Option Description
--base-path Directory prepended to sys.path before import (default: .)

Example output:

Function:    get_user_info
Tool name:   get-user-info
Description: Return public information for a user.
Mode:        sync
Returns:     UserInfo
Parameters:
  user_id: str — required

mcp-scaffold generate <module:function>

Inspect a Python function and generate tool scaffolding from its signature.

mcp-scaffold generate my_module.tools:get_user_info --project-dir ./my-service
mcp-scaffold generate my_module.tools:get_user_info --project-dir ./my-service --dry-run
Option Description
--project-dir, -p Target project root (default: .)
--base-path Directory prepended to sys.path
--force Overwrite existing files
--dry-run Show what would be created without writing

mcp-scaffold validate [path]

Validate the structure of a generated project.

mcp-scaffold validate .
mcp-scaffold validate ./my-service

Checks:

  • pyproject.toml exists
  • src/ directory exists
  • At least one Python package under src/
  • tests/ directory exists

Exit-code policy

Code Meaning
0 Success
1 General execution failure
2 Invalid command usage or arguments
3 Target already exists or overwrite denied
4 Unsupported inspected type or invalid source function
5 Generated project validation failure

All commands return non-zero codes on failure and print human-readable error messages to stderr. Python tracebacks are suppressed for expected user errors.


Script reference

Script (Unix) Script (Windows) Purpose
scripts/setup.sh scripts/setup.bat Install dependencies
scripts/test.sh scripts/test.bat Run test suite
scripts/lint.sh scripts/lint.bat Run linter
scripts/typecheck.sh scripts/typecheck.bat Run type checker
scripts/run.sh scripts/run.bat Start the MCP server
scripts/validate-template.sh scripts/validate-template.bat Validate project structure

Scripts contain no business or generation logic. They delegate entirely to Python tools and stop on any non-zero exit code.


Creating the first MCP server

# Generate the project
mcp-scaffold new-project hello-mcp --output-dir /tmp

# Inspect the generated layout
ls /tmp/hello-mcp/src/hello_mcp/

# Run the generated tests (no installation required)
cd /tmp/hello-mcp
python -m pytest tests/ -v

# Start the server
python -m hello_mcp

The generated server.py includes a composition root:

def create_server() -> fastmcp.FastMCP:
    """Create and configure the MCP server."""
    mcp = fastmcp.FastMCP("mcp-server")
    adapter = FastMCPAdapter(mcp)
    adapter.register_tool(name="echo", description="...", handler=echo)
    return mcp

Importing this module does not start the server. Only calling create_server() and then server.run() does.


Creating the first tool

mcp-scaffold new-tool search-products --project-dir /tmp/hello-mcp

This generates:

src/hello_mcp/application/tools/search_products.py
src/hello_mcp/application/tools/search_products_registration.py
tests/test_search_products.py

Edit the stub to implement your business logic, then call the registration helper from server.py:

from hello_mcp.application.tools.search_products_registration import register_search_products

def create_server() -> fastmcp.FastMCP:
    mcp = fastmcp.FastMCP("mcp-server")
    adapter = FastMCPAdapter(mcp)
    register_search_products(adapter)
    return mcp

Inspect-driven generation

Given an ordinary Python function:

# my_service/tools.py
from pydantic import BaseModel

class UserInfo(BaseModel):
    user_id: str
    display_name: str
    active: bool

def get_user_info(user_id: str) -> UserInfo:
    """Return public information for a user."""
    raise NotImplementedError

Inspect it:

mcp-scaffold inspect my_service.tools:get_user_info --base-path .

Output:

Function:    get_user_info
Tool name:   get-user-info
Description: Return public information for a user.
Mode:        sync
Returns:     UserInfo
Parameters:
  user_id: str — required

Generate scaffolding:

mcp-scaffold generate my_service.tools:get_user_info \
  --project-dir ./my-server --base-path .

The inspect pipeline runs in explicit stages:

Python function
  -> discovery         validate callable is public and annotated
  -> metadata          extract name, docstring, parameters, return type
  -> type mapping      validate each annotation
  -> normalization     produce ToolMetadata
  -> rendering model   plan files to create
  -> generated files   write to disk (skipped in --dry-run)

Supported and unsupported Python types

Supported

Python type Example
str x: str
int x: int
float x: float
bool x: bool
None (return only) -> None
Optional[T] x: Optional[str]
Literal[...] x: Literal["a", "b"]
list[T] x: list[str]
dict[K, V] x: dict[str, int]
Pydantic BaseModel subclass x: UserInfo

Unsupported

Python type Behaviour
Union[A, B] (non-nullable) Raises UnsupportedTypeError
set, tuple, frozenset Raises UnsupportedTypeError
*args, **kwargs Validation error recorded in metadata
Missing type annotation Recorded as unsupported parameter
Missing return annotation Validation error (override with allow_missing_return=True)
Private functions (_name) Raises InspectError (override with allow_private=True)
Classes Raises InspectError

Unknown types never silently fall back to str.


Generated template structure

<project-name>/
├── pyproject.toml               # build config, pytest, ruff, mypy
├── README.md
├── .gitignore
├── src/
│   └── <pkg>/
│       ├── __init__.py
│       ├── __main__.py          # entry point
│       ├── server.py            # composition root (create_server)
│       ├── domain/
│       │   ├── __init__.py
│       │   └── ports.py         # MCPServerPort protocol
│       ├── application/
│       │   ├── __init__.py
│       │   └── tools/
│       │       ├── __init__.py
│       │       └── example_tool.py
│       └── infrastructure/
│           ├── __init__.py
│           └── mcp_adapter.py   # FastMCP adapter
├── tests/
│   ├── __init__.py
│   ├── test_example_tool.py
│   └── test_server.py
└── scripts/
    ├── run.sh / run.bat
    ├── test.sh / test.bat
    ├── lint.sh / lint.bat
    └── typecheck.sh / typecheck.bat

Generated versus handwritten files

File Type Notes
pyproject.toml Generated Regenerated by --force
README.md Generated Regenerated by --force
.gitignore Generated Regenerated by --force
src/<pkg>/__init__.py Generated Regenerated by --force
src/<pkg>/domain/ports.py Generated Regenerated by --force
src/<pkg>/infrastructure/mcp_adapter.py Generated Regenerated by --force
src/<pkg>/server.py Generated (composition root) Edit to add tool registrations
src/<pkg>/application/tools/example_tool.py Generated Replace with real logic
src/<pkg>/application/tools/<tool>.py Generated stub Handwritten after generation
src/<pkg>/application/tools/<tool>_registration.py Generated Safe to edit registration call
tests/test_*.py Generated stub Handwritten after generation

Files marked Handwritten after generation are created once and never overwritten unless --force is supplied.


SDK adapter boundary

mcp-scaffold applies Dependency Inversion between the application layer and the MCP SDK.

application/domain
    MCPServerPort  <-- Protocol owned by the inner layer
                       (no fastmcp import)

infrastructure
    FastMCPAdapter  --> implements MCPServerPort
                        (only file that imports fastmcp)

The composition root (server.py) wires them together:

import fastmcp
from <pkg>.infrastructure.mcp_adapter import FastMCPAdapter

def create_server() -> fastmcp.FastMCP:
    mcp = fastmcp.FastMCP("name")
    adapter = FastMCPAdapter(mcp)
    adapter.register_tool(...)
    return mcp

To replace FastMCP with another SDK:

  1. Create a new adapter class that implements register_tool(*, name, description, handler).
  2. Replace FastMCPAdapter in server.py with your adapter.
  3. The application layer requires zero changes.

Architecture and diagrams

Overall architecture

graph TD
    CLI["CLI (Typer)"]
    App["Application Services"]
    Domain["Domain Models + Ports"]
    Infra["Infrastructure Adapters"]

    CLI --> App
    App --> Domain
    Infra --> Domain
    CLI --> Infra

Dependency flow

graph LR
    CLI --> InspectSvc["inspect_service"]
    CLI --> GenerateSvc["generate_service"]
    CLI --> ValidateSvc["validate_service"]
    InspectSvc --> Models["domain/models.py"]
    InspectSvc --> TypeMapper["infrastructure/type_mapper.py"]
    GenerateSvc --> Models
    GenerateSvc --> Renderer["infrastructure/renderer.py"]
    FastMCPAdapter["infrastructure/fastmcp_adapter.py"] --> Ports["domain/ports.py"]

New-project generation flow

flowchart TD
    A["mcp-scaffold new-project my-server"] --> B["generate_project()"]
    B --> C["Renderer.stage() all files"]
    C --> D{"dry_run?"}
    D -- yes --> E["Return GenerationPlan (no writes)"]
    D -- no --> F["Conflict check"]
    F --> G{"conflict?"}
    G -- "yes, no --force" --> H["RenderConflictError (exit 3)"]
    G -- "no or --force" --> I["Write files to disk"]
    I --> J["Return GenerationPlan"]

New-tool generation flow

flowchart TD
    A["mcp-scaffold new-tool get-user"] --> B["generate_tool()"]
    B --> C["_detect_package()"]
    C --> D["Stage tool stub"]
    D --> E["Stage registration helper"]
    E --> F["Stage test stub"]
    F --> G["Renderer.commit()"]

Tool request sequence

sequenceDiagram
    participant Client
    participant FastMCP
    participant Adapter as FastMCPAdapter
    participant Tool as Tool Function

    Client->>FastMCP: call tool "echo"
    FastMCP->>Tool: invoke handler
    Tool-->>FastMCP: return result
    FastMCP-->>Client: tool result

Inspect-generation sequence

sequenceDiagram
    participant User
    participant CLI
    participant InspectSvc as inspect_service
    participant TypeMapper as type_mapper
    participant GenerateSvc as generate_service
    participant Renderer

    User->>CLI: mcp-scaffold generate module:fn
    CLI->>InspectSvc: inspect_from_string(spec)
    InspectSvc->>TypeMapper: map_type(annotation)
    TypeMapper-->>InspectSvc: mapped type string
    InspectSvc-->>CLI: ToolMetadata
    CLI->>GenerateSvc: generate_from_metadata(metadata)
    GenerateSvc->>Renderer: stage files
    Renderer-->>GenerateSvc: GenerationPlan
    GenerateSvc-->>CLI: GenerationPlan
    CLI-->>User: files created

CLI-to-Python-generator call flow

flowchart LR
    CLI["cli/main.py"] --> GenSvc["generate_service.py"]
    CLI --> InspSvc["inspect_service.py"]
    CLI --> ValSvc["validate_service.py"]
    GenSvc --> Renderer["renderer.py"]
    InspSvc --> TypeMapper["type_mapper.py"]

Testing

Run the full validation suite:

python -m pytest tests/ -v
python -m ruff check .
python -m mypy src tests
mcp-scaffold validate .

Test areas covered

Area Tests
Inspect discovery Public/async/private functions, optional params, missing annotations
Type mapping Primitives, Optional, Literal, list, dict, Pydantic, unsupported types
Renderer Stage/commit, dry-run, conflict detection, force, LF line endings
Generate service Project generation, dry-run, conflict, tool addition
CLI Help, new-project, new-tool, validate, dry-run, force, error codes
Adapter contract RecordingAdapter sync/async, multiple tools, name propagation

Generated project validation

After mcp-scaffold new-project test-server:

cd test-server
python -m pytest tests/ -v   # should pass with no installation

Troubleshooting

ModuleNotFoundError: No module named '<pkg>' in generated project tests

The generated pyproject.toml sets pythonpath = ["src"] in pytest options. If you see this error, ensure your pytest version supports pythonpath (pytest >= 7.0).

TypeError: FastMCP.add_tool() got an unexpected keyword argument 'name'

This occurs with fastmcp >= 2.x. The adapter uses Tool.from_function(fn, name=..., description=...). Ensure you are using the adapter generated by this version of mcp-scaffold.

RenderConflictError: File already exists

Use --force to overwrite, or delete the conflicting file manually.

Unsupported type error during inspect

Check that all parameter and return type annotations use supported types (see Supported and unsupported Python types). Union types with more than two members (excluding None) are not supported.


Known limitations

  • mcp-scaffold generate does not automatically update server.py to register the new tool. You must add the register_<tool>(adapter) call manually.
  • Windows batch scripts require cmd.exe and do not support PowerShell-only features.
  • Paths containing ! may cause issues with set -e in some sh implementations.
  • Forward references (string annotations) are resolved with typing.get_type_hints; they may fail if the referenced type is not importable at inspect time.
  • The validate command checks structural presence only; it does not run tests or verify import correctness.

Roadmap

  • Automatic server.py update after new-tool.
  • Generated project type-check and import smoke-test in validate.
  • Support for Union[A, B] (non-nullable) with explicit opt-in.
  • Interactive prompts for project metadata.
  • Optional conftest.py and fixture generation.
  • GitHub Actions CI template generation.

Contributing

  1. Fork the repository and create a feature branch.

  2. Install development dependencies: pip install -e ".[dev]".

  3. Run checks before opening a PR:

    python -m pytest tests/ -v
    python -m ruff check .
    python -m mypy src tests
    
  4. Keep changes focused: one concern per PR.

  5. Add or update tests for every changed behaviour.

  6. Write commit messages in English.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选