بناء أول خادم MCP: دليل عملي للمطورين
Engineering Team
ما هو Model Context Protocol (MCP)؟
Model Context Protocol (MCP) هو معيار مفتوح أنشأته Anthropic يحدد كيفية تواصل تطبيقات AI مع مصادر البيانات والأدوات الخارجية. فكر في MCP كـ USB-C لدمج AI: تمامًا كما يوفر USB-C موصلًا عالميًا واحدًا للشحن ونقل البيانات وإخراج الشاشة، يوفر MCP بروتوكولًا عالميًا واحدًا لربط نماذج AI بقواعد البيانات وواجهات API وأنظمة الملفات وأي خدمة أخرى. يلغي MCP الحاجة إلى عمليات دمج مخصصة بين كل تطبيق AI وكل أداة.
في عام 2026، أصبح MCP المعيار المهيمن لاتصالات AI-الأدوات. تتوقع Gartner أن 40% من تطبيقات المؤسسات ستتضمن وكلاء AI بحلول نهاية 2026، و MCP هو البروتوكول الذي يجعل هذا عمليًا على نطاق واسع. قبل MCP، كان ربط نموذج AI بـ N أداة يتطلب N عملية دمج مخصصة. مع M تطبيق AI و N أداة، كنت بحاجة إلى M x N محول دمج. يختزل MCP هذا إلى M + N: كل تطبيق AI ينفذ MCP client واحد، وكل أداة تنفذ MCP server واحد.
بنية MCP: Host و Client و Server و Transport
فهم بنية MCP ضروري قبل كتابة أي كود. يحدد البروتوكول أربعة أدوار رئيسية:
| المكون | الدور | مثال |
|---|---|---|
| Host | تطبيق AI الذي يتفاعل معه المستخدم النهائي | Claude Desktop، Cursor، VS Code Copilot |
| Client | معالج بروتوكول MCP داخل الـ Host | مدمج في تطبيق الـ Host |
| Server | يكشف tools و resources و prompts عبر MCP | خادمك المخصص (ما سنبنيه) |
| Transport | طبقة الاتصال بين Client و Server | stdio، HTTP+SSE، Streamable HTTP |
يعمل تدفق الاتصال كالتالي:
- يبدأ Host ويهيئ MCP Client لكل خادم مُعد.
- يتصل Client بـ Server عبر Transport (stdio للمحلي، HTTP للبعيد).
- يرسل Client طلب
initialize، يتفاوض على إصدار البروتوكول والقدرات. - يستجيب Server بـ tools و resources و prompts المتاحة.
- عندما يحتاج نموذج AI إلى بيانات أو إجراءات خارجية، يستدعي Client طريقة الخادم المناسبة.
- ينفذ Server العملية ويعيد النتائج عبر رسائل JSON-RPC 2.0.
Tools مقابل Resources مقابل Prompts
يمكن لخوادم MCP كشف ثلاثة أنواع من القدرات:
- Tools هي دوال يمكن لنموذج AI استدعاؤها. تقبل مدخلات منظمة وتعيد مخرجات منظمة. مثال:
query_database(sql: string)أوsend_email(to: string, subject: string, body: string). - Resources هي بيانات يمكن لنموذج AI قراءتها. يتم تحديدها بواسطة URIs وتعيد محتوى. مثال:
file:///path/to/document.mdأوpostgres://localhost/mydb/users. - Prompts هي قوالب prompts قابلة لإعادة الاستخدام يوفرها الخادم. تساعد في توحيد طريقة تفاعل AI مع مجال الخادم. مثال: قالب
summarize_ticketلخادم Jira MCP.
خطوة بخطوة: بناء خادم MCP في TypeScript
لنبنِ خادم MCP عملي يوفر أداة للاستعلام من قاعدة بيانات SQLite. هذا حالة استخدام شائعة: منح نموذج AI وصولًا آمنًا للقراءة فقط إلى بيانات تطبيقك.
الخطوة 1: تهيئة المشروع
mkdir mcp-sqlite-server && cd mcp-sqlite-server
npm init -y
npm install @modelcontextprotocol/sdk better-sqlite3
npm install -D typescript @types/node @types/better-sqlite3
npx tsc --init
تكوين tsconfig.json:
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"declaration": true
},
"include": ["src/**/*"]
}
الخطوة 2: تنفيذ الخادم
إنشاء src/index.ts:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import Database from "better-sqlite3";
import { z } from "zod";
const db = new Database(process.env.DB_PATH || "./data.db", {
readonly: true,
});
const server = new McpServer({
name: "sqlite-query",
version: "1.0.0",
});
server.tool(
"query",
"تنفيذ استعلام SQL للقراءة فقط على قاعدة بيانات SQLite",
{
sql: z.string().describe("استعلام SQL SELECT المراد تنفيذه"),
},
async ({ sql }) => {
const normalized = sql.trim().toUpperCase();
if (!normalized.startsWith("SELECT")) {
return {
content: [{ type: "text", text: "خطأ: يُسمح فقط باستعلامات SELECT." }],
isError: true,
};
}
try {
const rows = db.prepare(sql).all();
return {
content: [{ type: "text", text: JSON.stringify(rows, null, 2) }],
};
} catch (error) {
return {
content: [{ type: "text", text: `خطأ في الاستعلام: ${(error as Error).message}` }],
isError: true,
};
}
}
);
server.tool(
"list_tables",
"عرض جميع الجداول في قاعدة البيانات مع مخططاتها",
{},
async () => {
const tables = db
.prepare(
`SELECT name, sql FROM sqlite_master
WHERE type='table' AND name NOT LIKE 'sqlite_%'
ORDER BY name`
)
.all();
return {
content: [{ type: "text", text: JSON.stringify(tables, null, 2) }],
};
}
);
الخطوة 3: البناء والاختبار المحلي
npx tsc
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}' | node dist/index.js
يجب أن ترى استجابة JSON-RPC تحتوي على قدرات الخادم.
الخطوة 4: بناء نفس الخادم بلغة Python
لمطوري Python، إليك التنفيذ المكافئ باستخدام SDK Python الرسمي لـ MCP:
import sqlite3
import json
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("sqlite-query")
DB_PATH = "data.db"
@mcp.tool()
def query(sql: str) -> str:
"""تنفيذ استعلام SQL للقراءة فقط على قاعدة بيانات SQLite."""
normalized = sql.strip().upper()
if not normalized.startswith("SELECT"):
raise ValueError("يُسمح فقط باستعلامات SELECT.")
conn = sqlite3.connect(DB_PATH)
conn.row_factory = sqlite3.Row
try:
cursor = conn.execute(sql)
rows = [dict(row) for row in cursor.fetchall()]
return json.dumps(rows, indent=2, default=str)
finally:
conn.close()
@mcp.tool()
def list_tables() -> str:
"""عرض جميع الجداول في قاعدة البيانات مع مخططاتها."""
conn = sqlite3.connect(DB_PATH)
conn.row_factory = sqlite3.Row
try:
cursor = conn.execute(
"SELECT name, sql FROM sqlite_master "
"WHERE type='table' AND name NOT LIKE 'sqlite_%'"
)
tables = [dict(row) for row in cursor.fetchall()]
return json.dumps(tables, indent=2)
finally:
conn.close()
if __name__ == "__main__":
mcp.run(transport="stdio")
الاتصال بـ Claude Desktop
يدعم Claude Desktop خوادم MCP بشكل أصلي. لربط خادمك، عدّل ملف تكوين Claude Desktop:
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
Windows: %APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"sqlite-query": {
"command": "node",
"args": ["/absolute/path/to/dist/index.js"],
"env": {
"DB_PATH": "/absolute/path/to/your/data.db"
}
}
}
}
أعد تشغيل Claude Desktop. سترى أيقونة مطرقة في واجهة المحادثة تشير إلى أدوات MCP المتاحة. الآن يمكنك سؤال Claude مثل “ما الجداول الموجودة في قاعدة البيانات؟” أو “أظهر لي أعلى 10 مستخدمين حسب تاريخ التسجيل” وسيستخدم Claude خادم MCP الخاص بك للاستعلام من قاعدة البيانات مباشرة.
الاتصال بـ Cursor
يدعم Cursor أيضًا خوادم MCP. أضف التكوين إلى .cursor/mcp.json في جذر مشروعك:
{
"mcpServers": {
"sqlite-query": {
"command": "node",
"args": ["./dist/index.js"],
"env": {
"DB_PATH": "./data.db"
}
}
}
}
بعد الحفظ، أعد تشغيل Cursor. ستظهر أدوات MCP في لوحة مساعد AI ويمكن استدعاؤها أثناء جلسات إنشاء الكود وتصحيح الأخطاء.
اختبار وتصحيح أخطاء خادم MCP
استخدام MCP Inspector
MCP Inspector هو أداة التصحيح الرسمية. يوفر واجهة ويب للتفاعل مع خادمك:
npx @modelcontextprotocol/inspector node dist/index.js
يفتح هذا واجهة متصفح على http://localhost:5173 حيث يمكنك:
- عرض جميع tools و resources و prompts المسجلة
- استدعاء tools بمدخلات مخصصة وفحص الاستجابات
- مراقبة تدفق رسائل JSON-RPC في الوقت الفعلي
- اختبار معالجة الأخطاء بإرسال طلبات مشوهة
مشاكل التصحيح الشائعة
| المشكلة | السبب | الحل |
|---|---|---|
| الخادم لا يظهر في Claude Desktop | مسار التكوين أو خطأ في صيغة JSON | تحقق من JSON، تحقق من المسارات المطلقة |
| خطأ “Tool not found” | الأدوات غير مسجلة قبل الاتصال | سجل الأدوات قبل استدعاء server.connect() |
| انتهاء المهلة عند استدعاء الأدوات | عملية طويلة بدون تقدم | أضف إشعارات التقدم عبر server.sendProgress() |
| مخرجات stderr تفسد البروتوكول | console.log يكتب إلى stdout (نقل stdio) | استخدم console.error() للتسجيل مع stdio |
| انقطاع الاتصال بعد الخمول | انتهاء مهلة النقل | نفذ heartbeat أو استخدم نقل HTTP |
أفضل الممارسات للإنتاج
-
التحقق من المدخلات: تحقق دائمًا من مدخلات الأدوات ونظفها. استخدم مخططات Zod (TypeScript) أو نماذج Pydantic (Python) للتحقق الصارم من الأنواع.
-
القراءة فقط افتراضيًا: ابدأ بالوصول للقراءة فقط. أضف قدرات الكتابة فقط عند الحاجة الفعلية، واطلب دائمًا التأكيد للعمليات التدميرية.
-
معالجة الأخطاء: أعد رسائل خطأ منظمة مع
isError: true. لا تكشف أبدًا عن تتبعات المكدس الداخلية أو سلاسل اتصال قاعدة البيانات. -
التسجيل: سجل جميع استدعاءات الأدوات مع الطابع الزمني والمدخلات ومدة التنفيذ. استخدم stderr للتسجيل (وليس stdout) مع نقل stdio.
-
تحديد المعدل: نفذ حدود معدل لكل أداة لمنع حلقات AI غير المتحكم فيها من إغراق خدمات الواجهة الخلفية.
-
المهلة الزمنية: عيّن مهلات تنفيذ على جميع معالجات الأدوات. قد تستدعي نماذج AI أدوات تطلق استعلامات مكلفة — احمِ بنيتك التحتية.
-
فصل البيئات: استخدم متغيرات البيئة لجميع التكوينات. لا تشفر أبدًا عناوين URL لقواعد البيانات أو مفاتيح API أو مسارات الملفات.
-
التنسيق: استخدم التنسيق الدلالي لخادم MCP الخاص بك. يتضمن مصافحة
initializeتفاوض الإصدار — التغييرات غير المتوافقة تتطلب زيادة في الإصدار الرئيسي.
الأسئلة الشائعة
س: ما الفرق بين MCP و function calling؟ ج: يحدد function calling (المستخدم من OpenAI و Anthropic وغيرهم) الأدوات بشكل مضمن في كل طلب API. يُخرج MCP تعريفات الأدوات إلى خوادم مستقلة يمكن لمضيفات MCP المتوافقة اكتشافها واستخدامها. Function calling يعمل لكل طلب؛ MCP هو بروتوكول دائم مع جلسات ذات حالة.
س: هل يمكنني استخدام MCP مع نماذج غير Claude؟ ج: نعم. MCP هو بروتوكول مفتوح. اعتمدت OpenAI و Google DeepMind و Microsoft دعم MCP في منصاتهم منذ أوائل 2026. أي تطبيق AI ينفذ MCP client يمكنه الاتصال بأي MCP server.
س: هل MCP للأدوات المحلية فقط؟ ج: لا. بينما صُمم نقل stdio للخوادم المحلية، يدعم نقل HTTP+SSE و Streamable HTTP خوادم MCP البعيدة. يمكنك نشر خادم MCP كخدمة سحابية يمكن الوصول إليها عبر الشبكة.
س: كيف يتعامل MCP مع المصادقة؟ ج: يدعم البروتوكول OAuth 2.0 للخوادم البعيدة. ترث خوادم stdio المحلية سياق أمان عملية المضيف. لعمليات النشر المؤسسية، يمكن لبوابة MCP مركزة المصادقة والتفويض.
س: ما اللغات التي يمكن استخدامها لبناء خادم MCP؟ ج: تتوفر SDKs الرسمية لـ TypeScript و Python و Java و Kotlin و C# و Swift. تشمل SDKs المجتمع Rust و Go و Ruby و PHP. البروتوكول لا يعتمد على لغة معينة — أي لغة يمكنها قراءة/كتابة JSON-RPC عبر stdio أو HTTP يمكنها تنفيذ خادم MCP.
س: كيف تحدث خادم MCP دون إعادة تشغيل المضيف؟
ج: يدعم MCP إشعارات تغيير القدرات. عندما تتغير أدوات خادمك، يمكن للخادم إرسال رسالة notifications/tools/list_changed، مما يدفع client لإعادة جلب قائمة الأدوات. لخوادم stdio، عادة ما يحتاج المضيف لإعادة التشغيل. يمكن تحديث خوادم HTTP دون إعادة تشغيل المضيف.