Repository navigation
MCP Tools Reference
MCE exposes exactly 4 tools + 1 prompt to the LLM. This intentionally small surface area keeps token usage low and enforces a consistent interaction pattern.
| Tool | When to use |
|---|---|
list_servers |
First call in any session — discover what APIs are available |
get_functions |
Before writing any code — inspect signatures and return types |
execute_code |
Run Python code against one or more APIs |
run_cached_code |
Reuse a previous execution with different input values |
| Prompt | When to use |
|---|---|
reusable_code_guide |
Remind the LLM of the top-level variable pattern |
graph LR
A[list_servers] --> B[get_functions] --> C[execute_code] --> D["run_cached_code (optional)"]
get_functions must be called before execute_code. It returns the exact import_statement, parameter names, and response schema. Guessing produces broken code.
Returns a compact index of all compiled API servers and their available functions.
Input: none
Output:
{
"sandbox_libraries": ["httpx", "pydantic", "orjson"],
"servers": [
{
"name": "weather",
"summary": "Weather data for US locations",
"functions": [
{ "name": "get_current_weather", "summary": "Get current conditions for a location" },
{ "name": "get_forecast", "summary": "Get 7-day forecast" }
]
}
]
}sandbox_libraries lists the packages available inside the Docker container. Do not import anything else.
Batch-fetches full details for 1–5 functions. Returns typed signatures, parameter descriptions, response schemas, and a ready-to-use import statement.
Input:
[
{ "server_name": "weather", "function_name": "get_current_weather" },
{ "server_name": "weather", "function_name": "get_forecast" }
]Output:
{
"functions": [
{
"server_name": "weather",
"function_name": "get_current_weather",
"import_statement": "from weather.functions import get_current_weather",
"parameters": [
{
"name": "city",
"type": "str",
"required": true,
"description": "City name, e.g. 'London'"
},
{
"name": "units",
"type": "str",
"required": false,
"default": "metric",
"description": "Temperature units: metric | imperial"
}
],
"return_type": "GetCurrentWeatherResponse",
"usage_example": "class GetCurrentWeatherResponse(TypedDict, total=False):\n temperature: float\n condition: str\n humidity: int\n\ndef get_current_weather(city: str, units: str = 'metric') -> GetCurrentWeatherResponse:\n ..."
}
]
}At compile time, MCE parses each endpoint's response schema and generates a TypedDict class. This lets the LLM chain calls with confidence — field names and types are explicit, not guessed:
class GetCurrentWeatherResponse(TypedDict, total=False):
temperature: float
condition: str
humidity: int
wind_speed: float
def get_current_weather(city: str, units: str = "metric") -> GetCurrentWeatherResponse:
...Functions without a parseable response schema fall back to -> Any.
Runs Python code inside an isolated Docker sandbox and returns the result.
Input:
| Parameter | Type | Description |
|---|---|---|
code |
str |
Python source code. Must follow the code contract below. |
description |
str |
Short human-readable description. Used to index the cache for future lookup. |
Output (success):
{
"success": true,
"data": { "temperature": 15.2, "condition": "Cloudy" },
"cache_id": "abc123def456...",
"execution_time_ms": 312
}Output (failure):
{
"success": false,
"error": "SecurityViolationError: import of 'os' is not allowed",
"cache_id": null
}Every execute_code payload must follow these rules:
-
Define a
main()function that takes no arguments. -
All dynamic values must be top-level variables —
main()reads them as globals. -
Call
result = main()at module level. -
Import only from compiled server modules (listed in
list_servers) and the sandbox libraries. -
No dangerous imports —
os,sys,subprocess,socket, and 70+ other modules are blocked by the AST guard.
# CORRECT — top-level variable pattern
from weather.functions import get_current_weather
city = "London" # top-level: will be injectable via run_cached_code
def main():
return get_current_weather(city=city, units="metric")
result = main()# INCORRECT — hardcoded value inside main()
def main():
return get_current_weather(city="London", units="metric") # cannot be reused
result = main()Every successful execute_code stores the code and description in the SQLite cache. The returned cache_id is a SHA-256 hash of the normalized code. Identical code always produces the same cache_id — subsequent calls are served from cache.
Re-executes a previously cached code snippet with different top-level variable values. This is the SIMD pattern — same code, different data.
Input:
| Parameter | Type | Description |
|---|---|---|
cache_id |
str |
The cache_id returned by a previous execute_code call. |
params |
dict | null |
Key-value pairs to inject as top-level variable assignments before execution. |
Output: Same structure as execute_code.
Example:
# First call
execute_code(...city = "London"...) → cache_id: "abc123"
# Reuse for Paris
run_cached_code("abc123", params={"city": "Paris"})
→ { "success": true, "data": { "temperature": 18.5, ... }, "cache_id": "def456" }
# Reuse for Tokyo
run_cached_code("abc123", params={"city": "Tokyo"})
→ { "success": true, "data": { "temperature": 12.0, ... }, "cache_id": "ghi789" }
params injects assignments before the cached code runs:
# What actually executes:
city = "Paris" # injected from params
# ...rest of cached code...
from weather.functions import get_current_weather
def main():
return get_current_weather(city=city, units="metric")
result = main()
paramskeys must match the top-level variable names in the original code exactly.
A built-in prompt that returns concise rules for writing parameterized, cacheable code. Call it at the beginning of a complex session or when the LLM seems to be hardcoding values.
Input: none
Output: A plain-text guide reminding the model to:
- Use top-level variables for all dynamic values
- Always call
get_functionsbefore writing code - Structure code so
run_cached_codecan replay it with new inputs
sequenceDiagram
participant User
participant LLM
participant MCE
User->>LLM: "What's the weather in London, Paris, and Tokyo?"
LLM->>MCE: list_servers()
MCE-->>LLM: { servers: [{ name: "weather", functions: [...] }] }
LLM->>MCE: get_functions([{ server_name: "weather", function_name: "get_current_weather" }])
MCE-->>LLM: { functions: [{ import_statement: "from weather.functions import ...", ... }] }
LLM->>MCE: execute_code(city="London", description="get weather by city")
MCE-->>LLM: { success: true, data: { temperature: 15.2 }, cache_id: "abc123" }
LLM->>MCE: run_cached_code("abc123", params={"city": "Paris"})
MCE-->>LLM: { success: true, data: { temperature: 18.5 }, cache_id: "def456" }
LLM->>MCE: run_cached_code("abc123", params={"city": "Tokyo"})
MCE-->>LLM: { success: true, data: { temperature: 12.0 }, cache_id: "ghi789" }
Three API calls. Only one piece of code. No token waste on repeated tool descriptions.
Next: Configuration Reference →