Metadata-Version: 2.4
Name: mcp-yaml-server
Version: 1.1.0
Summary: Generate MCP (Model Context Protocol) tools from YAML configuration files. Turn any REST API into callable MCP tools without writing code.
Author-email: Mohamed Zayed <mohamed.zayed@labtessi.fr>
Maintainer-email: Mohamed Zayed <mohamed.zayed@labtessi.fr>
License: MIT
Project-URL: Homepage, https://github.com/labtessi/mcp-yaml-server
Project-URL: Documentation, https://github.com/labtessi/mcp-yaml-server#readme
Project-URL: Repository, https://github.com/labtessi/mcp-yaml-server.git
Project-URL: Issues, https://github.com/labtessi/mcp-yaml-server/issues
Project-URL: Changelog, https://github.com/labtessi/mcp-yaml-server/blob/main/CHANGELOG.md
Keywords: mcp,model-context-protocol,yaml,api,rest,claude,ai,llm,tools,fastmcp,automation
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: System Administrators
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Software Development :: Code Generators
Classifier: Typing :: Typed
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: fastmcp<3,>=2.0
Requires-Dist: pyyaml>=6.0
Requires-Dist: httpx[http2]>=0.27
Requires-Dist: pydantic>=2.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
Requires-Dist: pytest-cov>=4.0; extra == "dev"
Requires-Dist: ruff>=0.1; extra == "dev"
Requires-Dist: black>=23.0; extra == "dev"
Requires-Dist: mypy>=1.0; extra == "dev"
Requires-Dist: types-PyYAML; extra == "dev"
Requires-Dist: pre-commit>=3.0; extra == "dev"
Provides-Extra: docs
Requires-Dist: mkdocs>=1.5; extra == "docs"
Requires-Dist: mkdocs-material>=9.0; extra == "docs"
Requires-Dist: mkdocstrings[python]>=0.24; extra == "docs"
Dynamic: license-file

# MCP YAML Server

[![PyPI version](https://badge.fury.io/py/mcp-yaml-server.svg)](https://badge.fury.io/py/mcp-yaml-server)
[![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

**Generate MCP (Model Context Protocol) tools from YAML configuration files.** Turn any REST API into callable MCP tools without writing code.

Perfect for teams who want to expose internal APIs to AI assistants like Claude Desktop, GitHub Copilot, or any MCP-compatible client.

## ✨ Features

- 🎯 **Zero-Code Integration** - Define API endpoints in YAML, get working MCP tools
- 🔐 **Multiple Auth Methods** - API key, Bearer token, Basic auth, custom headers
- 📁 **File Upload Support** - Handle multipart/form-data with type/size validation
- 🔄 **All HTTP Methods** - GET, POST, PUT, PATCH, DELETE
- 🌐 **Remote & Local** - Works with both local and remote MCP servers
- ⚡ **High Performance** - Async HTTP with connection pooling and HTTP/2
- 🛡️ **Type Safe** - Pydantic validation for configuration and parameters
- 🐳 **Docker Ready** - Deploy as containerized HTTP/SSE server
- 🔧 **Environment Variables** - Secure credential management with `${VAR}` syntax

## 📦 Installation

```bash
pip install mcp-yaml-server
```

**Requirements**: Python 3.10+

## 🚀 Quick Start

### 1. Create a YAML Configuration

Create `my-api.yaml`:

```yaml
server:
  name: "my_api_tools"
  version: "1.0.0"
  description: "My API integration"

global:
  base_url: "https://jsonplaceholder.typicode.com"
  timeout: 30

endpoints:
  - name: "get_user"
    description: "Retrieve user information by their unique ID"
    method: "GET"
    path: "/users/{id}"
    parameters:
      - name: "id"
        type: "integer"
        description: "The unique identifier of the user"
        required: true
        location: "path"

  - name: "create_post"
    description: "Create a new blog post for a user"
    method: "POST"
    path: "/posts"
    parameters:
      - name: "title"
        type: "string"
        description: "The title of the blog post"
        required: true
        location: "body"
      - name: "body"
        type: "string"
        description: "The content body of the post"
        required: true
        location: "body"
      - name: "userId"
        type: "integer"
        description: "The ID of the user creating the post"
        required: true
        location: "body"
```

### 2. Start the MCP Server

```bash
# Standard MCP server (stdio transport for Claude Desktop)
mcp-yaml-server my-api.yaml

# HTTP server for remote access
mcp-yaml-server --transport http --host 0.0.0.0 --port 8000 my-api.yaml

# SSE/Streaming HTTP server (for GitHub Copilot, etc.)
mcp-yaml-server --transport streamable-http --host 0.0.0.0 --port 8000 my-api.yaml

# Validate configuration without starting
mcp-yaml-server --dry-run my-api.yaml

# Debug mode with verbose logging
mcp-yaml-server --debug my-api.yaml
```

### 3. Connect from Claude Desktop

Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "my_api_tools": {
      "command": "mcp-yaml-server",
      "args": ["/absolute/path/to/my-api.yaml"]
    }
  }
}
```

Restart Claude Desktop - your tools are now available!

### 4. Connect from GitHub Copilot

Add to your MCP settings:

```json
{
  "servers": {
    "my_api_tools": {
      "type": "sse",
      "url": "http://localhost:8000/mcp"
    }
  }
}
```

## 📖 Configuration Reference

### Server Configuration

```yaml
server:
  name: "my_server"           # Required: Server identifier (alphanumeric + underscore)
  version: "1.0.0"            # Required: Semantic version
  description: "Description"   # Optional: Human-readable description
```

### Global Settings

```yaml
global:
  base_url: "https://api.example.com"  # Required: Base URL for all endpoints
  timeout: 30                           # Optional: Default timeout in seconds (default: 30)
  headers:                              # Optional: Headers added to all requests
    X-Custom-Header: "value"
  auth:                                 # Optional: Authentication configuration
    type: "bearer"                      # Options: bearer, api_key, basic, custom
    bearer:
      token: "${API_TOKEN}"
```

### Authentication Types

#### Bearer Token
```yaml
auth:
  type: "bearer"
  bearer:
    token: "${AUTH_TOKEN}"
```

#### API Key
```yaml
auth:
  type: "api_key"
  api_key:
    header_name: "X-API-Key"
    token: "${API_KEY}"
```

#### Basic Auth
```yaml
auth:
  type: "basic"
  basic:
    username: "${API_USER}"
    password: "${API_PASS}"
```

#### Custom Headers
```yaml
auth:
  type: "custom"
  custom:
    headers:
      Authorization: "Custom ${TOKEN}"
      X-Auth-Provider: "my-provider"
```

### Endpoint Configuration

```yaml
endpoints:
  - name: "endpoint_name"              # Required: Tool name (alphanumeric + underscore)
    description: "What this does"       # Required: Min 20 characters
    method: "GET"                       # Required: GET, POST, PUT, PATCH, DELETE
    path: "/resource/{id}"              # Required: URL path with optional {placeholders}
    parameters: [...]                   # Optional: List of parameters
    request_config: {...}               # Optional: Request customization
    response_config: {...}              # Optional: Response handling
```

### Parameter Configuration

```yaml
parameters:
  - name: "param_name"                 # Required: Parameter name
    type: "string"                     # Required: string, integer, number, boolean, array, object
    description: "What this param is"  # Required: Min 20 characters
    required: true                     # Required: true or false
    location: "path"                   # Required: path, query, header, body, file
    default: "value"                   # Optional: Default value if not provided
```

**Parameter Locations:**
- `path` - URL path parameter (e.g., `/users/{id}`)
- `query` - URL query parameter (e.g., `?limit=10`)
- `header` - HTTP header
- `body` - JSON request body field
- `file` - File upload (multipart/form-data)

### Request Configuration

```yaml
request_config:
  body_type: "json"              # Options: json, form, multipart (default: json)
  timeout: 60                    # Override global timeout for this endpoint
  headers:                       # Additional headers for this endpoint
    X-Custom: "value"
  files:                         # File upload configuration
    max_size_mb: 50
    allowed_types:
      - "application/pdf"
      - "image/jpeg"
      - "image/png"
    field_name: "file"           # Form field name for file
    multiple: false              # Allow multiple files
```

### Response Configuration

```yaml
response_config:
  success_codes: [200, 201]              # Expected success status codes
  extract_path: "$.data.items"           # JSONPath to extract from response
  error_handling:
    extract_message_path: "$.error.msg"  # JSONPath to extract error message
    default_message: "Request failed"    # Fallback error message
    include_status_code: true            # Include HTTP status in error
    include_response_body: false         # Include response body in error
```

## 📁 File Uploads

The server supports file uploads from both local and remote MCP clients:

```yaml
endpoints:
  - name: "upload_document"
    description: "Upload and process a document file"
    method: "POST"
    path: "/documents/upload"
    parameters:
      - name: "file"
        type: "string"
        description: "Path to the document file to upload"
        required: true
        location: "file"
      - name: "title"
        type: "string"
        description: "Title for the uploaded document"
        required: true
        location: "body"
    request_config:
      body_type: "multipart"
      files:
        max_size_mb: 50
        allowed_types:
          - "application/pdf"
          - "image/jpeg"
          - "image/png"
```

**Usage:**
- **Local paths**: `/Users/me/document.pdf` - File is read automatically
- **Base64 content**: Raw base64-encoded string for programmatic uploads
- **Data URI**: `data:application/pdf;base64,JVBERi0xLjQ...`

## 🔐 Environment Variables

Use `${VAR_NAME}` syntax for secure credential management:

```yaml
global:
  base_url: "${API_BASE_URL}"
  auth:
    type: "bearer"
    bearer:
      token: "${API_TOKEN}"
```

With defaults:
```yaml
base_url: "${API_BASE_URL:https://api.example.com}"
timeout: "${TIMEOUT:30}"
```

Set variables before running:
```bash
export API_BASE_URL="https://api.example.com"
export API_TOKEN="your-secret-token"
mcp-yaml-server my-api.yaml
```

## 🐳 Docker Deployment

### Using the Dockerfile

```dockerfile
FROM python:3.11-slim

WORKDIR /app

RUN pip install mcp-yaml-server

COPY my-api.yaml /app/config.yaml

ENV MCP_CONFIG=/app/config.yaml
ENV HOST=0.0.0.0
ENV PORT=8000

CMD ["mcp-yaml-server", "--transport", "streamable-http", "--host", "0.0.0.0", "--port", "8000", "/app/config.yaml"]
```

### Build and Run

```bash
docker build -t my-mcp-server .
docker run -p 8000:8000 -e API_TOKEN=secret my-mcp-server
```

### Docker Compose

```yaml
version: '3.8'
services:
  mcp-server:
    build: .
    ports:
      - "8000:8000"
    environment:
      - API_TOKEN=${API_TOKEN}
      - API_BASE_URL=https://api.example.com
    volumes:
      - ./configs:/app/configs
    command: >
      mcp-yaml-server
      --transport streamable-http
      --host 0.0.0.0
      --port 8000
      /app/configs/my-api.yaml
```

## 🛠️ CLI Reference

```bash
# Start MCP server (stdio transport - default)
mcp-yaml-server config.yaml

# Start HTTP server
mcp-yaml-server --transport http --port 8000 config.yaml

# Start SSE/streaming server
mcp-yaml-server --transport streamable-http --port 8000 config.yaml

# Validate configuration
mcp-yaml-server --dry-run config.yaml
mcp-yaml-validate config.yaml

# Debug mode
mcp-yaml-server --debug config.yaml

# Multiple configurations
mcp-yaml-server api1.yaml api2.yaml

# Full options
mcp-yaml-server \
  --transport streamable-http \
  --host 0.0.0.0 \
  --port 8000 \
  --debug \
  config.yaml
```

## 🔧 Troubleshooting

### Common Issues

**"Configuration file not found"**
```bash
# Use absolute paths
mcp-yaml-server /absolute/path/to/config.yaml
```

**"bearer field required when type is 'bearer'"**
```yaml
# Correct nested structure:
auth:
  type: "bearer"
  bearer:
    token: "${TOKEN}"
```

**"String should have at least 20 characters"**
```yaml
# Descriptions must be at least 20 characters
description: "This is a detailed description of the endpoint"
```

**"Environment variable 'X' is not set"**
```bash
# Set required variables
export API_TOKEN="your-token"
mcp-yaml-server config.yaml
```

**File upload "File not found" on remote server**
- Ensure you're running the latest version with client-side file reading
- For remote servers, files are read locally and uploaded automatically

### Debug Mode

```bash
# Enable verbose logging
mcp-yaml-server --debug config.yaml

# Validate without starting
mcp-yaml-server --dry-run config.yaml
```

## 📚 Examples

See the `examples/` directory for complete configuration examples:

- `sample-config.yaml` - Basic GET endpoints
- `sample-github-api.yaml` - GitHub API with authentication
- `sample-file-upload.yaml` - File upload endpoints
- `dpr-mock-server-config.yaml` - Complex multi-endpoint configuration

## 🏗️ Architecture

- **Language**: Python 3.10+
- **MCP Framework**: FastMCP 2.x
- **HTTP Client**: httpx (async, HTTP/2)
- **Validation**: Pydantic 2.x
- **YAML Parser**: PyYAML 6.0+

## ⚡ Performance

- YAML parse/load: <1s for 50+ endpoints
- Tool metadata generation: <100ms per tool
- HTTP request overhead: <50ms beyond API latency
- Supports 100+ concurrent tool invocations
- Connection pooling and HTTP/2 for efficiency

## 🤝 Contributing

Contributions are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.

```bash
# Clone and install dev dependencies
git clone https://github.com/labtessi/mcp-yaml-server.git
cd mcp-yaml-server
pip install -e ".[dev]"

# Run tests
pytest

# Run linter
ruff check src/ tests/

# Format code
black src/ tests/
```

## 📄 License

MIT License - see [LICENSE](LICENSE) file for details.

## 🔗 Related Projects

- [Model Context Protocol](https://modelcontextprotocol.io/)
- [FastMCP Framework](https://github.com/jlowin/fastmcp)
- [Claude Desktop](https://claude.ai/desktop)

## 📞 Support

- **Issues**: [GitHub Issues](https://github.com/labtessi/mcp-yaml-server/issues)
- **Documentation**: [GitHub README](https://github.com/labtessi/mcp-yaml-server#readme)
- **MCP Docs**: [Model Context Protocol](https://modelcontextprotocol.io/)
