MCP YAML Server
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
pip install mcp-yaml-server
Requirements: Python 3.10+
🚀 Quick Start
1. Create a YAML Configuration
Create my-api.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
# 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:
{
"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:
{
"servers": {
"my_api_tools": {
"type": "sse",
"url": "http://localhost:8000/mcp"
}
}
}
📖 Configuration Reference
Server Configuration
server:
name: "my_server" # Required: Server identifier (alphanumeric + underscore)
version: "1.0.0" # Required: Semantic version
description: "Description" # Optional: Human-readable description
Global Settings
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
auth:
type: "bearer"
bearer:
token: "${AUTH_TOKEN}"
API Key
auth:
type: "api_key"
api_key:
header_name: "X-API-Key"
token: "${API_KEY}"
Basic Auth
auth:
type: "basic"
basic:
username: "${API_USER}"
password: "${API_PASS}"
Custom Headers
auth:
type: "custom"
custom:
headers:
Authorization: "Custom ${TOKEN}"
X-Auth-Provider: "my-provider"
Endpoint Configuration
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
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 headerbody- JSON request body fieldfile- File upload (multipart/form-data)
Request Configuration
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
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:
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:
global:
base_url: "${API_BASE_URL}"
auth:
type: "bearer"
bearer:
token: "${API_TOKEN}"
With defaults:
base_url: "${API_BASE_URL:https://api.example.com}"
timeout: "${TIMEOUT:30}"
Set variables before running:
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
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
docker build -t my-mcp-server .
docker run -p 8000:8000 -e API_TOKEN=secret my-mcp-server
Docker Compose
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
# 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"
# Use absolute paths
mcp-yaml-server /absolute/path/to/config.yaml
"bearer field required when type is 'bearer'"
# Correct nested structure:
auth:
type: "bearer"
bearer:
token: "${TOKEN}"
"String should have at least 20 characters"
# Descriptions must be at least 20 characters
description: "This is a detailed description of the endpoint"
"Environment variable 'X' is not set"
# 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
# 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 endpointssample-github-api.yaml- GitHub API with authenticationsample-file-upload.yaml- File upload endpointsdpr-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 for guidelines.
# 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 file for details.
🔗 Related Projects
📞 Support
- Issues: GitHub Issues
- Documentation: GitHub README
- MCP Docs: Model Context Protocol
Metadata
Release files for mcp-yaml-server 1.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| mcp_yaml_server-1.1.0.tar.gz | 37.5 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| mcp_yaml_server-1.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 68.0 kB
Release files / mcp_yaml_server-1.1.0.tar.gz
| Download URL | mcp_yaml_server-1.1.0.tar.gz |
|---|---|
| Size | 37.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
67d9b552c217755beb169310126fe037b186ed2208c469c849a1f9750462154e
|
|
BLAKE2b-256 checksum How to use checksums |
d9cea3368478ea8c395aca0a2e176db3c06bfb7c10a0fd0040d8609792315b63
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.10
|
Release files / mcp_yaml_server-1.1.0-py3-none-any.whl
| Download URL | mcp_yaml_server-1.1.0-py3-none-any.whl |
|---|---|
| Size | 30.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
384fc2d6d84b8553532afacf347121cc2b7d848746ef4a6bd45ea761691ed511
|
|
BLAKE2b-256 checksum How to use checksums |
275ae290dc9a4e135f8597f391a728a3c4edebb031a221316c854995af10bf6b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.10
|
Security for mcp-yaml-server 1.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.