mcp-from-api
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@mcp-from-apiWhat endpoints are available for candidates?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
API-First SaaS + MCP: A Pattern Guide
How to expose any REST API to an LLM — without duplicating schemas, hardcoding rules, or rebuilding your backend.
This repo is a working reference implementation of the pattern (Python + Flask + FastMCP), but the pattern itself is language-agnostic. The concepts in the first half apply equally to a Node.js Express API, a Rails app, a Go service, or any HTTP backend.
User (natural language)
→ LLM
→ MCP Server (thin adapter — fetches everything from the API at runtime)
→ Your REST API (unchanged business logic)
→ Your databaseThe Problem
Most approaches to "LLM + API" land in one of two failure modes:
Approach | Problem |
Hardcode everything in the system prompt | Schemas drift, rules go stale, every API change needs an LLM update |
Give the LLM raw OpenAPI spec | All schemas in context every turn — most irrelevant, tokens wasted |
One MCP tool per endpoint | 25 endpoints = 25 tool schemas in context always |
The root issue: knowledge duplication. The API already knows its own shape. The goal is to make the LLM ask the API what it can do, rather than telling the LLM upfront.
Related MCP server: OpenAPI MCP Server
Why MCP Over Direct Function Calling?
You could skip MCP entirely and pass your OpenAPI spec to the LLM as a tool list. Many integrations do this. Here's why that diverges from this pattern at scale:
Direct function calling | MCP + discovery | |
Schema in context | All tools loaded every turn | Fetched on demand — only what's needed |
Client portability | Tied to one LLM client | Any MCP-compatible client (Claude Desktop, Cursor, custom agents) |
API change propagation | Regenerate and redeploy the tool list | Discovery routes return the latest state automatically |
Business rules | In the system prompt or tool descriptions | In the rules registry, fetched at runtime |
MCP server updates | N/A | Never needed — server has no API knowledge |
MCP also gives you a transport-agnostic boundary: the same MCP server works over stdio (subprocess), HTTP/SSE, or WebSocket, and works with any LLM that supports tool use — not just the one you started with.
The short version: direct function calling works for simple APIs and single clients. MCP with discovery works for APIs that change and systems that need to evolve.
The Pattern
Three layers, each with a single job.
Running example: this guide uses a recruiting CRM throughout — an API with four entities: accounts (client companies), contacts (hiring managers), candidates (job seekers), and placements (candidate ↔ account relationships at a given stage). It's a good fit because the entities have real dependencies: you can't create a placement without both a candidate and an account. Wherever you see "recruiting CRM" or "placement", substitute your own domain.
Layer 1 — Your API (add a registry + discovery routes)
Your existing business logic stays untouched. You add two things:
A metadata registry — a structured description of every endpoint, stored in one place. Not used for routing. Used only to answer the question "what can you do, and how?"
Each entry in the registry holds:
pathandmethoddescription— plain English, searchable by the LLMbody_schema— what the endpoint acceptsresponse_schema— what the endpoint returnstags— domain grouping (accounts, candidates, placements…)
Four discovery endpoints — meta-routes that expose the registry over HTTP:
Route | Returns |
| All endpoints, optionally filtered by domain |
| Endpoints matching a keyword |
| Full schema for one endpoint |
| Dependency rules and validations |
These four routes are the only contract between your API and the MCP layer. They don't change your existing routes.
Generating the registry. If your framework already produces an OpenAPI spec — FastAPI, NestJS, Spring Boot, Django REST Framework, Gin, Fastify — write a one-time converter that reads /openapi.json and writes your registry file. You get paths, methods, parameters, and response schemas for free. The one thing no tool can generate is the rules registry: business rules like "a placement requires a candidate" are domain knowledge that must be authored by hand.
Registry as a team contract. When the API team and the AI/LLM team are different people, the registry + discovery routes become a formal handoff point. The AI team can build and test the entire MCP layer against the four discovery routes — without knowing anything about how the backend is implemented, what database it uses, or how routes are structured internally. Any breaking change to the registry is immediately visible to the LLM layer without any coordination overhead.
Layer 2 — The MCP Server (a thin discovery adapter)
The MCP server owns zero API knowledge. No schemas, no hardcoded paths, no business rules. It exposes six tools that the LLM calls on demand:
Tool | What it does |
| (optional) Domain map with endpoint counts — use for 50+ endpoint APIs or when search returns too many unrelated results |
| Browse available endpoints by domain |
| Find the right endpoint — returns scored results ranked by relevance |
| Read the full input + output schema |
| Fetch dependency rules before any write |
| Execute the actual call |
The LLM discovers what it needs, when it needs it. Adding an endpoint to your API makes it immediately discoverable — no changes to the MCP server.
Why 5 tools instead of one per endpoint:
With 25 endpoints, registering each as a separate MCP tool loads all 25 schemas into context on every turn — most irrelevant. With 5 meta-tools, the LLM pays schema cost only for the endpoints relevant to the current task.
User: "show me all active candidates"
LLM → search_api_endpoints("active candidates")
← [{path: "/api/candidates", method: "GET", description: "List all candidates..."}]
LLM → get_api_endpoint_details("/api/candidates", "GET")
← {query_params: {status: "active|passive|placed|inactive"}, response_schema: {...}}
LLM → call_api("/api/candidates", "GET", {status: "active"})
← {candidates: [...], total: 3}3 small tool calls. Zero schemas for unrelated endpoints loaded into context.
Layer 3 — Rules and Restrictions
This is the most commonly skipped part of the pattern, and the one that matters most for complex APIs.
Every API has implicit business rules. In a recruiting CRM:
You can't create a placement without a candidate and an account
Deleting an account cascades to all its contacts
A placement stage must follow a valid progression
If you put these rules in the LLM's system prompt, you now have two sources of truth. The system prompt drifts. Rules go stale when the API changes. The LLM "remembers" rules it was told, not rules the API actually enforces.
The solution: a rules registry, owned by the API.
A rules registry maps every entity + action to:
dependencies— what must exist before this action, and how to verify itvalidations— field constraints the LLM should enforcecascade— what else gets affectedorder_hint— the correct creation order for multi-step operations
What one entry looks like (language-agnostic JSON structure):
{
"placements": {
"create": {
"dependencies": [
{
"entity": "candidates",
"field": "candidate_id",
"required": true,
"verify_with": "GET /api/candidates/{candidate_id}",
"reason": "Placement must reference an existing candidate",
"if_missing": "Create candidate first via POST /api/candidates"
},
{
"entity": "accounts",
"field": "account_id",
"required": true,
"verify_with": "GET /api/accounts/{account_id}",
"reason": "Placement must reference an existing account",
"if_missing": "Create account first via POST /api/accounts"
}
],
"validations": [
"stage must be one of: sourced, screening, interviewing, offered, placed, rejected"
],
"order_hint": "accounts → candidates → (contacts optional) → placements"
},
"delete": {
"dependencies": [],
"cascade": [],
"order_hint": "safe to delete — no child records depend on placements"
}
}
}The structure is just data — store it as JSON, YAML, a module-level dict, a database table, whatever fits your stack. The only requirement is that it's readable by the /api/meta/rules route.
The LLM calls get_workflow_rules before any write. It gets back the exact checks to run. If a dependency is missing, it returns missing_dependency and stops — it never proceeds.
User: "place Alice at Acme Corp"
LLM → get_workflow_rules("placements", "create")
← dependencies: [candidate_id required, account_id required]
← order_hint: "accounts → candidates → placements"
LLM → search for "Alice" in candidates ✓ found: id=1
LLM → search for "Acme Corp" in accounts ✗ not found
LLM returns:
{
"status": "missing_dependency",
"summary": "Acme Corp does not exist as an account. Create it before the placement.",
"suggestions": ["Create account 'Acme Corp', then retry the placement"]
}Key principle: rules live in one place, fetched at runtime. Updating a rule requires changing only the registry — no LLM infrastructure changes.
Approach | Rule update requires |
Hardcoded in system prompt | Edit the CLI / agent config, redeploy |
Dynamic via rules registry | Edit the registry file — nothing else |
Full Write-Path Sequence
This is what the complete flow looks like for any create, update, or delete operation — combining Layer 2, Layer 3, and Layer 4:
User: "create a placement for Alice at Acme Corp"
│
├─ 1. DISCOVER
│ LLM → search_api_endpoints("create placement")
│ ← {path: "/api/placements", method: "POST", description: "..."}
│
│ LLM → get_api_endpoint_details("/api/placements", "POST")
│ ← {body_schema: {candidate_id, account_id, stage, ...}}
│
├─ 2. CHECK RULES
│ LLM → get_workflow_rules("placements", "create")
│ ← {dependencies: [candidate_id required, account_id required],
│ order_hint: "accounts → candidates → placements"}
│
├─ 3. VERIFY DEPENDENCIES
│ LLM → call_api("/api/candidates/search", "GET", {q: "Alice"})
│ ← found: {id: 1, name: "Alice Johnson"} ✓
│
│ LLM → call_api("/api/accounts", "GET", {q: "Acme Corp"})
│ ← not found ✗
│ → return missing_dependency, stop here
│
│ (if all dependencies exist, continue ↓)
│
├─ 4. HUMAN APPROVAL
│ Agent intercepts the call before it reaches the API
│ ╭─ Approval Required ─────────────────────────╮
│ │ POST /api/placements │
│ │ {"candidate_id": 1, "account_id": 5, ...} │
│ ╰─────────────────────────────────────────────╯
│ n → inject rejection into LLM context, stop
│ y → continue ↓
│
└─ 5. EXECUTE + RESPOND
LLM → call_api("/api/placements", "POST", body)
← {placement: {id: 12, stage: "sourced", ...}}
LLM returns structured envelope:
{"status": "success", "summary": "Placement created...", "data": {...}}Each step has a clear exit condition. The LLM never guesses — it checks rules, verifies existence, then acts.
Layer 4 — The Agent (human-in-the-loop + structured output)
Two more patterns at the LLM layer that complete the system:
Human-in-the-loop at the infrastructure layer.
Write operations are intercepted by the agent framework before they hit the API. The LLM calls the tool directly — the framework shows the approval prompt. The LLM never asks "shall I proceed?" in conversation (that causes a duplicate prompt). The gate is in the infrastructure, not the conversation.
╭─ Approval Required ──────────────────────────────────╮
│ Method : POST │
│ Path : /api/accounts │
│ Payload : {"name": "Acme Corp"} │
╰───────────────────────────────────────────────────────╯
Proceed? y/n:Read-only calls and discovery tools pass through without prompting.
Structured output envelope.
All responses follow a consistent shape, regardless of success or failure:
{
"status": "success | error | out_of_scope | missing_dependency",
"action": "short description of what was done",
"data": {},
"summary": "one-sentence plain English summary",
"suggestions": ["follow-up action 1", "follow-up action 2"]
}out_of_scope is returned when the LLM searches and finds no matching endpoints — no fabricated tool calls, no hallucinated routes.
How to Apply This to Your Own API
Step 1 — Build a metadata registry. In whatever language your API uses, create a data structure (file, module, table) that holds a description of every endpoint: path, method, input schema, output schema, tags. This is metadata only — it doesn't affect routing.
Step 2 — Add four discovery routes. Add the four meta-routes to your API. They read from the registry at runtime. No changes to existing business logic.
Step 3 — Define a rules registry.
For each entity and each action (create, update, delete), define what must exist first and what validations apply. Store this alongside the endpoint registry, in the same file or module.
Step 4 — Write a thin MCP server. Five tools: list, search, detail, rules, call. The MCP server has no API knowledge — it proxies the discovery routes and executes calls. It can be written in any language that has an MCP SDK (Python, TypeScript, Go, Kotlin…). It doesn't change when your API changes.
Step 5 — Configure the LLM agent. The system prompt is short because rules aren't in it. Instruct the LLM to: search → inspect schema → check rules → get approval → call. The workflow is mechanical, not conversational.
What This Pattern Doesn't Solve
Being clear about the limits helps you decide whether this pattern fits your situation:
Registry maintenance overhead. The registry is only as good as what you put in it. If descriptions are vague or response_schema is missing, the LLM will make poor tool calls. Keeping the registry accurate as the API evolves requires discipline — it won't self-update from your route handlers.
Latency per turn. Each LLM turn involves 2–4 HTTP calls to the discovery routes before the actual API call. For high-frequency or latency-sensitive use cases, the MCP server should cache the registry in memory at session start rather than fetching it fresh each call.
The rules registry doesn't enforce — it informs. The rules registry tells the LLM what to check. It doesn't prevent a bad API call if the LLM ignores it. Real enforcement still lives in the API itself (foreign key constraints, validation logic). The registry is a guide layer, not a hard gate.
This pattern doesn't replace API authentication or authorization. The MCP server calls the API with whatever credentials it has. If your API has per-user permissions, those must be enforced at the API layer — the MCP server is transparent to them.
This Implementation (Python)
The recruiter CRM is a reference implementation of the pattern. Four entities:
accounts ──< contacts (one account → many contacts)
accounts ──< placements (one account → many placements)
candidates──< placements (one candidate → many placements)
contacts ──< placements (one contact manages many placements, optional)Stack: Flask (API), FastMCP (MCP server), OpenRouter (LLM), SQLite (database), Rich (terminal UI)
File Structure
MCP-from-API/
├── src/
│ ├── registry.py ← ENDPOINT_REGISTRY + RULES_REGISTRY (single source of truth)
│ ├── recruiter_api.py ← Flask routes + DB logic (imports registry, no metadata here)
│ ├── mcp_server.py ← 6 MCP tools, zero hardcoded API knowledge
│ └── chat.py ← CLI: OpenRouter LLM, human-in-the-loop, JSON output
├── docs/
│ ├── api.md ← endpoint reference + schema tables
│ ├── mcp.md ← MCP design + tool details + improvement ideas
│ └── cli.md ← CLI usage guide + example queries
├── requirements.txt ← flask, fastmcp, httpx, python-dotenv, rich
├── .env.example ← template — copy to .env and fill in your key
└── recruiter.db ← SQLite (auto-created, seeded on first run only)Quick Start
# 1. Install dependencies
python3 -m venv .venv && .venv/bin/pip install -r requirements.txt
# 2. Configure
cp .env.example .env
# Edit .env — set OPENROUTER_API_KEY=sk-or-...
# 3. Start the API (terminal 1)
.venv/bin/python src/recruiter_api.py
# 4. Start the CLI (terminal 2)
.venv/bin/python src/chat.pyConfiguration
OPENROUTER_API_KEY=sk-or-...
OPENROUTER_MODEL=openai/gpt-4o-mini
MCP_SCRIPT=mcp_server.pyAdding a New Endpoint
Add the route to
recruiter_api.pywith@app.routeCall
register(...)inregistry.pywithbody_schemaandresponse_schemaIf the entity has dependencies, add an entry to
RULES_REGISTRYinregistry.py
No changes needed to mcp_server.py or chat.py.
Further Reading
docs/api.md — endpoint reference, schema tables, data model
docs/mcp.md — MCP design decisions, tool details, improvement ideas
docs/cli.md — CLI usage guide, example queries, troubleshooting
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- Alicense-qualityCmaintenanceA generic MCP server that dynamically converts OpenAPI-defined REST APIs into tools for LLMs like Claude. It supports multiple authentication methods and transport protocols, enabling seamless interaction with any OpenAPI-compliant API.Last updated17MIT
- AlicenseAqualityDmaintenanceA generic MCP server that dynamically exposes any OpenAPI-documented REST API to LLMs by auto-discovering endpoints. It provides tools for exploring API capabilities and making authenticated requests directly through natural language interfaces.Last updated217MIT
- Alicense-qualityDmaintenanceA universal MCP server that connects any REST API to AI assistants via OpenAPI or Postman specifications. It enables dynamic tool creation with GraphQL-style field selection and automatic schema inference for efficient data retrieval.Last updated95Inno Setup
- Alicense-qualityBmaintenanceEnables LLMs to interact with any REST API that has an OpenAPI specification by providing a lightweight MCP server that translates between natural language and API calls.Last updatedMIT
Related MCP Connectors
Self-hosted MCP gateway: turn any API, database or MCP server into AI connectors — no code.
MCP server providing access to the Scorecard API to evaluate and optimize LLM systems.
MCP server for AI agents to plan, verify, and deploy Cloudflare-native apps.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/naansari/mcp-from-api'
If you have feedback or need assistance with the MCP directory API, please join our Discord server