From mcp-create
Use when building a new MCP server from the Asgard MCP Template. Guides the complete workflow from reading service documentation through design, implementation, testing, and PyPI publishing.
How this skill is triggered — by the user, by Claude, or both
Slash command
/mcp-create:mcp-createThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
A structured workflow for building production-ready MCP (Model Context Protocol) servers from a template project to a published PyPI package. Based on real-world experience building the Asgard MCP server ecosystem.
A structured workflow for building production-ready MCP (Model Context Protocol) servers from a template project to a published PyPI package. Based on real-world experience building the Asgard MCP server ecosystem.
reference/ folder containing the service's documentation (API specs, SDK docs, protocol guides, or any third-party service documentation)Every MCP server project starts with two things:
The template -- An Asgard MCP server template project (already cloned/set up as the working directory). Contains pluggable auth modules, connectors, sample tools, project scaffolding, PyPI publish workflow, and an interactive init script.
The reference material -- Located in the project's reference/ folder (gitignored -- not committed to the repo). This is NOT limited to REST API docs. It can be:
The reference material determines the entire architecture. Read it first, then decide how to adapt the template.
The process follows 7 phases:
Phase 0: Understand the Template --> Know what's available to keep, modify, or delete
Phase 1: Read & Understand Ref --> Complete knowledge of the service's interface
Phase 2: Design --> Architecture decisions, design spec
Phase 3: Plan --> Implementation plan with tasks
Phase 4: Implement --> Working code, tests, docs
Phase 5: Verify & Fix --> All tests passing, template leftovers cleaned
Phase 6: Publish --> PyPI package + GitHub release
Goal: Know what the template provides so you can make informed decisions about what to keep, delete, or replace.
Template Structure:
├── app.py # FastMCP singleton (ALWAYS modify: rename service)
├── mcp_server.py # Entry point, tool imports (ALWAYS modify: change import)
├── config/settings.py # Endpoints, URL builder, auth delegation (ALWAYS rewrite)
├── auth/ # Pluggable auth modules (PICK ONE or create custom)
│ ├── bearer.py # Bearer token
│ ├── api_key.py # API key (header or query)
│ ├── oauth2.py # OAuth 2.0 client credentials
│ └── none.py # No auth (public APIs)
├── connectors/ # Data source connectors (PICK ONE or create custom)
│ ├── rest_client.py # HTTP REST with retry, 3 pagination modes
│ ├── rss_client.py # RSS/Atom feed parser
│ ├── scraper_client.py # Web scraper with BeautifulSoup
│ ├── mqtt_client.py # MQTT for IoT/industrial
│ └── graphql_client.py # GraphQL with Relay cursor pagination
├── tools/sample_tools.py # Example tools (ALWAYS delete & replace)
├── pyproject.toml # Package metadata, hatchling build (ALWAYS modify)
├── .env.example # Env var template (ALWAYS rewrite)
├── .mcp.json # Claude Code config (ALWAYS rewrite)
├── .github/workflows/publish.yml # PyPI publish workflow (ALWAYS update)
├── CONTRIBUTING.md # Contribution guide (update service name + connector import)
├── scripts/init.py # Interactive initializer (replaces {service} placeholders)
├── scripts/auth/test_connection.py # Connection test (ALWAYS rewrite)
├── tests/test_all_tools.py # E2E tests (ALWAYS rewrite)
└── reference/ # Service docs (gitignored, NOT committed)
Know what the template already provides for publishing:
pyproject.toml uses hatchling build backend with basic metadata -- add keywords, classifiers, [project.urls] for your service.github/workflows/publish.yml is ready with trusted publisher setup (OIDC id-token: write)README.md and README.zh-TW.md are template READMEs -- you'll rewrite them for your service, adding shield badges, install methods (PyPI, uvx, source), and client configs (Claude Desktop, Claude Code, Cursor)Know the init script:
scripts/init.py interactively replaces {service} placeholders across app.py, .mcp.json, pyproject.toml, and READMEs{service} placeholders must be replacedKnow the Pydantic FieldInfo pitfall:
Field(default=None) and are called directly (not via MCP protocol), defaults remain as FieldInfo objects, not None_val() helper to handle this -- see "Common Pitfalls" in Phase 4A mental inventory of the template: what exists, what patterns to follow, what to delete.
Goal: Build a complete mental model of the service before writing any code.
Locate reference material in the project's reference/ folder
reference/ is in .gitignore -- these files stay local, not committedRead EVERYTHING -- Do not skim. For PDFs, read in 20-page batches. For web docs, follow all linked pages.
Ask for test credentials -- After reading the docs, ask the user for test credentials immediately. You'll need them for connection testing in Phase 4-5. Common credentials:
Classify the service type -- This determines your architecture:
| Service Type | Template Modules to Use | Example |
|---|---|---|
| Standard REST API | rest_client.py + existing auth module | Shopline, Stripe |
| REST with custom encryption | Custom connector + custom auth | ezPay (AES-256-CBC) |
| SOAP/XML web service | Custom connector | Government services, legacy ERP |
| RSS/Atom feeds | rss_client.py + none.py | News aggregation |
| Web scraping | scraper_client.py + none.py | Sites without APIs |
| MQTT/IoT | mqtt_client.py | Sensor data, industrial |
| GraphQL | graphql_client.py + auth module | Modern platforms |
| SDK/Library wrapper | Custom connector (call SDK) | Cloud services with Python SDK |
| File/Data processing | No connector needed | Local file transformation |
For APIs (REST, SOAP, GraphQL):
| Detail | What to capture |
|---|---|
| Endpoints | URL paths, HTTP methods, test vs production base URLs |
| Authentication | Bearer, API key, OAuth, custom encryption, certificates |
| Request format | JSON, form POST, XML, multipart, custom encoding |
| Parameters | Per-endpoint: name, type, required/optional, constraints |
| Response format | Success/error shapes, nested parsing |
| Error codes | All codes and meanings |
| Rate limits | Requests per second/minute, pagination |
| Encryption/signing | AES, HMAC, signatures, checksums |
For non-API services (SDK, protocol, scraping):
| Detail | What to capture |
|---|---|
| Connection method | How to connect (TCP, WebSocket, SDK init, URL pattern) |
| Authentication | Credentials, certificates, tokens |
| Operations | What actions are available (read, write, subscribe) |
| Data format | How data is structured (JSON, XML, binary, HTML) |
| Error handling | How errors are reported (exceptions, status codes, error events) |
| Dependencies | Required libraries or SDKs |
A clear understanding of:
Goal: Decide architecture and get user approval before coding.
Map reference findings to template structure:
Propose 2-3 approaches with trade-offs:
| Approach | When to use |
|---|---|
| Keep template connector | Service is standard REST with standard auth |
| Minimal custom connector | Service has unique protocol (encryption, non-REST, SDK) |
| Hybrid | Standard REST but with custom auth or pre/post processing |
Present the design covering:
Write design spec to docs/superpowers/specs/YYYY-MM-DD-<name>-design.md
Connector choice decision tree:
Is it a standard REST API with JSON body?
├── Yes -> Does it use standard auth (Bearer/API key/OAuth)?
│ ├── Yes -> Keep rest_client.py + matching auth module
│ └── No -> Keep rest_client.py + create custom auth module
└── No -> What is it?
├── REST with custom encoding (form POST, encryption) -> Custom connector
├── GraphQL -> Keep graphql_client.py
├── RSS/Atom -> Keep rss_client.py
├── Web scraping -> Keep scraper_client.py
├── MQTT/IoT -> Keep mqtt_client.py
└── SDK/Protocol/Other -> Custom connector
Tool granularity: Generally 1 tool per user-facing operation. Group related API calls into one tool if they always happen together (e.g., "get token then fetch data" = 1 tool).
Optional field handling: Some APIs expect ALL fields present (empty string for unused ones). Others want unused fields omitted. Check sample code in the reference docs. Three common patterns:
"Field": _val(param) -- sends "" if None (API wants all fields)Goal: Break implementation into small, independent tasks with exact file paths and code.
A typical MCP server has these tasks:
Task 1: Clean up template (delete unused files, run scripts/init.py or manually replace {service})
Task 2: Update project config (pyproject.toml, app.py, mcp_server.py, .env.example, .mcp.json)
Task 3: Implement auth module (if custom auth needed)
Task 4: Implement config/settings.py (endpoints/config, env vars)
Task 5: Implement connector (service client)
Task 6: Implement MCP tools (@mcp.tool() functions)
Task 7: Test connection script
Task 8: E2E tests for all tools
Task 9: Documentation (README.md, README.zh-TW.md, CLAUDE.md, CONTRIBUTING.md, CHANGELOG.md)
Task 10: Final cleanup & verification
Task 1 ──┐
Task 2 ──┤ (independent, can run in parallel)
Task 4 ──┘
↓
Task 3 ──→ Task 5 ──→ Task 6 ──→ Task 7 + Task 8 (parallel)
↓
Task 9 ──→ Task 10
Update ALL of these (not just READMEs):
README.md -- Rewrite for your service: add badges, features, install methods, usage examplesREADME.zh-TW.md -- Same in Traditional ChineseCLAUDE.md -- Update architecture diagram, patterns, conventionsCONTRIBUTING.md -- Replace {service}, update connector import exampleCHANGELOG.md -- Reset with 0.1.0 initial releaseThis is critical. After implementation, audit the project for:
{service} placeholders left in any filerest_client, bearer)CONTRIBUTING.md still showing template example codeSave to docs/superpowers/plans/YYYY-MM-DD-<name>.md with:
Goal: Execute the plan task by task, testing as you go.
Parallel-safe tasks (dispatch simultaneously):
Sequential tasks:
FastMCP not MCPServer -- The template's app.py uses FastMCP:
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("mcp-your-service")
If you see MCPServer anywhere, it's wrong. The mcp package only exports FastMCP.
Pydantic FieldInfo in direct calls -- When MCP tool functions use Field(default=None) and are called directly (not via MCP protocol), defaults remain as FieldInfo objects, not None. Add this helper to your tools file:
from pydantic.fields import FieldInfo
def _val(v, default=""):
"""Resolve a parameter value, handling FieldInfo defaults from direct calls."""
if v is None or isinstance(v, FieldInfo):
return default
return v
Use it everywhere you build parameter dicts: "Field": _val(param).
Empty string vs omitted fields -- Some APIs require ALL fields present as empty strings. Others reject empty strings. Test with the actual service to determine behavior. Three common patterns:
"Field": _val(param) -- sends "" if None (API wants all fields)pyproject.toml build backend -- Template uses hatchling (hatchling.build). Don't change it to setuptools or another backend. The wheel config uses [tool.hatch.build.targets.wheel] with packages and force-include directives.
Python bytecode cache -- After editing files, clear __pycache__ if tests show stale behavior:
find . -path ./.venv -prune -o -name "__pycache__" -type d -exec rm -rf {} +
Test account setup -- Some services need more than just credentials:
If tests return unexpected errors, ask the user to verify their test account is fully configured.
Always test against the real service (test environment) during development. Real responses reveal:
Goal: All tests pass, all edge cases handled, no template leftovers.
[ ] Auth/crypto unit tests pass (if applicable)
[ ] Connection test passes (credentials + connectivity)
[ ] All tool E2E tests pass (no Python exceptions)
[ ] All tools get valid responses (even errors prove connectivity)
[ ] At least one tool returns SUCCESS with real data
[ ] MCP server starts without errors
[ ] Git working tree is clean
After all tests pass, search the entire project for leftovers:
# Search for template placeholders
grep -r "{service}" --include="*.py" --include="*.md" --include="*.json" --include="*.toml" .
# Search for references to deleted modules
grep -r "rest_client\|rss_client\|scraper_client\|mqtt_client\|graphql_client" --include="*.py" --include="*.md" .
grep -r "bearer\|api_key\|oauth2\|from auth.none" --include="*.py" --include="*.md" .
# Search for template TODOs
grep -r "TODO" --include="*.py" --include="*.md" .
# Search for example.com
grep -r "example.com" --include="*.py" --include="*.md" .
Fix any findings. Common leftover locations:
CONTRIBUTING.md -- still showing {service} or wrong connector import| Symptom | Likely cause | Fix |
|---|---|---|
ModuleNotFoundError | Dependency not installed | uv pip install <package> |
| API field format errors | Sending FieldInfo objects | Add _val() helper |
| API rejects empty fields | API wants fields omitted | Don't include in payload |
| API rejects missing fields | API wants empty strings | Include as "" |
| Stale code after edits | Python bytecode cache | Delete __pycache__ dirs |
| Test account errors | Account not fully configured | Ask user to verify setup |
Goal: Package on PyPI with automated release workflow.
The template already provides most of the publishing infrastructure. You mostly need to fill in placeholders.
The template has basic structure. Update existing fields and add missing ones:
name -- your package name (e.g., mcp-ezpay-einvoice)description -- one-line descriptiondependencies -- add service-specific deps (e.g., pycryptodome); uncomment connector deps if needed[project.scripts] -- update entry point name (e.g., mcp-ecpay = "mcp_server:main")keywords = [...] for PyPI discoverabilityclassifiers = [...] (Development Status, License, Python version)[project.urls] section with Homepage, Repository, Issues URLs[tool.hatch.build.targets.wheel] packages list if you added new top-level packagesThe template already has .github/workflows/publish.yml configured for trusted publisher (OIDC). No changes needed unless you want to customize the Python version or build steps.
On https://pypi.org/manage/account/publishing/:
mcp-your-servicemcp-your-servicepublish.ymlpypiRepo Settings > Environments > New environment named pypi.
gh repo edit org/mcp-your-service \
--description "MCP Server for ... — N AI-callable tools for ..." \
--add-topic "mcp,mcp-server,ai-tools,..." \
--homepage "https://..."
Add shield badges at the top of your README (replace org and mcp-your-service):
[](https://pypi.org/project/mcp-your-service/)
[](https://pypi.org/project/mcp-your-service/)
[](https://opensource.org/licenses/MIT)
[](https://modelcontextprotocol.io/)
[](https://github.com/org/mcp-your-service/stargazers)
[](https://github.com/org/mcp-your-service/issues)
[](https://github.com/org/mcp-your-service/commits/main)
git push origin main
gh release create v0.1.0 --title "v0.1.0 - Initial Release" --notes "..."
gh run watch # watch the publish workflow
The template README provides basic structure (features, project structure, connectors, auth, quick start). You'll rewrite it for your service with these sections:
1. Title + shield badges (add after first PyPI publish)
2. Language toggle (English / 繁體中文)
3. Overview (1 paragraph about YOUR service)
4. Features (YOUR tool list + technical highlights)
5. Prerequisites (YOUR service's credential requirements)
6. Installation (PyPI, uvx, source methods)
7. Configuration (YOUR env vars table)
8. Usage (Claude Desktop JSON, Claude Code .mcp.json, Cursor config)
9. Usage Examples (conversational format with real results)
10. Tools Reference (table with YOUR tools)
11. Error Codes Reference (from YOUR service's docs)
12. Architecture (update diagram for YOUR project)
13. Contributing
14. License
Follow this conversational pattern for each example:
### "Description of what the user wants to do"
> **You:** 用自然語言描述需求
**AI calls:**
\```
tool_name(
param1 = "value1",
param2 = "value2",
)
\```
**Result:** `SUCCESS` -- Description of what happened and key response data.
Include 6-8 examples covering main workflows. Use real responses from your test runs.
reference/ folderField() descriptions (add _val() helper if using optional params){service} placeholders remaining in codeGuides creation, editing, and verification of skills for AI coding agents using test-driven development with subagent scenarios. Use when authoring or debugging skills.
npx claudepluginhub wjwang/wjwang-marketplace --plugin mcp-create