Metadata-Version: 2.4
Name: skillmd-to-mcp
Version: 0.1.1
Summary: Generate MCP servers from plain-English skill descriptions
Author: skill-to-mcp contributors
License: MIT
License-File: LICENSE
Keywords: cli,codegen,llm,mcp,model-context-protocol,skill
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Code Generators
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Requires-Dist: jinja2>=3.1
Requires-Dist: mcp>=1.0
Requires-Dist: openai>=1.30
Requires-Dist: pydantic>=2.0
Requires-Dist: rich>=13.0
Requires-Dist: typer>=0.12
Provides-Extra: apify
Requires-Dist: apify>=2.0; extra == 'apify'
Provides-Extra: dev
Requires-Dist: jsonschema>=4.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest-mock>=3.12; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.3; extra == 'dev'
Description-Content-Type: text/markdown

# skill-to-mcp

> **Turn a plain-English skill description into a working MCP server in seconds.**

[![Python](https://img.shields.io/badge/Python-3.10%2B-blue)](https://www.python.org/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
[![Tests](https://img.shields.io/badge/tests-136%20passed-brightgreen)](tests/)
[![PyPI version](https://img.shields.io/pypi/v/skillmd-to-mcp)](https://pypi.org/project/skillmd-to-mcp/)
[![Python Versions](https://img.shields.io/pypi/pyversions/skillmd-to-mcp)](https://pypi.org/project/skillmd-to-mcp/)
[![PyPI Downloads](https://img.shields.io/pypi/dm/skillmd-to-mcp)](https://pypi.org/project/skillmd-to-mcp/)
[![Coverage](https://img.shields.io/badge/coverage-95%25-brightgreen)](tests/)

![skill-to-mcp demo](demo/demo.gif)

**`skill-to-mcp`** is a CLI code generator that reads a `SKILL.md` file — a plain-English description of a tool — and scaffolds a complete, runnable **MCP (Model Context Protocol) server** with:

- ✅ Correct JSON-RPC schema (`tool_name`, `description`, `inputSchema`, `outputSchema`)
- ✅ Pydantic v2 models for type-safe input/output
- ✅ MCP server boilerplate (stdio or HTTP SSE transport)
- ✅ `pyproject.toml` ready for `pip install`
- ✅ Auto-generated `README.md` with usage instructions

> 📖 [Leggi in Italiano](README.it.md)

---

## Why?

Describing a tool in Markdown is **more readable, versionable, and composable** than hand-writing JSON-RPC schemas. `skill-to-mcp` bridges the gap between *"I know what my tool does"* and *"I have a working MCP server."*

Read the full motivation: [SKILL.md might be a better abstraction than function calling](https://reddit.com/r/ClaudeAI/comments/1ug83x6/why_skillmd_might_be_a_better_abstraction_than/)

---

## Apify Actor

Run skill-to-mcp on the [Apify platform](https://apify.com/):

```bash
docker build -t skill-to-mcp .
docker run -e DEEPSEEK_API_KEY=sk-... skill-to-mcp
```

The Apify Actor accepts the same inputs as the CLI via a web form:
**Skill Description** (required), **LLM Model**, **MCP Transport**,
**Generate Example Implementation**, and **API Key**.

Output is pushed to the Apify Dataset and a ZIP archive of all generated
files is saved to the key-value store.

The Actor builds directly from this git repository via
[`.actor/actor.json`](.actor/actor.json) and the root
[`Dockerfile`](Dockerfile) (entry point: `python -m skill_to_mcp.actor`).

> 📖 [Apify Actor documentation](.actor/input_schema.json)

---

## Quick Start

### 1. Install

```bash
pip install skillmd-to-mcp
```

### 2. Set your API key

```bash
export DEEPSEEK_API_KEY="sk-your-key-here"
```

Get a key at [platform.deepseek.com](https://platform.deepseek.com/api_keys). **Cost per generation: < $0.001** with DeepSeek.

> **OpenAI fallback:** If you have an OpenAI key, set `OPENAI_API_KEY` instead.
> The CLI automatically tries both: `DEEPSEEK_API_KEY` first, then `OPENAI_API_KEY`.

### 3. Create a `SKILL.md`

```markdown
# webpage-reader

Fetches a web page given a URL and returns:
- title: the page title
- description: the meta description
- body_text: visible text stripped of HTML
- links: list of the first 10 internal links

## Input

- url: string, required
- max_links: integer, default 10
```

### 4. Generate the MCP server

```bash
skill-to-mcp generate SKILL.md
```

**Output:**

```
output/
└── webpage-reader/
    ├── server.py          # MCP server, runnable immediately
    ├── models.py          # Pydantic v2 input/output models
    ├── handler.py         # Tool logic (stub — implement here)
    ├── pyproject.toml     # Dependencies and metadata
    └── README.md          # Installation and usage guide
```

### 5. Implement & Run

```bash
cd output/webpage-reader
pip install -e .
# Edit handler.py to add your business logic
python server.py
```

---

## CLI Reference

```
skill-to-mcp generate SKILL.md [OPTIONS]
```

### Arguments

| Argument | Description |
|----------|-------------|
| `SKILL.md` | Path to the skill description file |

### Options

| Option | Short | Default | Description |
|--------|-------|---------|-------------|
| `--output-dir` | `-o` | `./output` | Output directory for generated files |
| `--model` | `-m` | `deepseek-v4-flash` | LLM model for schema inference |
| `--api-key` | `-k` | `$DEEPSEEK_API_KEY` or `$OPENAI_API_KEY` | API key for the LLM provider. Falls back to `OPENAI_API_KEY` if `DEEPSEEK_API_KEY` is not set |
| `--transport` | `-t` | `stdio` | MCP transport: `stdio` or `sse` |
| `--dry-run` | | `false` | Show inferred schema without generating files |
| `--prompt-file` | `-p` | | Custom system prompt for LLM inference |
| `--with-example-impl` | | `false` | Generate a working handler implementation via LLM |
| `--templates-dir` | | | Custom Jinja2 templates directory |
| `--verbose` | `-v` | `false` | Enable debug logging |

### Batch Processing

Process multiple `SKILL.md` files at once:

```bash
skill-to-mcp generate tool1.md tool2.md tool3.md -o ./my-tools
skill-to-mcp generate *.md --with-example-impl
```

### Examples

```bash
# Basic usage
skill-to-mcp generate SKILL.md

# Preview the inferred schema (no files written)
skill-to-mcp generate SKILL.md --dry-run

# Use SSE transport for HTTP-based MCP hosts
skill-to-mcp generate SKILL.md --transport sse

# Custom output directory
skill-to-mcp generate SKILL.md -o ./my-mcp-tools

# Use a specific model
skill-to-mcp generate SKILL.md -m deepseek-v4-flash

# Custom LLM prompt
skill-to-mcp generate SKILL.md --prompt-file my_prompt.txt

# Verbose output for debugging
skill-to-mcp generate SKILL.md -v
```

---

## Architecture

```mermaid
flowchart LR
    A[SKILL.md] --> B[parser.py]
    B --> C[llm.py]
    C --> D[DeepSeek API]
    D --> E[validator.py]
    E -->|valid| F[renderer.py]
    E -->|invalid + retry| C
    F --> G[templates/]
    G --> H[output/]
```

### Pipeline

1. **[`parser.py`](skill_to_mcp/parser.py)** — Reads `SKILL.md`, extracts title and sections
2. **[`llm.py`](skill_to_mcp/llm.py)** — Calls DeepSeek API (OpenAI-compatible) to infer JSON Schema
3. **[`validator.py`](skill_to_mcp/validator.py)** — Validates schema (types, snake_case, required fields)
4. **[`renderer.py`](skill_to_mcp/renderer.py)** — Applies Jinja2 templates to generate Python code
5. **[`cli.py`](skill_to_mcp/cli.py)** — Typer-based CLI with Rich output

### LLM Prompt

The system prompt is **transparent and overridable**. View the default in [`config.py`](skill_to_mcp/config.py) or customize with `--prompt-file`.

---

## MCP Host Configuration

### Claude Desktop

Add to `claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "my-tool": {
      "command": "python",
      "args": ["path/to/output/my-tool/server.py"]
    }
  }
}
```

### Cursor IDE

Add to `.cursor/mcp.json`:

```json
{
  "mcpServers": {
    "my-tool": {
      "command": "python",
      "args": ["path/to/output/my-tool/server.py"]
    }
  }
}
```

### SSE Transport

For HTTP-based MCP hosts:

```bash
skill-to-mcp generate SKILL.md --transport sse
cd output/my-tool && python server.py
# MCP endpoint: http://127.0.0.1:8000/sse
```

---

## Development

### Setup

```bash
git clone https://github.com/YOUR_USERNAME/skill-to-mcp
cd skill-to-mcp
pip install -e ".[dev]"
```

### Run Tests

```bash
# All tests (99 total)
pytest tests/ -v

# End-to-end only
pytest tests/test_e2e.py -v

# With real API (requires DEEPSEEK_API_KEY)
DEEPSEEK_API_KEY="sk-..." skill-to-mcp generate tests/fixtures/simple_skill.md --dry-run
```

### Code Quality

```bash
ruff check skill_to_mcp/ tests/
```

---

## Tech Stack

| Component | Technology |
|-----------|-----------|
| CLI Framework | [Typer](https://typer.tiangolo.com/) |
| Templates | [Jinja2](https://jinja.palletsprojects.com/) |
| LLM SDK | [OpenAI Python](https://github.com/openai/openai-python) (DeepSeek-compatible) |
| Validation | [Pydantic v2](https://docs.pydantic.dev/) |
| MCP SDK | [modelcontextprotocol/python-sdk](https://github.com/modelcontextprotocol/python-sdk) |
| Output | [Rich](https://rich.readthedocs.io/) |
| Testing | [pytest](https://pytest.org/) + [pytest-mock](https://github.com/pytest-dev/pytest-mock) |
| Linting | [ruff](https://github.com/astral-sh/ruff) |

---

## FAQ

### Do I need to understand MCP protocol internals?

**No.** The generated server handles JSON-RPC, tool discovery, and schema validation. You only need to implement `handler.py`.

### What if the LLM produces a wrong schema?

The validator catches type errors, invalid names, and missing fields. Retry is automatic (up to 3 attempts). Use `--dry-run` to preview before generating.

### Can I use OpenAI instead of DeepSeek?

Yes. The CLI automatically falls back to `OPENAI_API_KEY` if `DEEPSEEK_API_KEY` is not set.

```bash
export OPENAI_API_KEY="sk-..."
skill-to-mcp generate SKILL.md --model gpt-4o-mini
```

Any OpenAI-compatible endpoint works (OpenAI, DeepSeek, OpenRouter, Together AI, etc.).

### What Python version do I need?

Python 3.10+ for the CLI. The generated MCP servers also require Python 3.10+.

### Is this production-ready?

This is an **MVP (v0.1.0)**. The generated code is correct and runnable, but you should review and test the generated handler before deploying.

### How much does each generation cost?

**~$0.001 per generation** with DeepSeek V4 Flash. The prompt is ~300 tokens and the response is ~200 tokens.

### Is the --with-example-impl handler production-ready?

The LLM-generated handler is a **starting point**. It may contain bugs (e.g., referencing local variables instead of `input.field`). Always review and test the generated code before deploying. The prompt has been tuned to minimize common LLM mistakes, but human review is recommended.

---

## Roadmap

- [x] `--with-example-impl` flag for LLM-generated handler implementations
- [ ] TypeScript/Go output support
- [x] Batch processing (multiple `SKILL.md` files)
- [ ] Hosted registry at `registry.skill-to-mcp.dev`
- [ ] `skill-to-mcp publish` command
- [ ] Smithery integration
- [x] Custom template directories (`--templates-dir`)

---

## Contributing

Pull requests are welcome! See [`project_state.md`](project_state.md) for current status and next steps.

1. Fork the repo
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
3. Run tests (`pytest tests/ -v`)
4. Commit your changes
5. Push and open a PR

---

## License

MIT — see [LICENSE](LICENSE).

---

*Built with ❤️ for MCP developers. If this saved you time, ⭐ the repo!*
