ابنِ خادم MCP واحدًا بلغة TypeScript وسجّله في كل من Claude Code وGitHub Copilot. ستحصل على كود أدوات يعمل، وإعدادات دقيقة لكل عميل، وروتين لتصحيح الأخطاء باستخدام MCP Inspector، وأداة لتوليد الصور تستدعي واجهة PicassoIA API.
كتبتَ سكربتًا يوفّر عليك عشر دقائق يوميًا، والآن تريد أن يشغّله مساعدك الذكي دون المرور بخطوة النسخ واللصق. ابنِه مرة واحدة كخادم Model Context Protocol، وسيتمكن كل من Claude Code وGitHub Copilot من استدعاء الأدوات نفسها، لأن MCP هي اللغة المشتركة التي يتخاطبان بها مع أي شيء خارج المحرر. يبني هذا الدليل خادمًا صغيرًا بلغة TypeScript، ويسجّله في Claude Code، ثم يسجّله في Copilot داخل VS Code، وينتهي بأداة صور حقيقية تستدعي واجهة PicassoIA API. خصّص نحو 40 دقيقة وحوالي 100 سطر من الكود.
لماذا يتفوّق خادم واحد على اثنين
قبل MCP، كان لكل مساعد تنسيق إضافات خاص به، وملف بيان خاص به، وقواعد تغليف خاصة به. يستبدل خادم MCP كل ذلك بعملية واحدة تعلن ما تستطيع فعله. يشغّل العميل العملية، ويطلب منها قائمة إمكانياتها، ثم يمرّر هذه القائمة إلى النموذج. بعد ذلك يقرّر النموذج، أثناء المحادثة، متى يستحق الاستدعاء.
البروتوكول نفسه، وعميلان
يمكن للخادم أن يعرض ثلاثة أنواع من الإمكانيات:
الأدوات: دوال يستطيع النموذج استدعاءها، مثل add_note أو generate_image.
الموارد: بيانات للقراءة فقط يمكن للعميل إرفاقها بالمحادثة، مثل ملف سجل أو مخطط.
الأوامر الجاهزة: قوالب قابلة لإعادة الاستخدام يُفعّلها المستخدم عن قصد.
الأدوات هي مصدر معظم القيمة اليوم، لذلك يركّز هذا المقال عليها. يتحدث كل من Claude Code و Copilot لغة رسائل JSON-RPC نفسها عبر وسائل النقل نفسها، وهذا يعني أن الخادم الذي يعمل مع أحدهما سيعمل مع الآخر تقريبًا دون تغييرات.
أين تختلف الإعدادات
الخادم واحد، لكن التسجيل مختلف. إليك الفرق كاملًا في جدول واحد:
الإعداد
Claude Code
GitHub Copilot في VS Code
ملف الإعداد
.mcp.json في المشروع، أو ~/.claude.json
.vscode/mcp.json، أو ملف تعريف المستخدم
الخاصية الجذرية
mcpServers
servers
الإضافة من الطرفية
claude mcp add
لوحة الأوامر: MCP: Add Server
حقل النقل
type (stdio، http، sse)
type إلزامي (stdio أو http)
الأسرار
علامة --env أو توسيع ${VAR}
كتلة inputs مع ${input:id}
مكان تشغيل الأدوات
أي جلسة
وضع الوكيل فقط
💡 نصيحة: الخاصية الجذرية هي الفخ الكلاسيكي. إذا لصقتَ إعداد Claude Code في VS Code دون تعديل، فلن يُحمَّل أي شيء، لأن Copilot يبحث عن servers وليس mcpServers.
إعداد المشروع
اختر مجلدًا خارج مستودعك الرئيسي حتى يتمكن الخادم من خدمة عدة مشاريع لاحقًا. تحتاج إلى Node.js 20 أو أحدث وإلى طرفية.
تستخدم الأمثلة API @modelcontextprotocol/sdk 1.x مع McpServer وregisterTool. إذا غيّر إصدار رئيسي أحدث مسار استيراد، فستبقى المفاهيم الواردة أدناه كما هي.
البدء مع stdio
يحدد MCP وسيلتي نقل رئيسيتين. stdio تعني أن العميل يشغّل خادمك كعملية فرعية ويتبادل الرسائل عبر الإدخال والإخراج القياسيين. أما 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، الإخراج القياسي ملك للبروتوكول. يكفي استدعاء console.log واحد عابر ليحقن نصًا عاديًا في تدفق JSON-RPC، فيُسقط العميل الاتصال. سجّل عبر console.error، الذي يكتب إلى stderr، وسيلتقطه العميلان كمخرجات تشخيصية بدلًا من ذلك.
💡 نصيحة: اكتب أوصاف الأدوات كتعليمات للنموذج، لا كتوثيق للبشر. عبارة "استخدمها عندما يطلب المستخدم تذكّر قرار" تجعل الأداة تُختار في اللحظة المناسبة، أما "أداة ملاحظات" فلا تفعل.
الاتصال مع 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 افتراضيًا، وأدوات MCP لا تعمل إلا في وضع Agent. بدّل الوضع في لوحة الدردشة، وافتح منتقي الأدوات، وتأكد من ظهور team-notes مع تحديد الأداتين. إذا لم يظهر، فشغّل 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
التبديل إلى وضع Agent
الأداة موجودة لكنها لا تُختار أبدًا
وصف غامض
أعد كتابته بعبارات محفّزة
متغير بيئة فارغ
غير مُعلن في الإعداد
أضفه إلى كتلة env
استدعاءات الصور تفشل تحت الضغط
أكثر من 5 مهام في وقت واحد
ضع الاستدعاءات في طابور داخل الأداة
أضِف أداة توليد صور إلى خادمك
الملاحظات مفيدة، لكن أفضل عرض لقدرات MCP هو أداة تفعل شيئًا لا يستطيع المساعد فعله وحده. توليد الصور خيار مناسب: يكتب النموذج أمرًا نصيًا دقيقًا، ويحوّله خادمك إلى ملف، ويعود الرابط مباشرة إلى المحادثة.
استدعاء واجهة PicassoIA API
تتوفر PicassoIA API للمطورين على https://api.picassoia.com/v1، وتستخدم توكن Bearer يبدأ بالنص pia_sk_. التنبؤات غير متزامنة وتعمل بأسلوب Replicate: تنشئ تنبؤًا، ثم تستعلم عنه بشكل متكرر حتى تصبح حالته succeeded. يقبل نموذج PicassoIA Imageprompt بطول يصل إلى 4,000 حرف، وكذلك aspect_ratio، ويُعيد قائمة بعناوين URL للصور. أضف هذه الأداة قبل سطر server.connect:
يسمح الحساب بعدد 5 تنبؤات متزامنة، مشتركة بين كل التوكنات والاتصالات، لذلك ستصطدم حلقة تُطلق عشر صور دفعة واحدة بهذا السقف. ولّد الصور واحدة تلو الأخرى داخل الأداة، أو احتفظ بطابور صغير.
💡 نصيحة: تحقّق من التسعير الحالي ومتطلبات الخطة في صفحة واجهة PicassoIA API قبل أن تنشر خادمًا ليستخدمه الآخرون. الوثائق وصفحة التسعير تصفان الوصول بطريقتين مختلفتين، لذا تأكد مما تتضمنه خطتك أنت.
كيفية استخدام Sonnet 5 على PicassoIA
أوصاف أدواتك أوامر نصية، ونموذج لغوي هو أفضل محرر لها. يُعد Claude Sonnet 5 خيارًا قويًا لهذه المهمة، ويمكنك تشغيله على PicassoIA دون مغادرة المتصفح.
افتح صفحة Claude Sonnet 5 في مجموعة النماذج اللغوية الكبيرة.
الصق تعريفات أدواتك، بما فيها الأسماء والأوصاف والمخططات، في الأمر النصي.
اطلب منه إعادة كتابة كل وصف كتعليمة قصيرة تحدد متى تُستدعى الأداة وماذا تُرجع.
اطلب عشر مدخلات لحالات حدّية لكل أداة، مثل السلاسل الفارغة والنصوص الطويلة جدًا والوسوم غير المعتادة.
مرّر تلك المدخلات عبر MCP Inspector، وأصلح كل فشل، والصق الأوصاف المحسّنة في كودك.
للحصول على رأي ثانٍ في المنطق المعقد، يُدرج كلٌّ من Claude Fable 5 وGPT 5.6 Sol ضمن مهام البرمجة. ومقارنة إعادة كتابتهما لوصف واحد تكشف غالبًا أي صياغة ملتبسة.
جرّبه بنفسك على PicassoIA
أصبح لديك الآن خادم واحد يعمل في مساعدين: ملاحظات للذاكرة، وأداة صور للمخرجات، وروتين اختبار يُبقي الاثنين صادقين. يمكن لهذا النمط أن يتوسع إلى أي شيء تستطيع تغليفه في دالة، من سكربتات النشر إلى عمليات البحث في قواعد البيانات.
ابدأ بأداة الصور، لأنها تعطيك ردود فعل فورية. اكتب أمرًا نصيًا، واستدعِ generate_image من Claude Code أو Copilot، وشاهد النتيجة خلال ثوانٍ. ثم افتح صفحة PicassoIA Image وجرّب نسب العرض إلى الارتفاع وأساليب الأوامر النصية مباشرة، أو تصفّح كل النماذج المتاحة على picassoia.com/en/all-models. صورتك الأولى لا تبعد عنك سوى أمر نصي واحد.