PromptisePromptise
Docs
GitHub
Promptise - AI Framework LogoPromptise

The foundation layer for agentic intelligence. Build, secure and operate autonomous AI systems with Promptise Foundry.

pip install promptise

[01] Foundry

  • MCPcast
  • The Promptise Agent
  • Reasoning Engine
  • MCP
  • Agent Runtime
  • Prompt Engineering
  • Execution Engine
  • Agent Identity

[02] Resources

  • Documentation
  • GitHub
  • Guides
  • Learning Paths
  • Questions

[03] Company

  • About
  • Terms of Service
  • Privacy Policy
  • Cookie Policy
  • Subprocessors

© 2026 Promptise by Manser Ventures. All rights reserved.

Open source · Python

← Learning paths> Agentic Engineering with Promptise Foundry
Lesson 21 / 71
30%

OpenAPI to MCP: Turn Any REST API into an MCP Server

Turn an OpenAPI spec into a working MCP server in minutes. Read-only by default, human approval on every write, and tested by a real agent.

Intermediate15 minModel Context Protocol (MCP)OpenAPI and REST APIsHuman-in-the-Loop
All 71 lessons↓
  1. ✓What Is an AI Agent? Definition, How It Works, Examples
  2. ✓How to Build an AI Agent in Python (2026 Guide)
  3. ✓What Is an Agent Harness? Parts and How to Build One
  4. ✓System Prompt Best Practices for AI Agents in Python
  5. ✓Best LLM for AI Agents: Test and Switch Models in Python
  6. ✓AI Agent YAML: Define and Run an Agent From a Config File
  7. ✓Streaming LLM Responses with FastAPI: Tokens and Tool Calls
  8. ✓Claude AI Agent with MCP: Use Anthropic Models in Promptise
  9. ✓Gemini AI Agent with MCP: Use Google Models in Promptise
  10. ✓Azure OpenAI Agent with MCP: Use Deployments in Promptise
  11. ✓OpenRouter AI Agent with MCP: Many Models in Promptise
  12. ✓Ollama AI Agent with MCP: Run Local Models in Promptise
  13. ✓LLM Semantic Cache and Model Fallback in Python
  14. ✓What Is MCP (Model Context Protocol)? A Developer's Guide
  15. ✓What Is Tool Calling? LLM Function Calling Explained
  16. ✓MCP Tools vs Resources vs Prompts (With Python Examples)
  17. ✓How to Connect MCP Servers to Your AI Agent in Python
  18. ✓Build a Production MCP Server in Python (Step by Step)
  19. ✓MCP Transports: stdio vs Streamable HTTP vs SSE (Python)
  20. ✓FastAPI MCP Server: Turn Your FastAPI App into MCP Tools
  21. 21OpenAPI to MCP: Turn Any REST API into an MCP Server
  22. 22MCP Authentication in Python: API Keys, JWT and OAuth
  23. 23Build an Authenticated MCP Server for Your AI Agent (Python)
  24. 24MCP Security Best Practices: A Checklist for MCP Servers
  25. 25Testing MCP Servers in Python: pytest Without a Live Server
  26. 26Too Many MCP Tools? LLM Tool Selection, Measured
  27. 27MCP Server Caching, Timeouts and Circuit Breakers in Python
  28. 28MCP vs CLI for AI Agents: Tokens, Security, When to Use Each
  29. 29AI Agent Audit Logging: Tamper-Evident Logs in Python
  30. 30AI Agent Human in the Loop: Approve Tool Calls in Python
  31. 31AI Agent Prompt Injection: How to Defend Against It
  32. 32Build a Multi-Agent System in Python (and When Not To)
  33. 33Context Engineering for AI Agents: Context Window Management
  34. 34How to Deploy an AI Agent with Docker (2026 Guide)
  35. 35LLM Tool Rate Limiting: Per-Client Limits for MCP Servers
  36. 36MCP Long-Running Tasks: Background Jobs in Python
  37. 37MCP Server Monitoring: Prometheus, OpenTelemetry and Logs
  38. 38Python AI Agent Frameworks Compared: Which to Choose (2026)
  39. 39Self-Improving AI Agent: Learn From Tool Failures in Python
  40. 40AI Agent Identity: Authenticate Agents to MCP Servers
  41. 41Code-Action Agents in Python: One Program vs 37 Tool Calls
  42. 42Multi-Tenant AI Agents: Keep Each Customer's Data Apart
  43. 43What Is Agentic RAG? How It Differs from Classic RAG
  44. 44AI Agent Memory in Python: Short-Term and Long-Term
  45. 45Agentic RAG in Python: Give Your AI Agent a Search Tool
  46. 46AI Agent Reasoning Patterns in Python: 9 Patterns Tested
  47. 47Plan-and-Execute Agents in Python: Beyond the ReAct Loop
  48. 48Self-Modifying AI Agent in Python: Open Mode & Limits
  49. 49CrewAI Alternatives: 7 Options and How to Migrate (2026)
  50. 50Migrate from AutoGen: Agent Framework, Promptise and More
  51. 51What Is an LLM Guardrail? Types, Examples and Limits
  52. 52AI Agent Guardrails in Python: Block Injection, Redact PII
  53. 53AI Agent Code Execution Sandbox: Run LLM Code Safely
  54. 54AI Agent Production Checklist: What to Check Before Launch
  55. 55Approval Fatigue: Auto-Approve Safe AI Agent Actions
  56. 56What Is Human in the Loop AI? Approvals for AI Agents
  57. 57AI Agent Notifications: Webhook and Slack Alerts in Python
  58. 58AI Agent Observability: Trace Tool Calls, Tokens and Cost
  59. 59AI Agent Stuck in a Loop? How to Detect and Stop It
  60. 60How to Run, Debug and Deploy an AI Agent in Python
  61. 61AI Agent Cost Tracking: Tokens and Cost per Run and User
  62. 62How to Evaluate AI Agents: Evals, LLM Judges and pass^k
  63. 63AI Agent Cost Control: Budgets That Stop Runaway Spend
  64. 64How to Test LLM Prompts and AI Agents in Python
  65. 65AI Agent Evals in CI: Run Them on Every Pull Request
  66. 66Autonomous AI Agent Runtime: Budgets, Crash Recovery, API
  67. 67Event-Driven AI Agents: Webhook, Cron and File Triggers
  68. 68Durable AI Agents: Crash Recovery and Safe Resume
  69. 69Deploy AI Agents in Production: Long-Running Python Agents
  70. 70What Is an AI Agent Framework? When You Need One
  71. 71LangGraph Alternatives: 7 Options and How to Migrate (2026)

Lessons in this path

  1. ✓What Is an AI Agent? Definition, How It Works, Examples
  2. ✓How to Build an AI Agent in Python (2026 Guide)
  3. ✓What Is an Agent Harness? Parts and How to Build One
  4. ✓System Prompt Best Practices for AI Agents in Python
  5. ✓Best LLM for AI Agents: Test and Switch Models in Python
  6. ✓AI Agent YAML: Define and Run an Agent From a Config File
  7. ✓Streaming LLM Responses with FastAPI: Tokens and Tool Calls
  8. ✓Claude AI Agent with MCP: Use Anthropic Models in Promptise
  9. ✓Gemini AI Agent with MCP: Use Google Models in Promptise
  10. ✓Azure OpenAI Agent with MCP: Use Deployments in Promptise
  11. ✓OpenRouter AI Agent with MCP: Many Models in Promptise
  12. ✓Ollama AI Agent with MCP: Run Local Models in Promptise
  13. ✓LLM Semantic Cache and Model Fallback in Python
  14. ✓What Is MCP (Model Context Protocol)? A Developer's Guide
  15. ✓What Is Tool Calling? LLM Function Calling Explained
  16. ✓MCP Tools vs Resources vs Prompts (With Python Examples)
  17. ✓How to Connect MCP Servers to Your AI Agent in Python
  18. ✓Build a Production MCP Server in Python (Step by Step)
  19. ✓MCP Transports: stdio vs Streamable HTTP vs SSE (Python)
  20. ✓FastAPI MCP Server: Turn Your FastAPI App into MCP Tools
  21. 21OpenAPI to MCP: Turn Any REST API into an MCP Server
  22. 22MCP Authentication in Python: API Keys, JWT and OAuth
  23. 23Build an Authenticated MCP Server for Your AI Agent (Python)
  24. 24MCP Security Best Practices: A Checklist for MCP Servers
  25. 25Testing MCP Servers in Python: pytest Without a Live Server
  26. 26Too Many MCP Tools? LLM Tool Selection, Measured
  27. 27MCP Server Caching, Timeouts and Circuit Breakers in Python
  28. 28MCP vs CLI for AI Agents: Tokens, Security, When to Use Each
  29. 29AI Agent Audit Logging: Tamper-Evident Logs in Python
  30. 30AI Agent Human in the Loop: Approve Tool Calls in Python
  31. 31AI Agent Prompt Injection: How to Defend Against It
  32. 32Build a Multi-Agent System in Python (and When Not To)
  33. 33Context Engineering for AI Agents: Context Window Management
  34. 34How to Deploy an AI Agent with Docker (2026 Guide)
  35. 35LLM Tool Rate Limiting: Per-Client Limits for MCP Servers
  36. 36MCP Long-Running Tasks: Background Jobs in Python
  37. 37MCP Server Monitoring: Prometheus, OpenTelemetry and Logs
  38. 38Python AI Agent Frameworks Compared: Which to Choose (2026)
  39. 39Self-Improving AI Agent: Learn From Tool Failures in Python
  40. 40AI Agent Identity: Authenticate Agents to MCP Servers
  41. 41Code-Action Agents in Python: One Program vs 37 Tool Calls
  42. 42Multi-Tenant AI Agents: Keep Each Customer's Data Apart
  43. 43What Is Agentic RAG? How It Differs from Classic RAG
  44. 44AI Agent Memory in Python: Short-Term and Long-Term
  45. 45Agentic RAG in Python: Give Your AI Agent a Search Tool
  46. 46AI Agent Reasoning Patterns in Python: 9 Patterns Tested
  47. 47Plan-and-Execute Agents in Python: Beyond the ReAct Loop
  48. 48Self-Modifying AI Agent in Python: Open Mode & Limits
  49. 49CrewAI Alternatives: 7 Options and How to Migrate (2026)
  50. 50Migrate from AutoGen: Agent Framework, Promptise and More
  51. 51What Is an LLM Guardrail? Types, Examples and Limits
  52. 52AI Agent Guardrails in Python: Block Injection, Redact PII
  53. 53AI Agent Code Execution Sandbox: Run LLM Code Safely
  54. 54AI Agent Production Checklist: What to Check Before Launch
  55. 55Approval Fatigue: Auto-Approve Safe AI Agent Actions
  56. 56What Is Human in the Loop AI? Approvals for AI Agents
  57. 57AI Agent Notifications: Webhook and Slack Alerts in Python
  58. 58AI Agent Observability: Trace Tool Calls, Tokens and Cost
  59. 59AI Agent Stuck in a Loop? How to Detect and Stop It
  60. 60How to Run, Debug and Deploy an AI Agent in Python
  61. 61AI Agent Cost Tracking: Tokens and Cost per Run and User
  62. 62How to Evaluate AI Agents: Evals, LLM Judges and pass^k
  63. 63AI Agent Cost Control: Budgets That Stop Runaway Spend
  64. 64How to Test LLM Prompts and AI Agents in Python
  65. 65AI Agent Evals in CI: Run Them on Every Pull Request
  66. 66Autonomous AI Agent Runtime: Budgets, Crash Recovery, API
  67. 67Event-Driven AI Agents: Webhook, Cron and File Triggers
  68. 68Durable AI Agents: Crash Recovery and Safe Resume
  69. 69Deploy AI Agents in Production: Long-Running Python Agents
  70. 70What Is an AI Agent Framework? When You Need One
  71. 71LangGraph Alternatives: 7 Options and How to Migrate (2026)
← Path overview

You already have an API, and you want AI agents to use it. The quickest honest route is to turn its OpenAPI spec into an MCP server, but a converter that maps every endpoint to a tool tends to produce something agents struggle with: dozens of look-alike tools, and write access nobody signed off on. This guide uses MCPcast, the OpenAPI-to-MCP generator in Promptise Foundry, to do it properly. You'll generate a server from a real API, read what it decided and why, allow writes behind human approval, connect it to Claude, and measure how well an agent actually uses it. Every command was run against Promptise Foundry 1.3.1 and the public Swagger Petstore, with gpt-5-mini wherever a model is needed, and the output is what it printed.

[01]

How do you turn a REST API into an MCP server?

Point MCPcast at your OpenAPI spec:

>_Terminal
pip install promptise
promptise mcpcast https://petstore3.swagger.io/api/v3/openapi.json

That one command reads the spec, sorts every operation by risk, decides which ones become tools, and writes a complete Python project: the MCP server, a test per tool, a Dockerfile, and copy-paste setup for Claude Desktop, Claude Code and Cursor. By default the server can only read. Anything that writes has to be switched on, and then it waits for a person to approve it.


[02]

Why not one tool per endpoint?

Mapping each endpoint to a tool is easy, and it's where most conversions go wrong. A large API turns into a wall of tools whose names come from the URL structure, and the model has to pick the right one from descriptions written for human developers. Worse, every POST, PUT and DELETE becomes something an agent can call on its own.

MCPcast puts a few deliberate steps between the spec and the server:

Rendering diagram…

The plan file in the middle is the point. Every decision lands there in plain YAML, with a reason, before any code exists. You can read it, change it, and regenerate.


[03]

What you need

  • Python 3.10 or newer, and pip install "promptise>=1.3.1".

  • An OpenAPI spec, as a URL or a file. If your API is built with FastAPI, it already serves one at /openapi.json.

  • Optionally, an API key for a model provider. MCPcast uses a model to design the tools and to test them, but --no-curate runs fully offline.


[04]

Turn the Petstore API into an MCP server, step by step

The Swagger Petstore is a public demo API with 19 operations for pets, orders and users. It's a fair stand-in for a real product API.

Step 01

Generate a read-only server

Start offline, without a model, so every decision comes from MCPcast's own rules:

>_Terminal
promptise mcpcast https://petstore3.swagger.io/api/v3/openapi.json --no-curate -o petstore-readonly
Output
Parsed 19 operations from https://petstore3.swagger.io/api/v3/openapi.json;
profile=read-only auth=passthrough
╭────────────────────────────────── mcpcast ───────────────────────────────────╮
│ petstore → petstore-readonly/                                                │
│   tools: 6  (0 require human approval)                                       │
│   not exposed: 13 operations (with reasons in the plan)                      │
│   files: petstore_mcp/ (10 modules), tests/ (2), mcpcast.plan.yaml,          │
│ server.py, README.md, pyproject.toml, Dockerfile, .env.example, .gitignore   │
╰──────────────────────────────────────────────────────────────────────────────╯

Six tools out of 19 operations. That's the read-only profile at work: searching pets, reading orders and users stays in, and everything that changes data stays out.

Step 02

Read the plan

Open mcpcast.plan.yaml. Each tool lists its risk class and the API routes it calls. At the bottom, every operation that didn't become a tool is listed with the reason:

YAMLmcpcast.plan.yaml
1
2
3
4
5
6
7
8
9
10
dropped:
- operation_id: addPet
  reason: write operation excluded by profile 'read-only'
- operation_id: getPetById
  reason: 'unsupported by mcpcast: requires an API key in header ''api_key'', but this server presents
    its credential in header ''Authorization'''
- operation_id: deletePet
  reason: destructive operation excluded by profile 'read-only'
- operation_id: uploadFile
  reason: 'unsupported by mcpcast: unsupported request body media type application/octet-stream'

There are two kinds of reasons. Some operations are left out on purpose, because of the safety profile. Others MCPcast can't support yet, and it says so instead of generating a tool that would fail later. The full list is longer; this is an excerpt.

The risk class comes from fixed rules, not guesswork. GET is a read. DELETE, and verbs like cancel or revoke, are destructive. Words like charge or refund make an operation financial. Most other POST, PUT and PATCH calls are writes. The profile decides which classes make it in:

Profile

Reads

Writes

Destructive and financial

read-only (default)

Yes

No

No

standard

Yes

Yes, with approval

No

full

Yes

Yes, with approval

Yes, with approval

Step 03

Allow writes, behind approval

To let agents create pets and place orders, switch to the standard profile. --auth env-token makes the server send one API token, from an environment variable, on every call it makes to your API. It's the simplest setup for a desktop client.

>_Terminal
promptise mcpcast https://petstore3.swagger.io/api/v3/openapi.json \
  --no-curate --profile standard --auth env-token -o petstore-mcp
Output
Parsed 19 operations from https://petstore3.swagger.io/api/v3/openapi.json;
profile=standard auth=env-token
╭────────────────────────────────── mcpcast ───────────────────────────────────╮
│ petstore → petstore-mcp/                                                     │
│   tools: 13  (7 require human approval)                                      │
│   not exposed: 6 operations (with reasons in the plan)                       │
│   files: petstore_mcp/ (10 modules), tests/ (2), mcpcast.plan.yaml,          │
│ server.py, README.md, pyproject.toml, Dockerfile, .env.example, .gitignore   │
╰──────────────────────────────────────────────────────────────────────────────╯

Thirteen tools now, and all seven writes need a person to approve them. The three deletes are still out: standard never generates destructive operations.

Step 04

Run the generated tests

The project is an ordinary Python package with its own tests. They run against a fake copy of your API, so they never touch real data:

>_Terminal
cd petstore-mcp
pip install -e ".[dev]"
pytest
Output
…
tests/test_tools.py ......................                               [100%]

============================== 22 passed in 0.47s ==============================

Each tool is checked three ways: it's listed, it calls the right route, and it's gated when it should be. One more test checks that every parameter advertises the schema from your spec, so the agent sees allowed values and limits, not just a type.

Step 05

Connect it to Claude

The generated README.md includes setup for Claude Desktop, Claude Code, Cursor and Promptise agents, filled in for your server. For Claude Desktop, add it to claude_desktop_config.json:

JSONclaude_desktop_config.json
1
2
3
4
5
6
7
8
9
{
  "mcpServers": {
    "petstore": {
      "command": "python",
      "args": ["/absolute/path/to/server.py"],
      "env": { "MCPCAST_UPSTREAM_TOKEN": "Bearer <your API token>" }
    }
  }
}

For Claude Code, it's one command:

>_Terminal
claude mcp add petstore -e MCPCAST_UPSTREAM_TOKEN="Bearer <your API token>" -- python /absolute/path/to/server.py
Note

Put the token in the client's config, as shown. A desktop app started from the dock doesn't see variables you export in a terminal.


[05]

What happens when an agent tries to change something?

Here's a Promptise agent using the generated server. It's the same pattern as in How to Connect MCP Servers to Your AI Agent in Python:

Pythonagent.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import asyncio

from promptise import build_agent
from promptise.config import StdioServerSpec


async def main():
    agent = await build_agent(
        model="openai:gpt-5-mini",
        servers={
            "petstore": StdioServerSpec(
                command="python",
                args=["petstore-mcp/server.py"],
                env={"MCPCAST_UPSTREAM_TOKEN": "Bearer demo"},
            ),
        },
        instructions="You help staff run the pet store. Use your tools.",
        trace_tools=True,
    )
    try:
        for question in [
            "How many pets are available right now?",
            "Add a new pet called Rex with the photo https://example.com/rex.jpg.",
        ]:
            result = await agent.ainvoke({"messages": [{"role": "user", "content": question}]})
            print(">>>", result["messages"][-1].content)
    finally:
        await agent.shutdown()


asyncio.run(main())
Output
→ Invoking tool: find_pets_by_status with {'status': 'available'}
✔ Tool result from find_pets_by_status: [{"id": 1, "name": "Pet1", "photoUrls": ["test1", "test2"], "tags": [], "status": "available"}]
>>> There is 1 pet available right now: Pet1 (ID 1). Would you like to see more details or the full list?
→ Invoking tool: add_pet with {'name': 'Rex', 'photoUrls': ['https://example.com/rex.jpg'], 'status': 'available'}
Elicitation request failed (McpError: Elicitation not supported) — treated as declined
✖ add_pet error: APPROVAL_DENIED: Approval denied for tool 'add_pet': client declined or returned an invalid elicitation response
→ Invoking tool: add_pet with {'name': 'Rex', 'photoUrls': ['https://example.com/rex.jpg'], 'status': 'available'}
Elicitation request failed (McpError: Elicitation not supported) — treated as declined
✖ add_pet error: APPROVAL_DENIED: Approval denied for tool 'add_pet': client declined or returned an invalid elicitation response
>>> I tried to add Rex but the store API returned an "approval denied" error and the pet wasn’t created.

Before I retry, please confirm or provide any of these details (I can use defaults if you prefer):
- Status (default: available)
- ID (optional — I can generate one)
- Category (optional)
- Tags (optional)

How would you like me to proceed? (e.g., "Retry with defaults", or provide ID/category/tags)

The read went straight through and returned live data. The write was stopped, twice: the model tried add_pet again after the first denial, then gave up and asked the user what to do. Look at what it sent: the name and photo the user gave, plus a status nobody asked for. The optional id, category and tags it left out, and since Promptise 1.3.1 an agent's tool call carries only the arguments the model gave, so they never reach the server as null. Small guesses like that status are exactly why a person should see a write before it reaches your API.

By default, approval uses MCP elicitation: the server asks the client to confirm the exact call, and the client asks a person. The agent above has nobody to ask. Give it an approval handler with approval=, and the server's question goes to your handler. This one prints it and declines:

Pythonagent_review.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import asyncio

from promptise import build_agent
from promptise.approval import ApprovalDecision, ApprovalRequest
from promptise.config import StdioServerSpec


async def reviewer(request: ApprovalRequest) -> ApprovalDecision:
    print(request.context_summary)
    return ApprovalDecision(approved=False, reason="Not today.")


async def main():
    agent = await build_agent(
        model="openai:gpt-5-mini",
        servers={
            "petstore": StdioServerSpec(
                command="python",
                args=["petstore-mcp/server.py"],
                env={"MCPCAST_UPSTREAM_TOKEN": "Bearer demo"},
            ),
        },
        instructions="You help staff run the pet store. Use your tools.",
        approval=reviewer,
        trace_tools=True,
    )
    try:
        result = await agent.ainvoke({"messages": [{"role": "user", "content": "Add a new pet called Rex with the photo https://example.com/rex.jpg."}]})
        print(">>>", result["messages"][-1].content)
    finally:
        await agent.shutdown()


asyncio.run(main())
Output
→ Invoking tool: add_pet with {'name': 'Rex', 'photoUrls': ['https://example.com/rex.jpg'], 'status': 'available'}
Server 'petstore' asks: Approval required: call tool 'add_pet' with arguments {'name': 'Rex', 'photoUrls': ['https://example.com/rex.jpg'], 'id': None, 'category': None, 'tags': None, 'status': 'available'}. Approve?
✖ add_pet error: APPROVAL_DENIED: Approval denied for tool 'add_pet': client declined or returned an invalid elicitation response
>>> Sorry — I wasn’t able to add Rex because the store-add tool call was blocked by an approval error on my side.
…

The handler saw the call as the server checked it: the three arguments the model sent, and the optional ones it left out, shown as None. The server leaves those out of the request it sends to the API. When the handler approves, the call goes through and the API's answer comes back. When it declines, or the client can't ask at all, the answer is APPROVAL_DENIED:

Rendering diagram…

That's why the first agent was refused: it was built without an approval handler, so it tells the server it can't answer, and the server fails closed. In production your handler asks a real person, for example through a webhook or a queue. For agents that run without anyone at the client, generate the server with --auth api-key instead. Approvals then use the pending mode: a gated call waits until a different person on the same tenant, with the approver role, approves it through the generated approval tools. Human approval in the docs covers both modes.


[06]

Let a model design the tools

So far every tool mirrors one endpoint. Leave out --no-curate and MCPcast asks a model to design the tool set: clearer names, better descriptions, and related endpoints merged into one tool. By default it uses openai:gpt-5-mini, and --model picks another. This run also adds --eval, so set your model provider's key and MCPCAST_UPSTREAM_TOKEN first; the evaluation reads from the live API.

>_Terminal
promptise mcpcast https://petstore3.swagger.io/api/v3/openapi.json \
  --profile standard --auth env-token --eval --eval-tasks 8 -o petstore-curated
Output
Parsed 19 operations from https://petstore3.swagger.io/api/v3/openapi.json;
profile=standard auth=env-token
Curating with openai:gpt-5-mini (budget 25 tools)…
╭────────────────────────────────── mcpcast ───────────────────────────────────╮
│ petstore → petstore-curated/                                                 │
│   tools: 12  (7 require human approval)                                      │
│   not exposed: 6 operations (with reasons in the plan)                       │
│   files: petstore_mcp/ (10 modules), tests/ (2), mcpcast.plan.yaml,          │
│ server.py, README.md, pyproject.toml, Dockerfile, .env.example, .gitignore   │
╰──────────────────────────────────────────────────────────────────────────────╯
Evaluating with openai:gpt-5-mini (8 tasks)…
Agent Readiness: B  (6/8 tasks succeeded)
  ✗ `get_order` (task t6: orderId=3) called the API with identifiers from the
plan's example that it does not recognise — the example teaches both the task
writer and the agent, so replace those example values with ones that exist
  • 4 tools not covered by any task: `create_user`, `logout_user`, `get_user`,
`update_user` — raise --eval-tasks to score them
Report: petstore-curated/eval/report.md  Tasks: petstore-curated/eval/tasks.yaml

Thirteen tools became twelve. The two pet searches, by status and by tags, became a single find_pets that calls whichever route fits the arguments. Names got shorter and clearer: get_order_by_id became get_order, get_user_by_name became get_user, and create_users_with_list_input became create_users_bulk. The model proposes, but MCPcast's code decides: it checks every proposal, and it never lets a tool's risk drop below what the rules assigned.

MCPcast also checks what the model writes. A description that sends agents to a tool the profile removed, such as a delete_pet under standard, goes back to the model with the list of tools it may name, and if the last attempt still names one, that sentence is cut. The model can still get other details wrong, which is why the plan stays in your hands. To change a description, edit it in mcpcast.plan.yaml and regenerate from the plan:

>_Terminal
promptise mcpcast mcpcast.plan.yaml

Regenerating rewrites the server code from the plan, without calling the model again. It also re-checks the plan's safety rules. Try to remove the approval from a write and it refuses:

Output
Error: invalid plan:
1 validation error for MCPcastPlan
(root)
  Value error, tool 'add_pet' is write: profile 'standard' requires requires_approval=true

[07]

Measure how well agents use it

--eval is the part most converters skip. A model writes realistic tasks for your API, and a real agent tries to complete each one against the generated server. Reads hit your live API. Writes go to mocks built from the spec, so an evaluation never changes real data. The report for the run above:

Measure

Result

Agent Readiness

B, score 0.85

Tasks completed

6 of 8

Right tool chosen first

100%

Parameter errors

0%

The score is 0.6 × the task success rate plus 0.4 × the rate of choosing the right tool first. The agent chose the right tool first in every task, so neither failure was a wrong pick. One task asked for order 3, the example the model had written into the plan for get_order, and the Petstore has no order 3, so it answered 404. The report blames the plan's example, rightly: an example teaches both the task writer and the agent, so give it an order that exists. The other task logged in, and the Petstore answered with plain text labelled as JSON, which the generated server refuses rather than guess at. The report also flagged four tools no task had covered, and suggested raising --eval-tasks to test them too.


[08]

Honest limits

MCPcast is upfront about what it doesn't do yet, and you'll want to know before you rely on it:

  • OpenAPI only. Postman collections and GraphQL aren't supported.

  • No OAuth 2.1 yet. Use a bearer token or pre-shared API keys.

  • Some operations are skipped. Operations that need header or cookie parameters, or take multipart, plain-text or binary uploads, are left out with a reason in the plan. Petstore's uploadFile is one of them.

  • Your spec is trusted input. MCPcast fetches spec URLs without checking for private networks and builds code from what the spec says, so only use specs you'd trust as much as code you run.

  • Claude Desktop needs stdio. Its config can't send headers to an HTTP server, so use the stdio setup shown above.

The full list is in the limits section of the MCPcast reference.


[09]

Frequently asked questions

What is MCPcast?

MCPcast is the part of Promptise Foundry that turns an existing API into an MCP server. It reads an OpenAPI spec and generates a complete, editable Python project. Like the rest of Promptise Foundry, it's open source under the Apache 2.0 license.

Do I need an LLM to use it?

No. With --no-curate, everything runs offline and deterministically. A model is only used to design nicer tools and to run --eval.

Can I edit the generated code?

Edit mcpcast.plan.yaml and regenerate; the server code is rebuilt from it. Your own files and tests in the project are left alone, while generated modules are rewritten. pyproject.toml, the Dockerfile and .env.example are yours after the first run.

Does it work with a FastAPI app?

Yes. FastAPI serves its OpenAPI spec at /openapi.json, so you can point MCPcast at your running app. The guide for Python APIs walks through it.

Is there a guided setup?

Yes. Run promptise mcpcast with no arguments for an interactive setup in your terminal. It walks you through the spec, the model, the safety profile and the auth mode, shows every tool for review, and writes nothing until you confirm. At the end it prints the equivalent one-line command. See Guided setup.


[10]

Where to go next

  • MCPcast reference: every option, the plan format and the approval modes.

  • Recipes for Stripe, GitHub and your own app: real APIs, step by step.

  • Approval gates: how human approval works in any Promptise MCP server.

  • Deployment: running your server in production.

  • How to Connect MCP Servers to Your AI Agent in Python: use your new server from your own agent.

← PreviousFastAPI MCP Server: Turn Your FastAPI App into MCP ToolsNext lesson →MCP Authentication in Python: API Keys, JWT and OAuth