كيف تبني خادم MCP محليًا وتربطه بتطبيق Claude (مع كود يعمل)
ابنِ خادم MCP محليًا من مجلد فارغ: ثبّت TypeScript SDK، وسجّل أداتين تعملان، واختبرهما في MCP Inspector، ثم اربط الخادم بتطبيق Claude Desktop و Claude Code. يتضمن ملفات الإعداد، وإصلاحات لمسارات Windows، وقائمة تحقق للأخطاء التي تُفسد معظم الإعدادات.
تكتفي معظم دروس MCP بمثال "hello world" وتتركك تنظر إلى شارة Disconnected الحمراء. ينتهي هذا الدرس بخادم يعمل على جهازك، وClaude يستدعي أدواته، وقائمة تحقق قصيرة للأخطاء التي توقع معظم الناس فيها. ستكتب نحو 70 سطرًا من TypeScript، وتختبرها في أداة فحص تعمل عبر المتصفح، ثم تربط النتيجة بكلٍ من Claude Desktop وClaude Code.
الخادم أداة صغيرة للملاحظات: يستطيع Claude حفظ ملاحظة في ملف JSON على قرصك، والبحث فيها لاحقًا. وهو بسيط عمدًا، لأن آلية الربط واحدة سواء كانت أدواتك تقرأ ملف ملاحظات، أو تستعلم من قاعدة بيانات، أو تستدعي نموذج صور. جمّعتُ ملف الخادم التالي وشغّلته على الإصدار 1.32 من TypeScript SDK، لذلك يُبنى الكود تمامًا كما هو معروض.
ما الذي تبنيه فعلًا
MCP في فقرتين
بروتوكول سياق النموذج (MCP) معيار مفتوح قدّمته Anthropic في نوفمبر 2024، ويتيح لتطبيق الذكاء الاصطناعي التواصل مع الأدوات الخارجية بطريقة واحدة ومتسقة. فبدلًا من أن يخترع كل تطبيق صيغة إضافات خاصة به، يعرض خادم MCP قدراته، ويعثر عليها عميل MCP مثل Claude Desktop أو Claude Code ويستدعيها. الرسائل عبارة عن JSON-RPC 2.0 عادي، لذلك يمكن كتابة الخادم بأي لغة.
يستطيع الخادم أن يقدّم ثلاثة أنواع من الأشياء:
الأدوات (Tools): دوال يستطيع النموذج استدعاءها، مثل "احفظ ملاحظة" أو "شغّل استعلامًا".
الموارد (Resources): بيانات للقراءة فقط يستطيع التطبيق تحميلها كسياق، مثل ملف أو سجل في قاعدة بيانات.
القوالب (Prompts): قوالب أوامر نصية قابلة لإعادة الاستخدام يطلقها المستخدم عن قصد.
يلتزم هذا الدرس بالأدوات فقط، لأنها الأسهل في الاختبار، والأكثر فائدة في اليوم الأول.
لماذا تشغّله محليًا
يعمل الخادم المحلي كعملية فرعية تابعة للعميل، على جهازك، وبملفاتك وصلاحياتك. لا شيء يُعرَّض للإنترنت، ولا توجد فاتورة استضافة، والتكرار سريع: عدّل الملف، ثم أعد البناء، ثم أعد التشغيل. ويستخدم هذا الخادم ناقل stdio: يُشغّل العميل برنامجك ويتواصل معه عبر المدخل والمخرج القياسيين.
stdio (محلي)
Streamable HTTP (عن بُعد)
مكان التشغيل
عملية فرعية على حاسوبك
خادم ويب تستضيفه أنت أو غيرك
من يمكنه الوصول
التطبيق الذي شغّله فقط
أي شخص لديه الرابط وبيانات الدخول
المصادقة
لا توجد، إذ يرث حساب مستخدمك
مطلوبة (OAuth أو رموز)
الأنسب لـ
الأدوات الشخصية، والوصول إلى الملفات، والتطوير
الأدوات المشتركة للفرق، وتكاملات SaaS
💡 معلومة مفيدة: حلّ Streamable HTTP محل ناقل HTTP+SSE القديم في مراجعة المواصفة 2025-03-26. ولأن المتصفح لا يستطيع تشغيل عملية على حاسوبك، لا يمكن إضافة خادم stdio إلى claude.ai في المتصفح. الخوادم البعيدة فقط تعمل هناك.
إعداد المشروع
ما تحتاج إلى تثبيته
الأداة
الإصدار
التحقق بـ
Node.js
20 LTS أو أحدث
node --version
npm
يأتي مع Node
npm --version
Claude Desktop
الأحدث، على macOS أو Windows
Settings ثم Developer
Claude Code (اختياري)
الأحدث
claude --version
يتوفر Claude Desktop لنظامي macOS وWindows. على Linux، استخدم مسار Claude Code في قسم الربط أدناه؛ الخادم نفسه لا يتغير.
⚠️ انتبه: TypeScript 7، وهو الإصدار الذي يثبّته npm اليوم، لم يعد يحمّل حزم @types من تلقاء نفسه. بدون سطر "types": ["node"] ستحصل على Cannot find name 'process' وأخطاء مشابهة عند كل استيراد في Node.
اكتب أول أداتين لك
ملف الخادم الكامل
احفظ هذا الملف باسم src/index.ts. يعرض save_note وsearch_notes، ويخزّن كل شيء في ملف JSON واحد.
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 os from "node:os";
import path from "node:path";
const NOTES_DIR = process.env.NOTES_DIR ?? path.join(os.homedir(), "mcp-notes");
const NOTES_FILE = path.join(NOTES_DIR, "notes.json");
type Note = { id: number; title: string; body: 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: "local-notes", version: "1.0.0" });
server.registerTool(
"save_note",
{
title: "Save note",
description: "Save a short note with a title and a body to the local notes file.",
inputSchema: {
title: z.string().min(1).max(120).describe("Short title for the note"),
body: z.string().min(1).describe("The text of the note"),
},
},
async ({ title, body }) => {
const notes = await readNotes();
const note: Note = {
id: notes.length + 1,
title,
body,
createdAt: new Date().toISOString(),
};
await fs.mkdir(NOTES_DIR, { recursive: true });
await fs.writeFile(NOTES_FILE, JSON.stringify([...notes, note], null, 2));
return { content: [{ type: "text", text: `Saved note #${note.id}: ${title}` }] };
}
);
server.registerTool(
"search_notes",
{
title: "Search notes",
description: "Find saved notes whose title or body contains a word or phrase.",
inputSchema: {
query: z.string().min(1).describe("Word or phrase to look for"),
},
},
async ({ query }) => {
const q = query.toLowerCase();
const hits = (await readNotes()).filter((n) =>
`${n.title} ${n.body}`.toLowerCase().includes(q)
);
if (hits.length === 0) {
return { content: [{ type: "text", text: `No notes match "${query}".` }] };
}
const text = hits.map((n) => `#${n.id} ${n.title}\n${n.body}`).join("\n\n");
return { content: [{ type: "text", text }] };
}
);
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("local-notes MCP server running on stdio");
وظيفة كل جزء
McpServer هو الصنف عالي المستوى. الاسم name والإصدار version اللذان تمررهما يظهران في قائمة الخوادم لدى العميل وفي سجلاته.
registerTool يأخذ اسم أداة، وكائن إعداد (title وdescription وinputSchema)، ومعالجًا غير متزامن.
مخطط Zod يُتحقق منه قبل تشغيل المعالج، ثم يُحوَّل إلى JSON Schema حتى يستطيع Claude قراءته. كل وصف .describe() يصل إلى النموذج.
القيمة المرجعة تكون دائمًا { content: [...] }. النص العادي أبسط نوع محتوى، والصور وروابط الموارد مدعومة أيضًا.
StdioServerTransport يقرأ الطلبات من stdin ويكتب الردود إلى stdout.
💡 نصيحة: اكتب الأوصاف للنموذج، لا للبشر. الوصف "ابحث عن الملاحظات المحفوظة التي يحتوي عنوانها أو نصها على كلمة أو عبارة" يخبر Claude متى يستدعي الأداة. أما "بحث" فلا يفعل ذلك. ويختار Claude الأدوات غالبًا من أسمائها وأوصافها.
بما أن search_notes لا يغيّر أي شيء أبدًا، ضع ذلك عليه في إعداداته: annotations: { readOnlyHint: true }. التلميحات استشارية، والعملاء يقررون بأنفسهم مقدار الثقة التي يمنحونها لها، لكنها تساعد العملاء الملتزمين على معاملة الأدوات التي تقرأ فقط بلطف أكبر.
لا تطبع إلى stdout أبدًا
مع stdio، يكون stdout هو قناة البروتوكول. سطر console.log("started") واحد شارد يدفع سطرًا غير JSON إلى التدفق، وأغلب العملاء سيقطعون الاتصال أو يعرضون الخادم على أنه فشل. تذكّر هذه القاعدة وستتجنب أكثر أخطاء اليوم الأول شيوعًا:
افعل: استخدم console.error("message")، الذي يكتب إلى stderr، حيث تجمع العملاء السجلات.
لا تفعل: استخدم console.log(...) أو process.stdout.write(...) في أي موضع من خادمك، بما في ذلك داخل المكتبات التي تستوردها.
اختبره قبل أن يختبره Claude
شغّل MCP Inspector
MCP Inspector هو أداة التصحيح الرسمية. يشغّل خادمك بالطريقة نفسها التي يشغّله بها العميل، ويعطيك أزرارًا بدلًا من الأوامر النصية.
npm run build
npx @modelcontextprotocol/inspector node build/index.js
تفتح صفحة في متصفحك. انقر Connect، وافتح تبويب Tools، واضغط List Tools. يجب أن ترى save_note وsearch_notes مع مخططاتهما. شغّل save_note بعنوان ونص، ثم شغّل search_notes بكلمة من ذلك النص. يعيد الاستدعاء الأول Saved note #1: Standup، ويظهر ملف الملاحظات في مجلد mcp-notes داخل مجلدك الشخصي (أو في NOTES_DIR إذا حددت مسارًا آخر).
أرسل رسائل JSON-RPC خام
إذا أردت رؤية البروتوكول نفسه، فإن stdio يستخدم رسالة JSON واحدة في كل سطر. ضع هذه الأسطر الثلاثة في requests.jsonl:
على macOS يبدو المسار كالتالي /Users/you/projects/local-notes-mcp/build/index.js. احفظ الملف، ثم أغلق Claude Desktop إغلاقًا كاملًا (على Windows، من علبة النظام لا بزر الإغلاق في النافذة فقط)، ثم أعد فتحه. ستظهر أدواتك في قائمة الأدوات بجوار حقل الدردشة، ويطلب Claude إذنك قبل تشغيل أي منها.
أضفه إلى Claude Code
لا يحتاج Claude Code إلى تعديل أي ملف. أمر واحد يسجّل الخادم:
كل ما يأتي بعد الشرطتين المزدوجتين هو الأمر الذي يشغّل خادمك. تحقق منه بـ claude mcp list، أو اكتب /mcp داخل جلسة لترى حالته. ويحدد علم النطاق من يحصل على الخادم:
النطاق
مكان التخزين
من يراه
local (الافتراضي)
إعداداتك الخاصة بهذا المشروع
أنت فقط، في هذا المشروع
project (--scope project)
.mcp.json في المستودع
كل من يستنسخه، بعد أن يوافق عليه
user (--scope user)
إعدادات مستخدمك
أنت فقط، في كل المشاريع
جرّب أمرًا نصيًا حقيقيًا
اسأل Claude شيئًا يفرض استدعاء أداة:
احفظ ملاحظة بعنوان "Standup" تقول "Ship the MCP post on Friday". ثم ابحث في ملاحظاتي عن "Friday".
يستدعي Claude save_note، ثم search_notes، ويعيد اقتباس النتيجة لك. افتح notes.json لتتأكد من أن البيانات وصلت إلى قرصك. إن وصلت، فلديك خادم MCP محلي يعمل.
إصلاح الأعطال وتأمين الخادم
إصلاح الأخطاء الشائعة
العَرَض
السبب المرجح
الحل
الخادم يظهر كفاشل أو غير متصل
مخرجات على stdout، أو تعطل عند البدء
شغّل node build/index.js يدويًا واقرأ stderr؛ وأزل كل console.log
لا توجد أدوات بعد تعديل الإعداد
العميل ما زال يعمل، أو JSON غير صالح
أغلقه بالكامل؛ وتحقق من الفواصل الزائدة في آخر العناصر
spawn node ENOENT
التطبيق لا يجد node في PATH الخاص به
استخدم المسار المطلق لملف node التنفيذي بدلًا من command
يعمل في Inspector ويفشل في Claude
مسارات نسبية أو متغيرات بيئة مفقودة
مسارات مطلقة، وضع المتغيرات في env
تغييرات الكود بلا أثر
لم تُعِد البناء أو لم تُعِد التشغيل
شغّل npm run build، ثم أعد تشغيل العميل
تسبب تفصيلتان في Windows نصف المتاعب المتبقية. يجب مضاعفة الشرطات المائلة للخلف داخل نصوص JSON (C:\\Users\\you\\...)، أو يمكنك ببساطة استخدام الشرطات المائلة للأمام كما في المثال أعلاه. وعلى Windows الأصلي، غالبًا ما تحتاج الخوادم التي تُشغَّل عبر npx إلى غلاف cmd /c في command؛ أما أمر node العادي فلا يكفي.
عندما يستمر أي عطل، اقرأ السجلات. يكتب Claude Desktop سجلًا لكل خادم، في ~/Library/Logs/Claude على macOS وفي %APPDATA%\Claude\logs على Windows. وتظهر أسطر console.error الخاصة بك هناك.
إعدادات أمان افتراضية تستحق الاحتفاظ بها
يرث خادم stdio صلاحياتك أنت، لذلك عامل كل أداة كأنها كود يمكنه التصرف باسمك.
حدّ من نطاق الضرر. أبقِ الوصول إلى الملفات داخل مجلد واحد. إذا قبلت أداة مسارًا، فحوّله إلى مسار كامل وارفض كل ما يقع خارج المجلد المسموح به.
تحقق من كل مدخل. قواعد Zod مثل min وmax وenum لا تكلّف شيئًا، وتمنع الاستدعاءات المشوهة قبل تشغيل معالجك.
أبعد الأسرار عن الكود. ضع الرموز في كتلة env من إعدادك، واحتفظ بهذا الملف بعيدًا عن نظام التحكم بالإصدارات.
اقرأ قبل أن تثبّت. لا تضف إلا خوادم الطرف الثالث التي فحصت مصدرها. إنها تعمل بصلاحيات حسابك.
عامل مخرجات الأدوات كأنها غير موثوقة. قد يحمل النص الذي تجلبه أداتك من صفحات الويب أو رسائل البريد تعليمات موجّهة إلى النموذج. أعده كبيانات، وأبقِ عمليات الكتابة خلف تأكيد.
الانتقال من stdio إلى HTTP
عندما يحتاج زملاؤك إلى الأدوات نفسها، بدّل ناقل الاتصال. يأتي SDK مع StreamableHTTPServerTransport، الذي يقدّم McpServer نفسها عبر HTTP خلف مصادقتك الخاصة. لا تتغير استدعاءات registerTool لديك. الذي يتغير فقط هو الناقل وتسجيل العميل، على سبيل المثال claude mcp add --transport http notes https://your-host/mcp.
يمكنك رؤية هذا النمط في الاستخدام الفعلي. موصل PicassoIA في claude.ai هو خادم MCP بعيد يعرض توليد الصور وتحريرها وتوليد الفيديو كأدوات، ويستدعيها Claude دون أي عملية محلية على الإطلاق.
صِغ مواصفات الأدوات على PicassoIA
تبدأ الأدوات الجيدة بأسماء وأوصاف جيدة، ونموذج لغوي طريقة سريعة لصياغتها قبل كتابة الكود. يستضيف PicassoIA 75 نموذجًا نصيًا في فئة النماذج اللغوية الكبيرة لديه، ومنها Claude Sonnet 5، المصمم لمهام البرمجة. إليك طريقة استخدامه في تصميم الأدوات.
صِف الأداة بلغة بسيطة: ما الذي تفعله، وما المدخلات التي تأخذها، وما الذي تعيده، وهل تغيّر أي شيء.
اطلب صيغة مخرجات ثابتة: اسم أداة بصيغة snake_case، ووصفًا من جملتين على الأكثر مكتوبًا للنموذج، ومخطط Zod مع .describe() على كل حقل، وثلاث حالات حدّية يجب أن تفشل في التحقق.
ألصق الناتج في استدعاء registerTool، وأعد البناء، واختبره في Inspector.
عدّل الوصف لا المخطط عندما يختار Claude الأداة الخطأ. الصياغة هي المشكلة عادةً.
أمر نصي يعمل جيدًا:
أنا أبني أداة MCP اسمها list_overdue_tasks. تقرأ ملف tasks.json، وتعيد المهام التي يسبق فيها dueDate تاريخ اليوم، ولا تغيّر أي شيء. اكتب اسم الأداة، ووصفًا من جملتين لنموذج ذكاء اصطناعي، ومخطط إدخال Zod مع describe() على كل حقل، وثلاثة مدخلات غير صالحة يجب أن ترفضها.
بالنسبة إلى إعادة الهيكلة الأكبر، مثل تقسيم خادم من 600 سطر إلى وحدات، جرّب Claude Fable 5 أو Claude Opus 4.7 مع لصق ملفك كاملًا.
أنشئ أول صورة لك على PicassoIA
خادم الملاحظات الخاص بك قالب جاهز. استبدل ملف JSON باستدعاء لنموذج صور، وسيستطيع Claude عندها رسم الصور عند الطلب. لا تحتاج إلى بناء ذلك أولًا لترى النتيجة. كل صورة في هذا المقال أُنشئت بواسطة P-Image، وهو أحد نماذج تحويل النص إلى صورة على Picasso IA.
افتح Picasso IA، واكتب جملة واحدة تصف مشهدًا، ثم أنشئ الصورة. بعد ذلك جرّب ثلاث تجارب:
غيّر العدسة. أعد كتابة الأمر النصي نفسه مرة بعبارة "35mm" ومرة بعبارة "85mm"، وقارن التكوين.
غيّر الإضاءة. استبدل "ضوء نافذة الصباح" بـ "ضوء مصباح مكتب دافئ"، وراقب تغير المزاج.
غيّر الزاوية. اطلب لقطة من الأعلى، ثم لقطة من زاوية منخفضة للشخص نفسه.
ابنِ أداة واحدة تتمنى أن يكون لدى Claude، واربطها بالخطوات أعلاه، ثم اقضِ عشر دقائق على Picasso IA لإنشاء الصور لمشروعك. يستغرق بناء الخادم فترة بعد الظهر، أما الصور فتستغرق ثوانٍ.