أمثلة خوادم MCP بلغتي Python وTypeScript (كود GitHub) التي تعمل على أحدث حزم SDK
أمثلة خوادم MCP جاهزة للنسخ بلغتي Python وTypeScript، مبنية على مستودعات SDK الرسمية على GitHub. يعمل كل مثال عبر stdio أو Streamable HTTP، ويغلّف المثالان الأخيران API للصور والفيديو ليتمكن المساعد من توليد الوسائط من نافذة الدردشة.
خادم MCP برنامج صغير يزوّد مساعدًا ذكيًا بقائمة من الأدوات التي يستطيع استدعاءها، وأسرع طريقة لفهم عمله هي قراءة بعض الأمثلة التي تعمل فعلًا. يجمع هذا المقال أمثلة خوادم MCP بلغتي Python وTypeScript، مكتوبة وفق مستودعات SDK الرسمية على GitHub، لتتمكن من نسخ الملف وتشغيله ومشاهدة المساعد يستخدمه خلال دقائق.
كل ما يلي يتبع وثائق SDK حتى أكتوبر 2026، وهذا مهم لأن الحزمتين وصلتا مؤخرًا إلى الإصدار الرئيسي الثاني. في Python تحوّل FastMCP إلى MCPServer. في TypeScript انتقل كود الخادم إلى حزمة @modelcontextprotocol/server مستقلة. يبني النصف الأول من المقال خادمًا بسيطًا في كل لغة. ويضيف النصف الثاني أدوات تولّد الصور والفيديو، وهنا يتوقف MCP عن كونه عرضًا تجريبيًا ويبدأ في توفير جهد حقيقي.
ماذا يفعل خادم MCP؟
بروتوكول سياق النموذج (MCP) معيار مفتوح يتيح لعميل ذكاء اصطناعي، مثل تطبيق دردشة أو بيئة تطوير أو وكيل، استدعاء كود كتبته أنت. يفتح العميل اتصالًا، ويسأل خادمك عمّا يقدّمه، ويترك للنموذج تحديد متى يستخدم كل عنصر. تنتقل الرسائل بصيغة JSON-RPC، ولا يستدعي خادمك النموذج بنفسه أبدًا. بل ينتظر الطلب، ويشغّل الدالة، ويعيد النتيجة.
الأدوات والموارد والأوامر
يتكون كل خادم من ثلاثة أنواع من اللبنات الأساسية:
الأدوات دوال يستطيع النموذج استدعاءها، مثل add أو generate_image. قد تترك آثارًا جانبية، لذلك يجب تصميمها بعناية أكبر.
الموارد بيانات للقراءة فقط يُشار إليها بعنوان URI، مثل greeting://alice أو مسار ملف. يقرؤها العميل ليزوّد النموذج بالسياق.
الأوامر قوالب رسائل قابلة لإعادة الاستخدام يختارها الشخص من قائمة، مثل طلب مراجعة كود.
💡 نصيحة: ابدأ بالأدوات أولًا. معظم الخوادم على GitHub تعرض أدوات فقط، ويختار النموذج الأداة الصحيحة بثبات أكبر من قائمة قصيرة من أدوات بأسماء واضحة، مقارنةً بقائمة طويلة من أدوات غامضة.
أي خط من SDK تثبّت
لدى كل من حزمتي SDK الرسميتين الآن خط حالي وخط صيانة. اختر الخط الصحيح قبل نسخ الكود، لأن الاستيرادات تختلف.
Python
TypeScript
الحالي (v2)
pip install "mcp[cli]"، الصنف MCPServer
npm install @modelcontextprotocol/server
الصيانة (v1.x)
pip install "mcp[cli]<2"، الصنف FastMCP
npm install @modelcontextprotocol/sdk zod
مستودع GitHub
modelcontextprotocol/python-sdk
modelcontextprotocol/typescript-sdk
لا يتلقى خط Python v1 سوى إصلاحات الأمان الآن، لذا ينبغي أن تبدأ المشاريع الجديدة من v2، وهذا ما تفعله أمثلة Python أدناه. إذا كنت تصون خادم Python قديمًا، فالترحيل يعني تغيير from mcp.server.fastmcp import FastMCP إلى from mcp.server.mcpserver import MCPServer وإعادة تسمية استدعاء المُنشئ. تبقى المُزخرِفات (Decorators) كما هي.
في TypeScript، تستخدم الأمثلة الكاملة حزمة v1.x التي تستوردها معظم الخوادم الحالية. ويلي ذلك نسخة قصيرة من الخادم نفسه على v2، لترى بالضبط ما الذي يتغير.
مثال Python باستخدام MCPServer
تقدّم Python أقصر طريق من لا شيء إلى خادم يعمل، لأن تلميحات الأنواع وسلاسل التوثيق (docstrings) تتحول إلى مخطط للأداة. لا حاجة إلى كتابة مخطط JSON Schema يدويًا.
ملف الخادم
ثبّت الحزمة باستخدام uv add "mcp[cli]" (أو pip install "mcp[cli]")، ثم احفظ هذا الملف باسم server.py:
from mcp.server.mcpserver import MCPServer
mcp = MCPServer("Demo")
@mcp.tool()
def add(a: int, b: int) -> int:
"""Add two numbers."""
return a + b
@mcp.resource("greeting://{name}")
def greeting(name: str) -> str:
"""Greet someone by name."""
return f"Hello, {name}!"
@mcp.prompt()
def review_code(code: str) -> str:
"""Ask for a code review that lists bugs first."""
return f"Please review this code and list bugs first:\n\n{code}"
if __name__ == "__main__":
mcp.run(transport="stdio")
تتحول الدالة add إلى أداة يُولَّد مخطط مدخلاتها من a: int, b: int. وتصبح سلسلة التوثيق (docstring) الوصف الذي يقرؤه النموذج ليقرر متى يستدعيها. greeting قالب مورد: العميل الذي يقرأ greeting://Ada يحصل على Hello, Ada!.
التشغيل والفحص
uv run mcp dev server.py # opens the MCP Inspector in your browser
uv run mcp run server.py # plain stdio, for a client to launch
uv run mcp run server.py --transport streamable-http # HTTP instead
استخدم Inspector أولًا. فهو يعرض كل أداة، ويوفّر نموذجًا لإدخال الوسائط، ويُظهر حركة JSON-RPC الخام، وهذا أسرع طريقة لاكتشاف مخطط معطوب. وبعد أن يعمل الخادم، سجّله لدى عميل. في Claude Code يتم ذلك بسطر واحد:
claude mcp add demo -- uv run mcp run server.py
مثال TypeScript باستخدام Zod
يتطلب TypeScript قدرًا أكبر من التفاصيل، لأنك تصف المدخلات باستخدام Zod بدلًا من تلميحات الأنواع. في المقابل تحصل على تحقق وقت التشغيل ووسائط مُنمَّطة في معالِج الطلب.
ملف الخادم على v1.x
ثبّت الحزمة باستخدام npm install @modelcontextprotocol/sdk zod واضبط "type": "module" في package.json. اكتب الخادم كدالة ليتمكن النقلان من إعادة استخدامها. احفظ هذا الملف باسم src/build-server.ts:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
export function buildServer(): McpServer {
const server = new McpServer({ name: "demo", version: "1.0.0" });
server.registerTool(
"add",
{
title: "Add numbers",
description: "Add two numbers",
inputSchema: { a: z.number(), b: z.number() },
},
async ({ a, b }) => ({
content: [{ type: "text", text: String(a + b) }],
})
);
return server;
}
ثم نقطة دخول من ثلاثة أسطر لتشغيل stdio، في src/stdio.ts:
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { buildServer } from "./build-server.js";
await buildServer().connect(new StdioServerTransport());
ترجم باستخدام tsc، ثم تحقق منه باستخدام npx @modelcontextprotocol/inspector node dist/stdio.js. تستخدم الموارد والأوامر الشكل نفسه عبر registerResource وregisterPrompt. ويتضمن مستودع v1.x أيضًا src/examples/server/simpleStreamableHttp.ts، وهو مثال غني بالميزات يضم أدوات وموارد وأوامر وتسجيلًا وOAuth اختياريًا، ويستحق القراءة مرة واحدة حين يتجاوز خادمك كونه مشروعًا تجريبيًا بسيطًا.
الخادم نفسه على v2
import { McpServer } from "@modelcontextprotocol/server";
import { StdioServerTransport } from "@modelcontextprotocol/server/stdio";
import * as z from "zod/v4";
const server = new McpServer({ name: "demo", version: "1.0.0" });
server.registerTool(
"add",
{
description: "Add two numbers",
inputSchema: z.object({ a: z.number(), b: z.number() }),
},
async ({ a, b }) => ({
content: [{ type: "text", text: String(a + b) }],
})
);
await server.connect(new StdioServerTransport());
ثلاثة أمور تتغير: اسم الحزمة، ومسار الاستيراد الفرعي /stdio، والمخطط الذي يصبح z.object(...) كاملًا من zod/v4 بدلًا من كائن عادي يضم الحقول. جسم المعالج متطابق. أما تشغيل الخادم عبر HTTP في v2 فيمر عبر حزم محوّلة صغيرة مثل @modelcontextprotocol/express، لذا اقرأ ملف README لتلك الحزمة قبل نقل خادم HTTP.
stdio أو Streamable HTTP
النقل هو القرار الوحيد الذي يغيّر طريقة النشر. كود الأدوات يبقى كما هو.
خادم stdio هو الخيار الافتراضي الصحيح لأي شيء يمس جهازك، مثل الملفات أو قاعدة بيانات محلية أو سكربت. يشغّله العميل، ويتواصل عبر stdin وstdout الخاصين بالخادم، ويوقفه عند انتهاء الجلسة. لا يوجد منفذ يلزم تأمينه.
خادم بعيد عبر HTTP
يتيح Streamable HTTP لخادم واحد يعمل أن يجيب عددًا كبيرًا من العملاء. يبني هذا الإصدار عديم الحالة المبني على Express خادمًا جديدًا لكل طلب، وبذلك يتجنب حالة الجلسة المشتركة. احفظه باسم src/http.ts:
import express from "express";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import { buildServer } from "./build-server.js";
const app = express();
app.use(express.json());
app.post("/mcp", async (req, res) => {
const server = buildServer();
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined,
});
res.on("close", () => {
transport.close();
server.close();
});
await server.connect(transport);
await transport.handleRequest(req, res, req.body);
});
app.listen(3000, "127.0.0.1");
سجّله لدى claude mcp add --transport http demo http://127.0.0.1:3000/mcp. في Python يكون المكافئ سطرًا واحدًا: mcp.run(transport="streamable-http", host="127.0.0.1", port=9000). اربط الخادم بعنوان localhost ما لم يكن هناك شيء أمامه يتولى المصادقة، لأن منفذ MCP المفتوح باب مفتوح على كل أداة سجّلتها.
مثال: أدوات تولّد الصور
تصبح الأدوات أكثر إثارةً للاهتمام حين لا تكون النتيجة رقمًا. يصلح توليد الصور والفيديو مثالًا تعليميًا جيدًا لأنهما بطيئان وغير متزامنين، ويُرجعان رابطًا بدلًا من النص. تستدعي العينات أدناه PicassoIA API، الذي يتبع نمطًا شبيهًا بنمط Replicate: أنشئ تنبؤًا، ثم استطلِعه، ثم اقرأ المخرجات.
عنوان الأساس والمصادقة:https://api.picassoia.com/v1 مع الترويسة Authorization: Bearer pia_sk_...
الإنشاء:POST /models/{owner}/{name}/predictions مع الجسم {"input": {...}}
الاستطلاع:GET /predictions/{id} حتى تصبح الحالة succeeded أو failed أو canceled
التوقيت: تتضمن استجابة الإنشاء eta.next_poll_in_seconds، وهي فترة استطلاع تستحق الاحترام
أداة Python مع الاستطلاع
import asyncio
import os
import httpx
from mcp.server.mcpserver import MCPServer
API = "https://api.picassoia.com/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['PICASSOIA_API_TOKEN']}"}
mcp = MCPServer("picassoia-media")
slots = asyncio.Semaphore(5) # the account allows 5 concurrent predictions
async def run_prediction(model: str, payload: dict, timeout_s: int = 600) -> list[str]:
async with slots, httpx.AsyncClient(headers=HEADERS, timeout=30) as http:
created = await http.post(
f"{API}/models/{model}/predictions", json={"input": payload}
)
created.raise_for_status()
prediction = created.json()
waited = 0
while prediction["status"] in ("starting", "processing"):
if waited >= timeout_s:
raise TimeoutError(f"Prediction {prediction['id']} is still running")
delay = (prediction.get("eta") or {}).get("next_poll_in_seconds", 3)
await asyncio.sleep(delay)
waited += delay
polled = await http.get(f"{API}/predictions/{prediction['id']}")
polled.raise_for_status()
prediction = polled.json()
if prediction["status"] != "succeeded":
raise RuntimeError(f"Prediction {prediction['status']}: {prediction.get('error')}")
output = prediction["output"]
return output if isinstance(output, list) else [output]
@mcp.tool()
async def generate_image(prompt: str, aspect_ratio: str = "16:9") -> str:
"""Generate one image from a text prompt and return its URL."""
urls = await run_prediction(
"picassoia/picassoia-image",
{"prompt": prompt, "aspect_ratio": aspect_ratio, "num_outputs": 1},
)
return urls[0]
if __name__ == "__main__":
mcp.run(transport="stdio")
هناك تفصيلان مهمان هنا. تستخدم الحلقة asyncio.sleep، فيبقى الخادم مستجيبًا للطلبات الأخرى أثناء تشغيل المهمة، وتتبع الفترة التي يقترحها API بدلًا من إرسال الطلبات بوتيرة متسارعة. يُبقيك semaphore ضمن حد التزامن المسموح به للحساب. يقبل نموذج PicassoIA Imageprompt، aspect_ratio، seed، num_outputs (1 أو 2)، output_format، و output_quality. أما التعديلات، فوجّه الدالة المساعدة نفسها إلى PicassoIA Image Editor Pro.
أداة TypeScript للفيديو
يعمل النمط نفسه في TypeScript. تضيف هذه النسخة أداة فيديو فوق الدالة buildServer التي رأيناها سابقًا:
const API = "https://api.picassoia.com/v1";
const headers = {
Authorization: `Bearer ${process.env.PICASSOIA_API_TOKEN}`,
"Content-Type": "application/json",
};
async function runPrediction(model: string, input: Record<string, unknown>) {
const created = await fetch(`${API}/models/${model}/predictions`, {
method: "POST",
headers,
body: JSON.stringify({ input }),
});
if (!created.ok) throw new Error(`Create failed: ${created.status}`);
let prediction = await created.json();
while (["starting", "processing"].includes(prediction.status)) {
const delay = prediction.eta?.next_poll_in_seconds ?? 5;
await new Promise((resolve) => setTimeout(resolve, delay * 1000));
const polled = await fetch(`${API}/predictions/${prediction.id}`, { headers });
prediction = await polled.json();
}
if (prediction.status !== "succeeded") {
throw new Error(`Prediction ${prediction.status}`);
}
return [prediction.output].flat() as string[];
}
server.registerTool(
"generate_video",
{
title: "Generate video",
description: "Make a short clip with synchronized audio from a text prompt",
inputSchema: {
prompt: z.string().max(4000),
duration: z.union([z.literal(5), z.literal(10)]).default(5),
resolution: z.enum(["480p", "720p"]).default("720p"),
},
},
async ({ prompt, duration, resolution }) => {
const [url] = await runPrediction("picassoia/seedance-2.5-lite", {
prompt,
duration,
resolution,
});
return { content: [{ type: "text", text: url }] };
}
);
الفيديو أبطأ. تسرد صفحة النموذج Seedance 2.5 Lite أمثلة تشغيل تتراوح تقريبًا بين 100 و190 ثانية، وهي مدة طويلة تجعل بعض العملاء يتخلّون عن استدعاء الأداة الواحدة. التصميم الأكثر أمانًا يقسّم العمل إلى جزأين: start_video تعيد التنبؤ id فورًا، وcheck_video تأخذ هذا المعرّف وتعيد إما الحالة أو الرابط النهائي.
هكذا يعمل موصّل PicassoIA لموقع claude.ai. تعيد أدوات التوليد فيه predict_id ومدة انتظار مقترحة، ويُستدعى get_generation حتى تنجح المهمة أو تفشل. ويقبل النموذج نفسه أيضًا image اختياريًا كأول إطار، وseed، وaspect_ratio، وعلمًا save_audio للمقاطع الصامتة.
💡 نصيحة: اجعل نتيجة الأداة صغيرة. أعد عنوان URL وملخصًا من سطر واحد، لا الملف نفسه. يحتاج المساعد إلى رابط فقط ليعرضه أو يمرره.
كيفية استخدام PicassoIA مع MCP
هناك طريقتان للوصول إلى PicassoIA من مساعد ذكي: الموصّل الجاهز، أو خادمك الخاص الملفوف حول API، كما في العينات أعلاه.
الإعداد خطوة بخطوة
اختر المسار. بالنسبة إلى عميل المحادثة، أضِف موصّل PicassoIA في إعدادات claude.ai. يكشف الموصّل عن generate_image، و edit_image، و generate_video_picassoia، و generate_video_seedance، و get_generation، و cancel_generation، و list_models، و get_account، و list_generations. أما شيفرة الوكيل الخاصة بك فاستخدم مسار API.
أنشئ توكن API. سجّل الدخول إلى صفحة PicassoIA API وأنشئ واحدًا. يبدأ بالنص pia_sk_، ويحتفظ الحساب بحد أقصى توكنين منها. راجع صفحة الأسعار لمعرفة الخطة التي تتضمن الوصول إلى API قبل أن تبني عليه.
خزّنه في متغيرات البيئة. شغّل export PICASSOIA_API_TOKEN=pia_sk_... في الطرفية. أبقِه خارج ملفات المصدر وخارج أي إعداد للعميل تُرفعه إلى المستودع.
اختر نموذجًا من الجدول أدناه.
غلّف الأداة واختبرها. الصق أداة من العينات، وشغّلها في Inspector بأمر نصي بسيط، ثم سجّلها لدى العميل.
تحويل النص أو الصورة إلى فيديو مع صوت متزامن، مدة 5 أو 10 ثوانٍ
أي نموذج يقرر متى تُستدعى أدواتك؟ أي نموذج لغوي كبير يدعم استدعاء الأدوات يستطيع ذلك. يسرد كتالوج النماذج اللغوية الكبيرة في PicassoIA كلًا من Claude Sonnet 5 وGPT 5.6 Sol وKimi K2.6 وGemini 3.5 Flash، إلى جانب نماذج كثيرة أخرى. جرّب أكثر من نموذج على الخادم نفسه، وقارن مدى ثبات كل منها في اختيار الأداة الصحيحة.
حدود تستحق المعرفة
5 تنبؤات متزامنة لكل حساب، مشتركة بين التوكنات واتصالات MCP
10 MB حدًا أقصى لجسم الطلب
4,000 حرف حدًا أقصى لكل أمر نصي
3 ساعات قبل انتهاء مهلة التنبؤ
2 توكنات API لكل حساب
أخطاء تُعطّل خوادم MCP
تعود معظم التشغيلات الأولى الفاشلة إلى واحدة من ثلاث مشكلات. وكل واحدة منها سهلة التجنب بمجرد أن تعرف أين تبحث.
تسجيل الرسائل على stdout
مع stdio تكون المخرجات القياسية هي قناة البروتوكول. أي print() أو console.log() عابر يُفسد تدفق JSON-RPC، ويُبلغ العميل عن خطأ تحليل أو عن خادم لم يتصل أبدًا. أرسل السجلات إلى الخطأ القياسي بدلًا من ذلك:
في TypeScript، استخدم console.error("polling prediction").
الاستدعاءات المانعة والأسماء الغامضة
يؤدي time.sleep(30) داخل أداة غير متزامنة إلى تجميد كل الطلبات الأخرى على الخادم نفسه. استخدم await asyncio.sleep في Python ومؤقتًا بانتظار (await) في TypeScript، كما تفعل الأمثلة. وللأسماء الأهمية نفسها: أداة اسمها do_task لا تمنح النموذج ما يختار به، بينما generate_image مع docstring واضح تخبره بدقة متى يلجأ إليها.
الأسرار والحمولات الكبيرة
اقرأ التوكنات من متغيرات البيئة، لا من ملفات المصدر أبدًا، ولا تُكرّرها في نتيجة أي أداة. أما المخرجات، فأعِد روابط بدلًا من base64. قد تبلغ صورة واحدة بصيغة base64 عدة ميغابايت، وتُغرق نافذة السياق الخاصة بالنموذج، كما أن PicassoIA API يحدّ جسم الطلبات عند 10 MB على أي حال.
جرّبه على PicassoIA اليوم
انسخ server.py أو زوج TypeScript، وشغّله في Inspector، وسيكون لديك خادم MCP يعمل في أقل من عشر دقائق. ثم أعطه مهمة بصرية. تُعد الخوادم المرجعية الرسمية على GitHub مكانًا جيدًا لقراءة المزيد من الأنماط، ويتضمن مستودعا SDK مجلد أمثلة.