API को MCP सर्वर में बदलें: REST और OpenAPI स्टेप बाय स्टेप
मौजूदा REST API को एक MCP सर्वर के रूप में लपेटें, ताकि एजेंट बिना अंदाज़े के उसे कॉल कर सकें। OpenAPI फ़ाइल से FastMCP के साथ टूल्स जेनरेट करें, TypeScript में हाथ से बनाएँ, auth और धीमे इमेज या वीडियो जॉब संभालें, फिर Inspector से टेस्ट करें और stdio या HTTP पर डिप्लॉय करें।
आपका REST API पहले से काम कर रहा है, फिर भी एजेंट उसे ठीक से नहीं चला पाते। वे पैरामीटर के नाम अंदाज़े से लगाते हैं, 40 KB के JSON रिस्पॉन्स से जूझते हैं और जब GET कॉल करना होता है, तब वे DELETE कॉल कर देते हैं। समाधान कोई ज़्यादा स्मार्ट मॉडल नहीं है। बीच में एक पतली परत चाहिए: एक MCP सर्वर जो एजेंट को बताए कि कौन-सी क्रियाएँ मौजूद हैं, हर एक का इनपुट कैसा दिखता है और उसके जवाब में क्या लौटता है। यह ट्यूटोरियल दिखाता है कि OpenAPI स्पेक से API को MCP सर्वर में कैसे बदला जाए, पहले जेनरेट किए गए कोड से, फिर हाथ से, जिसमें auth, धीमे जॉब, टेस्टिंग और डिप्लॉयमेंट भी शामिल हैं।
शुरू करने से पहले तीन चीज़ें चाहिए: एक API जिसे आप पहले से curl से कॉल कर सकें, उसकी OpenAPI 3.x फ़ाइल (या उसे लिखने का धीरज), और कोई MCP क्लाइंट जैसे Claude Desktop, Cursor या VS Code, जिस पर नतीजा आज़माया जा सके। पहला काम करने वाला वर्ज़न एक दोपहर में बन जाता है। असली समय पॉलिश में जाता है, और क्वालिटी भी वहीं से आती है।
💡 संक्षेप में: MCP आपके API को टूल्स में लपेटता है। हर टूल का एक नाम, एक विवरण और इनपुट के लिए एक JSON Schema होता है। एजेंट टूल्स को उनके विवरण पढ़कर चुनता है, इसलिए विवरण HTTP की प्लंबिंग से ज़्यादा मायने रखते हैं।
MCP के रूप में API को क्यों लपेटें
REST उन डेवलपर्स के लिए बना था जो दस्तावेज़ एक बार पढ़ते हैं और फिर उसी के हिसाब से कोड लिखते हैं। एजेंट अलग तरह से काम करता है। वह सेशन की शुरुआत में सर्वर जो भी सूची दे, उसे पढ़ता है और सिर्फ़ उसी सूची के आधार पर तय करता है कि कौन-सी कॉल करनी है। सूची धुंधली हो तो वह अंदाज़ा लगाता है। सूची बहुत बड़ी हो तो उपयोगकर्ता के कुछ टाइप करने से पहले ही उसकी कॉन्टेक्स्ट विंडो भर जाती है।
एक MCP सर्वर दोनों समस्याएँ उसी तरह हल करता है जैसे एक स्विचबोर्ड ऑपरेटर करता है: वह साफ़ अनुरोध लेता है, उसे सही लाइन पर भेजता है और साफ़ जवाब लौटाता है।
एजेंट असल में क्या देखता है
क्लाइंट कनेक्ट होते ही सर्वर से उसकी टूल सूची माँगता है। हर प्रविष्टि में एक name, एक description, JSON Schema में लिखा एक inputSchema होता है और चाहें तो एक outputSchema और कुछ annotations भी। बस यही पूरी सतह है। एजेंट आपके रूट, HTTP verbs या status codes कभी नहीं देखता। वह सिर्फ़ नाम, वाक्य और स्कीमा देखता है।
एक नज़र में REST से MCP
OpenAPI ऑपरेशन का हर हिस्सा MCP की तरफ़ अपनी जगह पाता है:
REST / OpenAPI
MCP टूल
operationId
टूल name
summary और description
टूल description
पाथ, क्वेरी और बॉडी पैरामीटर
inputSchema, एक फ़्लैट JSON Schema ऑब्जेक्ट
200 रिस्पॉन्स स्कीमा
outputSchema और स्ट्रक्चर्ड कंटेंट
4xx और 5xx रिस्पॉन्स
isError: true और पढ़ने योग्य message वाला result
सिक्योरिटी स्कीम
सर्वर कॉन्फ़िग: एनवायरनमेंट टोकन, या रिमोट सर्वर के लिए OAuth
पेजिनेशन लिंक
स्पष्ट cursor और limit इनपुट
Structured output और output schemas स्पेक के 2025-06-18 रिवीज़न में आए थे, इसलिए outputSchema पर भरोसा करने से पहले जाँच लें कि आपका SDK वर्ज़न इन्हें सपोर्ट करता है।
OpenAPI ऑपरेशन को टूल्स में बदलें
स्पेक खोलें और हर चीज़ को एक्सपोज़ करने की इच्छा रोकें। 120 एंडपॉइंट वाला API 120 टूल्स वाला सर्वर बन जाता है, और सिर्फ़ टूल सूची ही हर बातचीत में हज़ारों टोकन खर्च कर सकती है। छोटे से शुरू करें, चीज़ों के अच्छे नाम रखें और उन्हें वैसे समझाएँ जैसे कोई सहकर्मी समझाता।
ऑपरेशन चुनें, एंडपॉइंट नहीं
किसी भी एंडपॉइंट को टूल बनने से पहले उससे चार सवाल पूछें:
क्या कोई व्यक्ति सादी भाषा में किसी असिस्टेंट से यह काम करने को कहेगा?
क्या एजेंट के रीट्राई करने पर इसे दो बार कॉल करना सुरक्षित है?
क्या रिस्पॉन्स कुछ किलोबाइट में समा जाता है, या आप उसे तब तक छोटा कर सकते हैं जब तक वह समा जाए?
क्या यह किसी और दर्शक वर्ग का है, जैसे एडमिन, बिलिंग या इंटर्नल टूलिंग?
जो पहले या आख़िरी सवाल में फेल हो, वह बाहर रहता है। सोच-समझकर चुने गए पाँच से दस टूल्स सौ कच्चे टूल्स से बेहतर हैं। जो फ़्लो कई चरणों में होते हैं, उन्हें एक टूल मिलना चाहिए: अगर "कार्ट बनाएँ, आइटम जोड़ें, चेकआउट करें" हमेशा क्रम से होता है, तो एजेंट को एक ही place_order क्रिया दिखनी चाहिए।
हर टूल का नाम रखें और उसका विवरण लिखें
operationId से शुरू करें, फिर उसे एक क्रिया और एक संज्ञा के रूप में दोबारा लिखें। अच्छा विवरण तीन सवालों का जवाब देता है: टूल क्या करता है, उसके पड़ोसी टूल की जगह इसे कब चुनना चाहिए, और यह क्या लौटाता है।
जेनरेट किया गया
दोबारा लिखा गया
नाम
OrdersController_findAll
search_orders
विवरण
"Find all"
"ग्राहक के ईमेल, स्टेटस या तारीख की सीमा से ऑर्डर खोजें। id, स्टेटस और कुल के साथ अधिकतम 20 ऑर्डर लौटाता है। लाइन आइटम के लिए get_order का उपयोग करें।"
path, query और body पैरामीटर को एक ही ऑब्जेक्ट में समेटें। enums रखें, required फ़ील्ड चिह्नित करें, हर प्रॉपर्टी को छोटा विवरण और एक उदाहरण मान दें, और maximum और maxLength जैसी सीमाएँ तय करें ताकि मॉडल 10,000 रो न माँग ले। ऐसा एक OpenAPI ऑपरेशन:
{
"name": "get_order",
"description": "Fetch one order by id. Returns status, total and line items. Use search_orders when you only have an email.",
"inputSchema": {
"type": "object",
"properties": {
"order_id": { "type": "string", "description": "Order id, for example ord_8f2c1" }
},
"required": ["order_id"]
}
}
सर्वर बनाने के दो तरीके
आप स्पेक से सीधे कुछ ही मिनटों में सर्वर जेनरेट कर सकते हैं, या हर टूल हाथ से लिख सकते हैं। ज़्यादातर टीमें दोनों करती हैं: पहले जेनरेट करके आकार देखती हैं, फिर उन पाँच टूल्स को हाथ से ठीक करती हैं जो सबसे ज़्यादा मायने रखते हैं।
FastMCP से जेनरेट करें
Python लाइब्रेरी FastMCP सीधे OpenAPI दस्तावेज़ से सर्वर बना सकती है:
सर्वर स्पेक पढ़ता है, टूल्स बनाता है और हर कॉल को उस httpx क्लाइंट के ज़रिए आगे भेजता है जो आप पास करते हैं। auth हेडर भी वहीं रहता है। रूट मैप एडमिन और इंटर्नल रूट्स को एजेंट के देखने से पहले ही हटा देते हैं।
⚠️ वर्ज़न जाँच: डिफ़ॉल्ट मैपिंग FastMCP के मेजर वर्ज़न के बीच अलग होती है। नए रिलीज़ हर ऑपरेशन को टूल बनाते हैं, जबकि पुराने 2.x रिलीज़ कुछ GET रूट्स को resources में मैप करते थे। अपना वर्ज़न पिन करें, रूट मैप साफ़-साफ़ सेट करें और इंस्टॉल किए गए रिलीज़ के दस्तावेज़ में इम्पोर्ट पाथ की पुष्टि करें।
जब जेनरेशन कम पड़े
FastMCP का अपना दस्तावेज़ चेताता है कि क्यूरेट किए गए सर्वर ऑटो-कन्वर्ट किए गए सर्वरों से मॉडल के लिए कहीं बेहतर नतीजे देते हैं, खासकर बहुत सारे एंडपॉइंट और पैरामीटर वाले API के लिए। पहली टेस्ट रन में ही आपको इसका कारण दिख जाएगा:
get_orders_by_id_using_get जैसे नाम, जिन्हें कोई इंसान नहीं लिखता
डेवलपर दस्तावेज़ से कॉपी किए गए विवरण, जो उन पाठकों के लिए लिखे गए हैं जो सिस्टम पहले से जानते हैं
ऐसे रिस्पॉन्स जो हर फ़ील्ड लौटाते हैं, इंटर्नल फ़्लैग्स समेत
चार टूल्स जो एक होने चाहिए थे
इन्हें इसी क्रम में ठीक करें: छाँटें, नाम बदलें, विवरण दोबारा लिखें, रिस्पॉन्स छोटे करें, फ़्लो मिलाएँ।
TypeScript में हाथ से बनाएँ
जो टूल्स मायने रखते हैं, उनके लिए आधिकारिक TypeScript SDK पूरा नियंत्रण देता है। @modelcontextprotocol/sdk और zod इंस्टॉल करें, फिर हर टूल को एक स्कीमा और एक हैंडलर के साथ रजिस्टर करें:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const API = "https://api.example.com";
const TOKEN = process.env.ORDERS_API_TOKEN;
const server = new McpServer({ name: "orders", version: "1.0.0" });
server.registerTool(
"get_order",
{
title: "Get order",
description:
"Fetch one order by id. Returns status, total and line items. Use search_orders when you only have an email.",
inputSchema: { order_id: z.string().describe("Order id, for example ord_8f2c1") },
annotations: { readOnlyHint: true },
},
async ({ order_id }) => {
const res = await fetch(`${API}/orders/${encodeURIComponent(order_id)}`, {
headers: { Authorization: `Bearer ${TOKEN}` },
});
if (!res.ok) {
return {
isError: true,
content: [{ type: "text", text: `Orders API returned ${res.status}. Check the id and try again.` }],
};
}
const order = await res.json();
return { content: [{ type: "text", text: JSON.stringify(order) }] };
}
);
await server.connect(new StdioServerTransport());
दो विवरण सबसे ज़्यादा काम करते हैं। readOnlyHint annotation क्लाइंट को बताता है कि यह कॉल बिना कन्फ़र्मेशन प्रॉम्प्ट के चलाना सुरक्षित है, और error शाखा isError: true के साथ पढ़ने लायक संदेश लौटाती है, ताकि एजेंट रुकने के बजाय दोबारा कोशिश कर सके।
Auth, सीक्रेट और गार्डरेल
क्रेडेंशियल सर्वर के पास रहते हैं। मॉडल के पास कभी नहीं।
टोकन को प्रॉम्प्ट से बाहर रखें
प्रोसेस शुरू होते समय API टोकन को environment variable या सीक्रेट मैनेजर से पढ़ें। उसे कभी टूल आर्गुमेंट के रूप में स्वीकार न करें, किसी error संदेश में न दोहराएँ और कभी request headers लॉग न करें। API जितना संकीर्ण टोकन दे सके, वही बनाएँ: केवल-पढ़ने वाले सर्वर के लिए केवल-पढ़ने वाला टोकन। रिमोट सर्वरों के लिए MCP का authorization flow OAuth 2.1 पर आधारित है, इसलिए हर उपयोगकर्ता अपने खाते से साइन इन करता है और हर कॉल उसी की अनुमतियाँ लेकर चलती है, किसी साझा superuser की नहीं।
जोखिम भरे टूल्स पर annotation लगाएँ
Annotations ऐसे संकेत हैं जो क्लाइंट को तय करने में मदद करते हैं कि उपयोगकर्ता से कब पुष्टि माँगनी है:
Annotation
इसे कब सेट करें
readOnlyHint: true
टूल केवल पढ़ता है, जैसे GET या search
destructiveHint: true
टूल डेटा हटाता या ओवरराइट करता है
idempotentHint: true
उसी इनपुट से कॉल दोहराने पर कुछ और नहीं बदलता
openWorldHint: true
टूल आपके अपने सिस्टम के बाहर पहुँचता है, जैसे खुला वेब
इन्हें लागू होने वाले नियम नहीं, संकेत मानें, क्योंकि क्लाइंट को ऐसे सर्वर के annotations पर भरोसा नहीं करना चाहिए जिसे वह जानता नहीं। असली सुरक्षा आपकी तरफ़ से होती है: पहला रिलीज़ केवल-पढ़ने वाला भेजें, लिखने वाले टूल्स एक-एक करके जोड़ें और विनाशकारी टूल्स को dry_run या confirm इनपुट दें, ताकि एजेंट को साफ़ तौर पर बताना पड़े।
धीमे जॉब, पोलिंग और मीडिया
इमेज जनरेशन, वीडियो रेंडरिंग और रिपोर्ट एक्सपोर्ट एक ही पैटर्न साझा करते हैं: API तुरंत एक job id लौटाता है, और नतीजा सेकंड या मिनट बाद आता है। जो टूल तीन मिनट तक रुका रहे, वह ज़्यादातर क्लाइंट्स में टाइम आउट हो जाएगा। रेस्तराँ में यही समस्या ऑर्डर टिकटों की किचन रेल हल करती है: ऑर्डर लो, टिकट थमाओ, डिश तैयार होने पर नंबर पुकारो।
बनाएँ, पोल करें, लाएँ
जॉब को तीन टूल्स में बाँटें: एक उसे शुरू करे, एक उसकी स्थिति जाँचे और एक उसे रद्द करे। स्टार्ट टूल एक id और यह संकेत लौटाता है कि कब दोबारा जाँचना है। चेक टूल एक छोटा स्टेटस ऑब्जेक्ट लौटाता है, queued, running, succeeded या failed, और जब लाने लायक कुछ तैयार हो तो एक URL भी। फ़ाइल के बाइट्स नहीं, लिंक लौटाएँ: कॉन्टेक्स्ट में चिपकाई गई 5 MB की इमेज किसी के काम नहीं आती।
server.registerTool(
"get_render",
{
description:
"Check a render started with start_render. Call again after next_poll_in_seconds until status is succeeded or failed.",
inputSchema: { render_id: z.string() },
annotations: { readOnlyHint: true },
},
async ({ render_id }) => {
const job = await api(`/renders/${render_id}`); // api() is your fetch helper
const done = job.status === "succeeded" || job.status === "failed";
const body = {
status: job.status,
url: job.output?.[0] ?? null,
next_poll_in_seconds: done ? null : 5,
};
return { content: [{ type: "text", text: JSON.stringify(body) }] };
}
);
एक असली इमेज और वीडियो उदाहरण
PicassoIA का अपना कनेक्टर इसी डिज़ाइन पर चलता है। उसके टूल्स generate_image, edit_image, generate_video_picassoia और generate_video_seedance एक predict_id लौटाते हैं, जैसे ही कोई GPU जॉब स्वीकार करता है, साथ में अनुमानित समय भी। फिर एजेंट लौटे हुए get_generation के बाद next_poll_in_seconds को कॉल करता है, और स्टेटस succeeded या failed होने तक दोहराता है। एक cancel_generation टूल चल रहे जॉब को रोकता है, और उसके निर्देशों में एक ईमानदार चेतावनी है: जो वीडियो GPU पहले से रेंडर कर रहा है, उसे अब रद्द नहीं किया जा सकता।
इसके नीचे Replicate जैसा REST API है, जो https://api.picassoia.com/v1 पर bearer-token auth के साथ चलता है। POST /v1/models/{owner}/{name}/predictions एक जॉब बनाता है, GET /v1/predictions/{id} उसे पढ़ता है और POST /v1/predictions/{id}/cancel उसे रोकता है। इसलिए यह एक आदर्श कन्वर्ज़न लक्ष्य है, और कनेक्टर के पीछे के चार मॉडल उसके चार जेनरेशन टूल्स से मेल खाते हैं:
सीमाएँ भी टूल विवरणों में होनी चाहिए। API हर अकाउंट के लिए 5 एक साथ चलने वाली predictions की अनुमति देता है, जो टोकन और MCP कनेक्शनों के बीच साझा होती हैं, और प्रॉम्प्ट 4,000 अक्षरों तक हो सकते हैं। इसलिए अच्छा विवरण एजेंट को बताता है कि छठा जॉब शुरू करने से पहले चल रहे जॉब के पूरे होने का इंतज़ार करे। API पर कोई प्रोडक्ट बनाने से पहले PicassoIA साइट पर मौजूदा प्लान शर्तें ज़रूर जाँच लें।
टेस्ट करें, फिर शिप करें
एजेंट एक बेरहम टेस्टर है: वह आपके टूल्स को ऐसे तरीकों से इस्तेमाल करता है जिनकी आपने योजना नहीं बनाई थी। उसे सौंपने से पहले सही टूलिंग से टेस्ट करें।
MCP Inspector चलाएँ
MCP Inspector आधिकारिक debugging इंटरफ़ेस है। उसे अपने सर्वर की ओर इंगित करें, जैसे npx @modelcontextprotocol/inspector node dist/server.js, और वह हर टूल की सूची दिखाएगा, आपको हर टूल को raw JSON से कॉल करने देगा और वह सटीक रिज़ल्ट दिखाएगा जो कोई क्लाइंट पाता। फिर कोई असली क्लाइंट जोड़ें और दस यथार्थवादी प्रॉम्प्ट चलाएँ। हर एक पर तीन बातें जाँचें: क्या एजेंट ने सही टूल चुना, क्या उसने आर्गुमेंट सही भरे, और क्या रिस्पॉन्स ने उसे जवाब देने लायक जानकारी दी।
stdio या HTTP चुनें
stdio
Streamable HTTP
कैसे चलता है
क्लाइंट द्वारा शुरू की गई लोकल प्रोसेस
एक URL के पीछे चलने वाली रिमोट सर्विस
ऑथ
यूज़र की मशीन पर एनवायरनमेंट वेरिएबल
OAuth या bearer tokens
किसके लिए सबसे अच्छा
पर्सनल टूल और डेवलपमेंट
टीम और शेयर्ड APIs
किस बात का ध्यान रखें
लॉग्स को कभी stdout पर प्रिंट न करें
TLS, रेट लिमिट, हॉरिज़ॉन्टल स्केलिंग
Streamable HTTP ने स्पेक के 2025-03-26 रिवीज़न में पुराने HTTP plus SSE ट्रांसपोर्ट की जगह ली, और प्रोटोकॉल लगातार बदल रहा है, इसलिए अपग्रेड करने से पहले SDK वर्ज़न पिन करें और उसका changelog पढ़ें।
पाँच आम गलतियाँ
हर एंडपॉइंट को एक्सपोज़ करना। टूल सूचियाँ हर एक टर्न पर टोकन खर्च करती हैं।
stdio सर्वर पर stdout में लॉग करना। Stdout खुद प्रोटोकॉल ले जाता है। लॉग stderr पर भेजें।
पूरा upstream payload लौटाना। केवल वे फ़ील्ड रखें जिनकी एजेंट को ज़रूरत है और बाकी के लिए pagination दें।
Exceptions फेंकना।isError रिज़ल्ट लौटाएँ जिसमें लिखा हो कि आगे क्या आज़माना है।
ओवरलैप करते विवरण। अगर दो टूल एक जैसे लगते हैं, तो एजेंट अंदाज़े से चुनता है। बताएँ कि कब किसे चुनना है।
बीस टूल विवरण हाथ से लिखना उबाऊ है, और जब आप नियम दें तो LLM यह काम अच्छे से करता है। PicassoIA पर Claude Sonnet 5 एक अच्छा विकल्प है: उसके मॉडल पेज में मल्टी-स्टेप कोडिंग और tool-use कार्य उसकी खूबियों में गिने गए हैं, वह system prompt स्वीकार करता है और आप तय कर सकते हैं कि वह कितना थिंकिंग करे।
सिस्टम प्रॉम्प्ट एक बार सेट करें। उदाहरण के लिए: You write MCP tool definitions. For each OpenAPI operation return a verb_noun name, a description that says what the tool does, when to use it and what it returns, and a flat JSON Schema with example values. Never copy internal parameter names.
एक बार में एक ऑपरेशन प्रॉम्प्ट फ़ील्ड में चिपकाएँ, या संबंधित ऑपरेशनों का छोटा समूह। पूरी 5 MB स्पेक से धुंधला आउटपुट आता है।
एफ़र्ट लेवल चुनें।low सबसे तेज़ है और थिंकिंग बंद कर देता है, medium सरल ऑपरेशनों के बैच के लिए ठीक है, और high नेस्टेड request bodies के लिए फ़ायदेमंद है।
पाँच से आठ ऑपरेशनों के बैच के लिए max tokens 8192 पर छोड़ें, जो डिफ़ॉल्ट है।
अगर आपके पास सिर्फ़ रेंडर किए गए दस्तावेज़ हैं तो स्क्रीनशॉट अटैच करें। इमेज फ़ील्ड एक इमेज स्वीकार करता है।
शिप करने से पहले समीक्षा करें। हर ड्राफ़्ट को Inspector से चलाएँ और ओवरलैप करने वाले नाम ठीक करें।
💡 क्या आपको ऐसा आउटपुट चाहिए जो हर बार JSON के रूप में पार्स हो सके? GPT 5 Structured साफ़ JSON लौटाने के लिए बना है, जो उन स्कीमा ड्राफ़्ट के लिए ठीक है जिन्हें आप सीधे कोड में लोड करना चाहते हैं।
अपना एजेंट टूलकिट बनाएँ
एक API, पाँच टूल्स और फ़ुरसत की एक दोपहर चुनें। पहले केवल-पढ़ने वाला वर्ज़न भेजें, उसे दस असली प्रॉम्प्ट से टेस्ट करें, और फिर ही लिखने या हटाने वाले टूल्स जोड़ें।
एजेंटों को सिर्फ़ पढ़ने के लिए डेटा नहीं, दिखाने के लिए चीज़ें भी चाहिए। Picasso IA पर खुद आज़माएँ: PicassoIA Image में एक प्रॉम्प्ट लिखें, 16:9 चुनें, फिर PicassoIA Image Editor Pro से नतीजे को रिफ़ाइन करें, जो अधिकतम तीन रेफ़रेंस इमेज स्वीकार करता है। जब स्थिर इमेज सही लगे, तो उसे Picasso IA Video से एनिमेट करें या Seedance 2.5 Lite से शॉट को दस सेकंड तक बढ़ाएँ। अपने दृश्यों के साथ प्रयोग करें, और जब तैयार हों, तो वह MCP टूल बनाएँ जो आपके एजेंट को भी यही करने दे।