<< All versions

Skill v1.0.0

currentAutomated scan100/100
alexai-mcp/hermes-ccc/mcporter
──Details
PublishedOctober 24, 2026 at 06:10 AM
Content Hashsha256:fa5d905f70071539...
Git SHA8107e895541f
──Files
Files (1 file, 4.1 KB)
SKILL.md4.1 KBactive
SKILL.md · 217 lines · 4.1 KB

name: mcporter description: Convert any CLI tool or Python function into an MCP server using fastmcp. Zero-config tool injection into Claude Code. version: 1.0.0 author: hermes-CCC (ported from Hermes Agent by NousResearch) license: MIT metadata: hermes: tags: [MCP, Tools, Development, fastmcp, Integration, Server] related_skills: [native-mcp]


MCPorter — CLI to MCP Converter

Turn any Python function or CLI tool into an MCP server that Claude Code can call natively.

Setup

bash
pip install fastmcp

Minimal Server

python
# my_server.py
from fastmcp import FastMCP
mcp = FastMCP("my-tool")
@mcp.tool()
def hello(name: str) -> str:
"""Say hello to someone."""
return f"Hello, {name}!"
if __name__ == "__main__":
mcp.run() # stdio transport (for Claude Code)

Register in .mcp.json:

json
{
"mcpServers": {
"my-tool": {
"command": "python",
"args": ["my_server.py"]
}
}
}

Restart Claude Code → hello tool appears natively.


Type Annotations Matter

Claude reads your type hints to understand the tool:

python
@mcp.tool()
def search_files(
query: str,
directory: str = ".",
max_results: int = 10,
case_sensitive: bool = False,
) -> list[str]:
"""Search for files matching query in directory."""
import subprocess
flag = "" if case_sensitive else "-i"
result = subprocess.run(
["grep", "-rl", flag, query, directory],
capture_output=True, text=True
)
return result.stdout.strip().split("\n")[:max_results]

Wrap a CLI Tool

python
import subprocess
from fastmcp import FastMCP
mcp = FastMCP("git-helper")
@mcp.tool()
def git_log(n: int = 10, format: str = "oneline") -> str:
"""Show git commit history."""
result = subprocess.run(
["git", "log", f"-{n}", f"--format={format}"],
capture_output=True, text=True
)
return result.stdout
@mcp.tool()
def git_diff(staged: bool = False) -> str:
"""Show git diff."""
args = ["git", "diff"]
if staged:
args.append("--staged")
result = subprocess.run(args, capture_output=True, text=True)
return result.stdout
if __name__ == "__main__":
mcp.run()

Async Tools

python
import asyncio
import httpx
from fastmcp import FastMCP
mcp = FastMCP("web-fetcher")
@mcp.tool()
async def fetch_url(url: str, timeout: int = 10) -> str:
"""Fetch content from a URL."""
async with httpx.AsyncClient() as client:
response = await client.get(url, timeout=timeout)
return response.text[:5000] # first 5k chars
if __name__ == "__main__":
mcp.run()

Resources (Read-Only Data)

python
@mcp.resource("config://settings")
def get_settings() -> str:
"""Return current settings."""
import json, pathlib
settings = pathlib.Path("settings.json").read_text()
return settings
@mcp.resource("file://{path}")
def read_file(path: str) -> str:
"""Read a file by path."""
return pathlib.Path(path).read_text()

Prompts

python
@mcp.prompt()
def code_review_prompt(code: str, language: str = "python") -> str:
"""Generate a code review prompt."""
return f"Review this {language} code for bugs and improvements:\n\n```{language}\n{code}\n```"

HTTP Transport (for non-CC clients)

python
if __name__ == "__main__":
mcp.run(transport="http", host="0.0.0.0", port=8080)

Then register as:

json
{"mcpServers": {"my-tool": {"url": "http://localhost:8080/sse"}}}

Error Handling

python
@mcp.tool()
def risky_operation(value: str) -> str:
"""Do something that might fail."""
if not value:
raise ValueError("value cannot be empty") # MCP returns error to Claude
return process(value)

Test Interactively

bash
fastmcp dev my_server.py
# Opens interactive MCP inspector in browser

With Environment Variables

json
{
"mcpServers": {
"my-api": {
"command": "python",
"args": ["api_server.py"],
"env": {
"API_KEY": "your-secret-key",
"BASE_URL": "https://api.example.com"
}
}
}
}
All versions