Claude Code और GitHub Copilot के लिए MCP सर्वर बनाएँ
एक ही TypeScript MCP सर्वर बनाएँ और उसे Claude Code और GitHub Copilot दोनों में रजिस्टर करें। आपको काम करने वाला टूल कोड, हर क्लाइंट के लिए सटीक कॉन्फ़िग, MCP Inspector के साथ डीबगिंग की प्रक्रिया और PicassoIA API को कॉल करने वाला एक इमेज टूल मिलेगा।
आपने एक स्क्रिप्ट लिखी है जो रोज़ आपके दस मिनट बचाती है, और अब आप चाहते हैं कि आपका AI असिस्टेंट उसे कॉपी-पेस्ट के झंझट के बिना चलाए। इसे एक बार Model Context Protocol सर्वर के रूप में बनाएँ, तो Claude Code और GitHub Copilot दोनों एक ही टूल्स को कॉल कर सकते हैं, क्योंकि MCP वह साझा भाषा है जिसमें वे एडिटर के बाहर की किसी भी चीज़ से बात करते हैं। यह वॉकथ्रू एक छोटा TypeScript सर्वर बनाता है, उसे Claude Code में रजिस्टर करता है, VS Code के अंदर Copilot में रजिस्टर करता है, और अंत में PicassoIA API को कॉल करने वाला एक असली इमेज टूल जोड़ता है। लगभग 40 मिनट और करीब 100 लाइन कोड का अनुमान रखें।
एक सर्वर दो से बेहतर क्यों है
MCP से पहले, हर असिस्टेंट अपना अलग प्लगइन फ़ॉर्मेट, अपना मैनिफ़ेस्ट और अपने पैकेजिंग नियम माँगता था। MCP सर्वर उस सारे झंझट की जगह एक ही प्रोसेस रखता है, जो बताता है कि वह क्या कर सकता है। क्लाइंट प्रोसेस शुरू करता है, उसकी क्षमताओं की सूची माँगता है, और वह सूची मॉडल को दे देता है। फिर मॉडल बातचीत के बीच ही तय करता है कि कॉल करना कब सार्थक है।
एक ही प्रोटोकॉल, दो क्लाइंट
एक सर्वर तीन तरह की क्षमताएँ दे सकता है:
Tools: ऐसे फ़ंक्शन जिन्हें मॉडल कॉल कर सकता है, जैसे add_note या generate_image।
Resources: केवल-पढ़ने वाला डेटा जिसे क्लाइंट बातचीत में जोड़ सकता है, जैसे कोई लॉग फ़ाइल या स्कीमा।
Prompts: दोबारा इस्तेमाल होने वाले टेम्पलेट जिन्हें यूज़र जानबूझकर चलाता है।
आज लगभग सारी वैल्यू टूल्स में है, इसलिए यह लेख उन्हीं पर केंद्रित रहता है। Claude Code और Copilot दोनों एक ही तरह के JSON-RPC मैसेज एक ही ट्रांसपोर्ट पर बोलते हैं, यानी जो सर्वर एक क्लाइंट के साथ चलता है, वह लगभग बिना बदलाव के दूसरे के साथ भी चलेगा।
सेटअप कहाँ अलग है
सर्वर एक जैसा है। रजिस्ट्रेशन एक जैसा नहीं है। पूरा अंतर एक टेबल में यह है:
सेटिंग
Claude Code
VS Code में GitHub Copilot
कॉन्फ़िग फ़ाइल
प्रोजेक्ट में .mcp.json, या ~/.claude.json
.vscode/mcp.json, या आपकी यूज़र प्रोफ़ाइल
रूट प्रॉपर्टी
mcpServers
servers
टर्मिनल से जोड़ें
claude mcp add
Command Palette: MCP: Add Server
Transport फ़ील्ड
type (stdio, http, sse)
type ज़रूरी है (stdio या http)
सीक्रेट्स
--env फ़्लैग या ${VAR} एक्सपैंशन
inputs ब्लॉक, जिसमें ${input:id} हो
टूल कहाँ चलते हैं
कोई भी सेशन
केवल Agent mode में
💡 टिप: रूट प्रॉपर्टी सबसे आम जाल है। Claude Code का कॉन्फ़िग बिना बदले VS Code में चिपकाएँ, तो कुछ भी लोड नहीं होता, क्योंकि Copilot servers ढूँढता है, mcpServers नहीं।
प्रोजेक्ट सेट करें
अपनी मुख्य रिपॉज़िटरी के बाहर कोई फ़ोल्डर चुनें, ताकि सर्वर बाद में कई प्रोजेक्ट्स की सेवा कर सके। आपको Node.js 20 या उससे नया और एक टर्मिनल चाहिए।
उदाहरणों में @modelcontextprotocol/sdk 1.x API का उपयोग है, जिसमें McpServer और registerTool शामिल हैं। अगर कोई नया मेजर वर्ज़न इम्पोर्ट पाथ बदलता है, तो नीचे की अवधारणाएँ वही रहेंगी।
stdio से शुरू करें
MCP में दो मुख्य ट्रांसपोर्ट हैं। stdio का मतलब है कि क्लाइंट आपके सर्वर को चाइल्ड प्रोसेस के रूप में शुरू करता है और मैसेज standard input और output के ज़रिए बदलता है। Streamable HTTP का मतलब है कि सर्वर अपने आप चलता है और क्लाइंट URL से जुड़ते हैं। stdio से शुरू करें। इसमें न पोर्ट चाहिए, न ऑथेंटिकेशन लेयर, न होस्टिंग, और दोनों क्लाइंट इसे सीधे सपोर्ट करते हैं। HTTP पर तभी जाएँ जब कई लोगों को एक ही चलते इंस्टेंस को साझा करना हो।
सर्वर लिखें
हमारा उदाहरण एक छोटा टीम नोट्स सर्वर है, जिसमें दो टूल हैं: एक नोट सेव करता है, दूसरा उन्हें खोजता है। यह इतना छोटा है कि एक मिनट में पढ़ा जा सके, और इतना असली है कि काम का हो।
एक टूल रजिस्टर करें
src/index.ts बनाएँ:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { promises as fs } from "node:fs";
import path from "node:path";
const NOTES_FILE = path.join(process.env.NOTES_DIR ?? process.cwd(), "notes.json");
type Note = { id: number; text: string; tags: string[]; createdAt: string };
async function readNotes(): Promise<Note[]> {
try {
return JSON.parse(await fs.readFile(NOTES_FILE, "utf8"));
} catch {
return [];
}
}
const server = new McpServer({ name: "team-notes", version: "1.0.0" });
server.registerTool(
"add_note",
{
title: "Add note",
description:
"Save a short engineering note with optional tags. Use it when the user asks to remember a decision, a command or a bug.",
inputSchema: {
text: z.string().min(3).max(2000),
tags: z.array(z.string()).default([]),
},
},
async ({ text, tags }) => {
const notes = await readNotes();
const note: Note = {
id: notes.length + 1,
text,
tags,
createdAt: new Date().toISOString(),
};
await fs.writeFile(NOTES_FILE, JSON.stringify([...notes, note], null, 2));
return { content: [{ type: "text", text: `Saved note #${note.id}` }] };
}
);
server.registerTool(
"search_notes",
{
title: "Search notes",
description: "Find saved notes whose text or tags contain the query.",
inputSchema: { query: z.string().min(1) },
},
async ({ query }) => {
const q = query.toLowerCase();
const hits = (await readNotes()).filter(
(n) =>
n.text.toLowerCase().includes(q) ||
n.tags.some((t) => t.toLowerCase().includes(q))
);
const text = hits.length
? hits.map((n) => `#${n.id} [${n.tags.join(", ")}] ${n.text}`).join("\n")
: "No notes matched.";
return { content: [{ type: "text", text }] };
}
);
await server.connect(new StdioServerTransport());
console.error("team-notes MCP server running on stdio");
npm run build चलाएँ। अब आपके पास dist/index.js है, और यही फ़ाइल वह अकेली चीज़ है जो दोनों क्लाइंट्स को जाननी है।
साफ़ नतीजे लौटाएँ
मॉडल जो भी आप लौटाते हैं वह पढ़ता है, इसलिए रिटर्न वैल्यू को एक इंटरफ़ेस की तरह समझें। नतीजे छोटे, स्ट्रक्चर्ड और ईमानदार रखें। जब कुछ विफल हो, तो ऐसा एक्सेप्शन न फेंकें जो ट्रांसपोर्ट लेयर में ही मर जाए। ऐसा एरर लौटाएँ जिसे मॉडल पढ़ और उस पर प्रतिक्रिया दे सके:
return {
isError: true,
content: [{ type: "text", text: "notes.json is not valid JSON. Fix or delete it." }],
};
isError नतीजा असिस्टेंट को समस्या समझाने, या अलग इनपुट के साथ दोबारा कोशिश करने देता है। क्रैश होने पर सिर्फ़ एक धुंधला "server disconnected" बैनर दिखता है।
stdout को शांत रखें
पहला सर्वर विफल होने का यह सबसे आम कारण है। stdio के साथ, standard output प्रोटोकॉल का है। एक भटका हुआ console.log सादा टेक्स्ट JSON-RPC स्ट्रीम में डाल देता है और क्लाइंट कनेक्शन तोड़ देता है। console.error पर लॉग करें, जो stderr पर लिखता है, और दोनों क्लाइंट उसे डायग्नोस्टिक आउटपुट के रूप में पकड़ लेंगे।
💡 टिप: टूल डिस्क्रिप्शन को इंसानों के लिए डॉक्यूमेंटेशन की तरह नहीं, बल्कि मॉडल के लिए निर्देश की तरह लिखें। "जब यूज़र कोई फ़ैसला याद रखने को कहे, तब इसका उपयोग करें" से टूल सही समय पर चुना जाता है। "Notes utility" से नहीं।
Claude Code से जोड़ें
CLI से जोड़ें
एक कमांड सर्वर रजिस्टर करता है। विकल्प नाम से पहले जाते हैं, और डबल डैश नाम को उस कमांड से अलग करता है जिसे Claude Code चलाएगा:
claude mcp add --transport stdio --scope user \
--env NOTES_DIR=/home/dev/notes \
team-notes -- node /absolute/path/to/team-notes-mcp/dist/index.js
एब्सोल्यूट पाथ इस्तेमाल करें। Claude Code प्रोसेस को उस डायरेक्टरी से शुरू करता है जहाँ आपका सेशन है, इसलिए रिलेटिव पाथ तब टूट जाते हैं जब आप कोई दूसरा प्रोजेक्ट खोलते हैं। फिर इसकी पुष्टि करें:
claude mcp list
claude mcp get team-notes
सेशन के अंदर /mcp टाइप करें, ताकि कनेक्शन स्थिति और टूल्स की सूची दिखे। कोई स्वाभाविक सवाल पूछें, जैसे "याद रखें कि हम गुरुवार को डिप्लॉय करते हैं, इसे release टैग करें", और देखें कि Claude Code add_note कॉल करने की अनुमति कैसे माँगता है।
.mcp.json के ज़रिए साझा करें
स्कोप फ़्लैग तय करता है कि सर्वर कौन इस्तेमाल करेगा। local उसे एक प्रोजेक्ट में केवल आपके लिए निजी रखता है, user उसे हर प्रोजेक्ट में उपलब्ध कराता है, और project एक .mcp.json फ़ाइल लिखता है, जिसे आप कमिट कर सकते हैं ताकि पूरी टीम को वह मिले। यहाँ एक साझा कॉन्फ़िग है जो हार्डकोडेड पाथ से बचती है:
हर टीममेट TEAM_NOTES_PATH को अपने शेल में एक बार सेट करता है। ${NOTES_DIR:-.notes} वाला रूप तब डिफ़ॉल्ट देता है जब वेरिएबल मौजूद न हो। Claude Code किसी प्रोजेक्ट-स्कोप्ड सर्वर को पहली बार देखने पर मंज़ूरी माँगता है, जो रिपॉज़िटरी से खींची गई किसी भी चीज़ के लिए एक समझदारी भरा सुरक्षा उपाय है।
GitHub Copilot से जोड़ें
.vscode/mcp.json लिखें
अपने वर्कस्पेस में .vscode/mcp.json बनाएँ। अलग रूट प्रॉपर्टी और ज़रूरी type को याद रखें:
${workspaceFolder} फ़ाइल को पोर्टेबल बनाता है, इसलिए आप उसे कमिट कर सकते हैं। सीक्रेट्स के लिए inputs ऐरे जोड़ें। VS Code एक बार पूछता है, वैल्यू सुरक्षित रूप से स्टोर करता है और उसे इंजेक्ट करता है:
Copilot Chat डिफ़ॉल्ट रूप से Ask mode में खुलता है, और MCP टूल्स केवल Agent mode में चलते हैं। चैट पैनल में मोड बदलें, टूल्स पिकर खोलें और पुष्टि करें कि team-notes दोनों टूल्स के टिक के साथ दिखता है। अगर वह न दिखे, तो Command Palette से MCP: List Servers चलाएँ, सर्वर चुनें और उसका आउटपुट पढ़ें। हर रीबिल्ड के बाद उसी मेन्यू से उसे फिर से शुरू करें।
Copilot आपको अपने प्लान में मिलने वाले मॉडलों में से चुनने देता है, इसलिए वही सर्वर अलग-अलग मॉडलों से परखा जाता है। यह जाँचने का एक आसान तरीका है कि आपकी टूल डिस्क्रिप्शन हर एक के लिए पर्याप्त साफ़ हैं या नहीं।
शिप करने से पहले टेस्ट और डीबग करें
MCP Inspector चलाएँ
किसी क्लाइंट को दोष देने से पहले, सर्वर को अकेले टेस्ट करें। आधिकारिक Inspector एक लोकल वेब पेज खोलता है, जहाँ आप टूल्स की सूची देख सकते हैं, आर्गुमेंट भर सकते हैं और रॉ रिस्पॉन्स देख सकते हैं:
add_note को एक खाली text के साथ कॉल करें। आपकी Zod स्कीमा इसे एक पढ़ने योग्य वैलिडेशन मैसेज के साथ अस्वीकार करनी चाहिए। फिर search_notes को उस टैग के साथ कॉल करें जो आपने अभी सेव किया है। अगर दोनों यहाँ सही व्यवहार करते हैं, तो बची हुई कोई भी समस्या कॉन्फ़िगरेशन में है, आपके कोड में नहीं।
आम विफलताएँ ठीक करें
लक्षण
संभावित कारण
समाधान
सर्वर कभी कनेक्ट नहीं होता
किसी console.log ने stdout में लिखा
console.error पर स्विच करें
"Command not found"
रिलेटिव पाथ या बिल्ड गायब
एब्सोल्यूट पाथ इस्तेमाल करें और npm run build चलाएँ
Copilot में टूल्स गायब हैं
चैट Ask mode में है
Agent mode पर स्विच करें
टूल मौजूद है पर कभी चुना नहीं जाता
अस्पष्ट डिस्क्रिप्शन
ट्रिगर वाक्यांशों के साथ उसे दोबारा लिखें
खाली एनवायरनमेंट वेरिएबल
कॉन्फ़िग में घोषित नहीं
इसे env ब्लॉक में जोड़ें
लोड में इमेज कॉल विफल होती हैं
एक साथ 5 से ज़्यादा जॉब
टूल के भीतर कॉल्स की कतार बनाएँ
अपने सर्वर को इमेज टूल दें
नोट्स ठीक हैं, लेकिन MCP का सबसे अच्छा प्रदर्शन वह टूल है जो वह काम करे जो असिस्टेंट अकेले नहीं कर सकता। इमेज जनरेशन इसके लिए अच्छा फ़िट है: मॉडल एक सटीक प्रॉम्प्ट लिखता है, आपका सर्वर उसे फ़ाइल में बदलता है, और URL सीधे बातचीत में वापस आ जाता है।
PicassoIA API को कॉल करें
PicassoIA डेवलपर API https://api.picassoia.com/v1 पर है और एक Bearer टोकन इस्तेमाल करता है जो pia_sk_ से शुरू होता है। प्रेडिक्शन असिंक्रोनस और Replicate-जैसे होते हैं: आप एक बनाते हैं, फिर तब तक पोल करते हैं जब तक स्टेटस succeeded न दिखे। PicassoIA Image मॉडल 4,000 अक्षरों तक का prompt और एक aspect_ratio स्वीकार करता है, और इमेज URLs की एक सूची लौटाता है। इस टूल को server.connect लाइन से पहले जोड़ें:
एक अकाउंट पर 5 एक साथ चलने वाले प्रेडिक्शन की अनुमति है, जो हर टोकन और कनेक्शन में साझा होती है, इसलिए एक लूप जो एक साथ दस इमेज चलाए, उस सीमा तक पहुँच जाएगा। टूल के भीतर उन्हें एक के बाद एक जनरेट करें, या एक छोटी कतार रखें।
💡 टिप: दूसरों के लिए सर्वर प्रकाशित करने से पहले PicassoIA API पेज पर मौजूदा प्राइसिंग और प्लान की ज़रूरतें जाँच लें। डॉक्स और प्राइसिंग पेज एक्सेस को अलग-अलग तरह से बताते हैं, इसलिए पक्का करें कि आपके अपने प्लान में क्या शामिल है।
PicassoIA पर Sonnet 5 कैसे इस्तेमाल करें
आपकी टूल डिस्क्रिप्शन भी प्रॉम्प्ट हैं, और इनके लिए लार्ज लैंग्वेज मॉडल सबसे अच्छा एडिटर है। Claude Sonnet 5 इस काम के लिए एक मज़बूत विकल्प है, और आप इसे PicassoIA पर ब्राउज़र छोड़े बिना चला सकते हैं।
लार्ज लैंग्वेज मॉडल कलेक्शन में Claude Sonnet 5 पेज खोलें।
अपनी टूल परिभाषाएँ, जिनमें नाम, डिस्क्रिप्शन और स्कीमा शामिल हैं, प्रॉम्प्ट में चिपकाएँ।
उससे हर डिस्क्रिप्शन को एक छोटे निर्देश के रूप में दोबारा लिखने को कहें, जो बताए कि टूल को कब कॉल करना है और वह क्या लौटाता है।
हर टूल के लिए दस एज केस इनपुट माँगें, जैसे खाली स्ट्रिंग, बहुत लंबा टेक्स्ट और असामान्य टैग।
उन इनपुट्स को MCP Inspector में चलाएँ, हर विफलता ठीक करें, और सुधरी हुई डिस्क्रिप्शन वापस अपने कोड में चिपका दें।
जटिल लॉजिक पर दूसरी राय के लिए, Claude Fable 5 और GPT 5.6 Sol दोनों कोडिंग कार्यों के लिए सूचीबद्ध हैं। एक ही डिस्क्रिप्शन पर उनके दोबारा लिखे गए संस्करणों की तुलना अक्सर यह दिखा देती है कि कौन-सा वाक्यांश अस्पष्ट है।
PicassoIA पर खुद आज़माएँ
अब आपके पास एक ही सर्वर दो असिस्टेंट्स में चल रहा है: याद रखने के लिए नोट्स, आउटपुट के लिए एक इमेज टूल, और एक टेस्टिंग रूटीन जो दोनों को ईमानदार रखता है। यही पैटर्न किसी भी चीज़ पर लागू होता है जिसे आप फ़ंक्शन में लपेट सकते हैं, डिप्लॉय स्क्रिप्ट से लेकर डेटाबेस लुकअप तक।
इमेज टूल से शुरू करें, क्योंकि उससे तुरंत फ़ीडबैक मिलता है। एक प्रॉम्प्ट लिखें, Claude Code या Copilot से generate_image कॉल करें, और सेकंडों में देखें कि क्या वापस आता है। फिर PicassoIA Image पेज खोलें और आस्पेक्ट रेशियो और प्रॉम्प्ट स्टाइल सीधे आज़माएँ, या picassoia.com/en/all-models पर उपलब्ध हर मॉडल देखें। आपकी पहली इमेज बस एक प्रॉम्प्ट दूर है।