Make your first MCP and REST calls
Turn two Python functions into a local service. You will calculate a price
(12.5 × 2 = 25.0) and check stock (3 ≤ 10 gives true).
Prepare the example once, then choose REST or
MCP. No plugin, Docker or AI account is needed for REST or the
Python MCP client.
1. Prepare your workspace
Install Apizr with the pip instructions and keep core/ activated.
You need Python 3.11–3.15, curl, a POSIX shell on macOS/Linux and internet access
for the example and server dependencies. In the same terminal:
mkdir apizr-quickstart
cd apizr-quickstart
apizr --version
python3 -m venv runtime
apizr --version should report outerspace-apizr 0.4.6. The new runtime/
environment will hold the generated server's dependencies separately from Apizr.
2. Get the example
Download the three files from the recorded shop example:
mkdir shop
curl --fail --location https://raw.githubusercontent.com/Alien6-Studio/outerspace-apizr/37259eaf0a231d747f1ae0eb2f7ce94b2a2b306f/examples/repository-shop/api.py --output shop/api.py
curl --fail --location https://raw.githubusercontent.com/Alien6-Studio/outerspace-apizr/37259eaf0a231d747f1ae0eb2f7ce94b2a2b306f/examples/repository-shop/inventory.py --output shop/inventory.py
curl --fail --location https://raw.githubusercontent.com/Alien6-Studio/outerspace-apizr/37259eaf0a231d747f1ae0eb2f7ce94b2a2b306f/examples/repository-shop/pricing.py --output shop/pricing.py
ls shop
You should have api.py, inventory.py and pricing.py in shop/.
Create the configuration that selects api.quote and inventory.available
and permits Apizr to analyze this directory:
cat > readiness-direct.json <<'JSON'
{"execution":{"modes":["direct"]}}
JSON
cat > exposure-direct.json <<'JSON'
{"selection":{"include":["python:api:quote","python:inventory:available"]},"interfaces":["rest","mcp"],"execution":{"allowed":["direct"]}}
JSON
python - <<'PYTHON'
import json
from pathlib import Path
root = str(Path("shop").resolve(strict=True))
policy = {
"schema": "apizr.operator-policy/v1",
"grants": [{
"adapter": "repository",
"operation": "analyze",
"target": {"kind": "local", "root": root},
"permissions": ["source.analyze"],
}],
}
Path("operator.json").write_text(json.dumps(policy, indent=2) + "\n")
print("Authorized source:", root)
PYTHON
The three files sit beside shop/. Keep that directory at the same location;
its analysis permission names this exact path.
What do these three configuration files do?
- Readiness: describes the kind of interface Apizr can generate here.
- Exposure: selects the public functions and allows direct execution.
- Operator: permits source analysis of this directory. It grants neither execution nor publication.
Private helpers are included when needed, but do not become public functions. For another project, review its source, select its functions and create an operator grant for its own root.
3. Choose your interface
Both paths use the files prepared above; choose one. Generation reads the source without running it. The generated server uses direct mode: calls execute trusted Python with your user permissions. Use execution policies when you need fresh workers or execution limits.
Use REST
Generate the API and install its runtime dependencies:
apizr expose build rest shop --operator-policy operator.json --readiness-policy readiness-direct.json --policy exposure-direct.json --output-dir build/rest
runtime/bin/python -m pip install -r build/rest/requirements.txt
build/rest contains app.py, openapi.json and the supporting source.
Start the server on a free local port:
runtime/bin/uvicorn app:app --app-dir build/rest --host 127.0.0.1 --port 8000
Keep this terminal open. In a second terminal, call the two endpoints:
curl --fail --silent --show-error http://127.0.0.1:8000/capabilities/api.quote --header 'Content-Type: application/json' --data '{"unit_price":12.5,"quantity":2}'
curl --fail --silent --show-error http://127.0.0.1:8000/capabilities/inventory.available --header 'Content-Type: application/json' --data '{"stock":10,"requested":3}'
The response bodies are 25.0 and true (curl does not append a newline).
Open the interactive API docs to try other inputs.
Stop the server with Ctrl+C when finished.
You can now continue with your own code, or try MCP in the original terminal. For a different example, the REST guide includes a video showing function selection, HTTP calls and input validation.
Use MCP
Generate the MCP bundle
Generate a server for the same two functions:
apizr expose build mcp shop --operator-policy operator.json --readiness-policy readiness-direct.json --policy exposure-direct.json --output-dir build/mcp
build/mcp contains server.py, mcp-tools.json, requirements.txt and the
supporting source. The tools are named api.quote and inventory.available.
Use a new output directory if you generate again.
This is your business-function server. The optional Apizr analysis MCP server is a separate tool for inspecting projects; you do not need it here.
Connect a client and make two calls
Install the server dependencies in the runtime environment:
runtime/bin/python -m pip install -r build/mcp/requirements.txt
Choose your usual MCP client or the Python client below.
Connect your usual MCP client
For Claude Desktop on macOS, follow the
official local-server guide.
In Settings → Developer → Edit Config, add this entry to your existing
mcpServers object. Replace both /absolute/path/apizr-quickstart prefixes with
the full directory path printed by pwd; keep your other servers.
{
"mcpServers": {
"repository-shop": {
"command": "/absolute/path/apizr-quickstart/runtime/bin/python",
"args": [
"/absolute/path/apizr-quickstart/build/mcp/server.py",
"--transport",
"stdio"
]
}
}
}
Restart the client. Ask it to call api.quote with unit_price=12.5, quantity=2,
then inventory.available with stock=10, requested=3. Approve those calls and
look at the tool results: {"result": 25.0} and {"result": true}.
Other MCP clients can use the same command and arguments in their configuration.
Use Python — no AI account required
Run this from apizr-quickstart. It starts the server, discovers the tools and
calls both of them using the installed MCP SDK:
runtime/bin/python - <<'PY'
import asyncio
import json
import sys
from pathlib import Path
from mcp import Client, StdioServerParameters
async def main():
server = StdioServerParameters(
command=sys.executable,
args=[str(Path("build/mcp/server.py").resolve()), "--transport", "stdio"],
)
async with Client(server, read_timeout_seconds=10) as client:
names = sorted(tool.name for tool in (await client.list_tools()).tools)
assert names == ["api.quote", "inventory.available"], names
print("tools:", ", ".join(names))
quote = await client.call_tool("api.quote", {"unit_price": 12.5, "quantity": 2})
available = await client.call_tool("inventory.available", {"stock": 10, "requested": 3})
assert not quote.is_error and not available.is_error
assert quote.structured_content == {"result": 25.0}
assert available.structured_content == {"result": True}
print("quote:", json.dumps(quote.structured_content))
print("available:", json.dumps(available.structured_content))
asyncio.run(main())
PY
Expected output:
tools: api.quote, inventory.available
quote: {"result": 25.0}
available: {"result": true}
The client closes the connection when finished. The server may also write logs to stderr. See the official Python client documentation for more client options.
Starting server.py --transport stdio alone waits for an MCP client; it is not a
chat terminal. The MCP guide includes a video of discovery,
calls and error handling with the Python client.
Continue with your own code
Initialize your project, inspect its functions and select the ones to expose. Review its source and dependencies before running the generated service. Follow The full journey to understand each stage, or the exposure guide for repository configuration.
For Apizr 0.4.6, your selected function can use statically resolvable private helpers in other modules without exposing those helpers.
Working with experiments instead? Start with the notebook guide or Python and Jupyter tracking.