One decorator. REST endpoint + MCP tool. Built-in UI, file serving, and LLM client.
Project description
simcpi
Building MCP tools today means maintaining two separate things — an API for your app and an MCP server for your AI client. simcpi collapses that into a single decorator.
Define a function once. Get a REST endpoint, an MCP tool, and a visual tester — all at the same time.
Features:
@create_tool_api— one decorator registers both a FastAPI route and an MCP tool- MCPark — built-in visual UI to inspect and test MCP tools in the browser
serve_file()— return DataFrames, charts, images, or any file from an MCP tool in one lineQuickClient— connect to any MCP server and run tools through an LLM in 5 linesconnect_claude()/connect_cursor()— auto-register your server in Claude Desktop or Cursor config- Multi-server support — combine tools from multiple MCP servers in one client
How it works:
simcpi wraps FastAPI and FastMCP behind a single MCPApi class. When you decorate a function with @app.create_tool_api(), it registers the function as a FastAPI route (accessible via REST) and as a FastMCP tool (accessible via the MCP streamable-HTTP transport) simultaneously. The same server handles both protocols. MCPark is mounted automatically and connects to the live MCP transport to test tools interactively.
pip install simcpi
Quick Start
from simcpi import MCPApi
import uvicorn
app = MCPApi(
title="my-api",
provider="openai",
api_key="sk-...",
model="gpt-4o",
)
@app.create_tool_api("/greet")
def greet(name: str) -> str:
"""
Greet the user by name.
Use this when the user wants to say hello.
"""
return f"Hello, {name}!"
if __name__ == "__main__":
uvicorn.run(app, host="127.0.0.1", port=8000)
- REST endpoint:
POST /greet - MCP tool:
greet - Visual tester:
http://localhost:8000/mcpark - Swagger UI:
http://localhost:8000/docs
Features
1. @create_tool_api — REST + MCP in one decorator
@app.create_tool_api("/add")
def add(a: int, b: int) -> int:
"""Add two numbers. Use when the user asks to sum or total values."""
return a + b
- Creates
POST /add(FastAPI route) - Registers
addas an MCP tool (FastMCP) - Docstring is the tool description the LLM sees — write it for the model
2. MCPark — visual MCP testing UI
Open http://localhost:8000/mcpark in any browser.
- Lists all registered tools in the sidebar
- Run tools with an LLM in the loop — select provider, paste API key
- Assistant toggle — off gives raw tool output, on gives LLM interpretation
- File results render inline: images show as images, Excel as a table, PDFs open in-page
- One-click copy of the Claude Desktop config snippet
No separate install. No external service. Ships inside the package.
3. serve_file() — return files from MCP tools
MCP tools can only return text. serve_file() bridges that gap — pass your object directly and get back a URL the client can download or MCPark can preview.
@app.create_tool_api("/report")
def sales_report() -> str:
"""Generate a sales report. Use when the user asks for sales data."""
import pandas as pd
df = pd.DataFrame({"Month": ["Jan","Feb","Mar"], "Sales": [1200, 1500, 1800]})
return app.serve_file("report.xlsx", df, port=8000)
@app.create_tool_api("/chart")
def sales_chart() -> str:
"""Generate a sales chart."""
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.bar(["Jan","Feb","Mar"], [1200, 1500, 1800])
return app.serve_file("chart.png", fig, port=8000)
@app.create_tool_api("/image")
def generate_image(prompt: str) -> str:
"""Generate an image from a prompt."""
url = call_image_api(prompt) # returns a remote image URL
return app.serve_file("image.png", url, port=8000)
| Content type | What happens |
|---|---|
pandas.DataFrame |
Serialized to .xlsx, .csv, or .json based on filename |
matplotlib.Figure |
Rendered to .png or .pdf |
bytes |
Written directly |
http://... / https://... |
Downloaded from URL and served |
str / Path |
Treated as a local file path, served as-is |
Install extras for DataFrame and Figure support:
pip install simcpi[files]
4. QuickClient — sync MCP client in 5 lines
from simcpi import QuickClient
client = QuickClient(
mcp_server="http://localhost:8000/mcp/mcp",
provider="openai",
api_key="sk-...",
model="gpt-4o",
)
print(client.run("Greet Mohan in Telugu"))
File results are auto-detected and downloaded:
result = client.run("Give me the sales report")
print(result.save()) # saves to disk, prints the path
Multi-server — tools from all servers combined:
client = QuickClient(
mcp_server=[
"http://localhost:8000/mcp/mcp",
"http://localhost:8080/mcp/mcp",
],
...
)
List available tools:
client.tools()
# {"http://localhost:8000/mcp/mcp": ["greet", "report"], ...}
5. MCPClient — async client with full trace
import asyncio
from simcpi import MCPClient
client = MCPClient(
mcp_server="http://localhost:8000/mcp/mcp",
provider="anthropic",
api_key="sk-ant-...",
model="claude-sonnet-4-20250514",
)
result = asyncio.run(client.run("Add 42 and 58"))
print(result.answer) # "100"
print(result.tools_called) # [ToolCall(name="add", arguments={...}, result="100")]
print(result.success) # True
| Field | Type | Description |
|---|---|---|
answer |
str |
Final LLM response |
tools_called |
list[ToolCall] |
Every tool invoked, in order |
success |
bool |
False if an exception occurred |
error |
str | None |
Error message if success=False |
6. connect_claude() — Claude Desktop integration
On the server instance:
app.connect_claude(port=8000) # local server
app.connect_claude(url="https://myapi.com/mcp/mcp") # remote server
Standalone — no MCPApi needed (register multiple servers at once):
from simcpi import connect_claude
connect_claude("my-api", port=8000)
connect_claude("reports", port=8080)
connect_claude("prod", url="https://myapi.com/mcp/mcp")
Multiple servers append as separate entries — no collision. Restart Claude Desktop after calling.
7. connect_cursor() — Cursor IDE integration
app.connect_cursor(port=8000) # global (~/.cursor/mcp.json)
app.connect_cursor(port=8000, scope="project") # project (.cursor/mcp.json)
# standalone
from simcpi import connect_cursor
connect_cursor("my-api", port=8000)
connect_cursor("prod", url="https://myapi.com/mcp/mcp")
8. get_client() — in-process client
When you're already inside the server process, skip the HTTP round-trip:
client = app.get_client()
result = await client.run("Add 42 and 58")
MCPApi parameters
app = MCPApi(
title="my-api", # API name — used as key in Claude/Cursor config
provider="openai", # "openai" or "anthropic" — pre-fills MCPark UI
api_key="sk-...", # pre-fills MCPark UI, used by get_client()
base_url="https://...", # custom LLM endpoint (AICredits, OpenRouter, etc.)
model="gpt-4o", # pre-fills model in MCPark UI
description="...", # shown in Swagger + MCP server instructions
mcp_path="/mcp", # MCP transport prefix (default: /mcp)
mcpark_path="/mcpark", # MCPark UI path, set None to disable
version="1.0.0",
)
Endpoints created automatically
| Path | Description |
|---|---|
GET /docs |
Swagger UI |
GET /mcpark |
MCPark visual testing UI |
POST /mcp/mcp |
MCP streamable-HTTP transport |
GET /files/{token}/{filename} |
File serving for serve_file() |
Supported providers
Any OpenAI-compatible provider works via base_url:
| Provider | Notes |
|---|---|
| OpenAI | model="gpt-4o" |
| Anthropic | provider="anthropic", model="claude-sonnet-4-20250514" |
| OpenRouter | custom base_url |
| AICredits | custom base_url |
| Any OpenAI-compatible API | custom base_url |
Requirements
- Python 3.10+
- fastapi, fastmcp, pydantic, uvicorn, openai, anthropic
Note: simcpi currently requires
fastmcp==3.2.4specifically. Newer versions introduce breaking changes that are being addressed — compatibility will be expanded in future releases.
Optional (pip install simcpi[files]):
- pandas + openpyxl — Excel / CSV output from DataFrames
- matplotlib — PNG / PDF output from Figures
License
MIT
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file simcpi-0.3.0.tar.gz.
File metadata
- Download URL: simcpi-0.3.0.tar.gz
- Upload date:
- Size: 30.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fd7fb463fda69b0a1a9072b190f7b7ea42dcaec62a8699f2746a93071e367be2
|
|
| MD5 |
7084040dd31ea8935b9276519683060b
|
|
| BLAKE2b-256 |
7d51cf75f520d0f798f14fbc4a7b1dd8afb357524c94fbaf7b4754c3106b9d8c
|
File details
Details for the file simcpi-0.3.0-py3-none-any.whl.
File metadata
- Download URL: simcpi-0.3.0-py3-none-any.whl
- Upload date:
- Size: 27.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
25334f36c31c8b8c89bb444aaf453f33e050ddbe3057a5a84fb3e646e7a1e78c
|
|
| MD5 |
8078fd5f4ed6f24724e653c13b60feb2
|
|
| BLAKE2b-256 |
1eb30023b110b80995fb939f49a0df7810dcddc4d4d6e70e7b5afcd807a98ade
|