اربط أنظمة شركتك الداخلية بـ Claude Code عبر MCP — من خادم fetch عام إلى كتابة MCP Server مخصص، يغطي منصات النشر والمراقبة وأنظمة التذاكر ومبادئ تصميم الأدوات.
في المقال السابق تحدثنا عن ربط قواعد البيانات. قواعد البيانات لديها بروتوكولات موحدة، مما يجعل عملية الربط مباشرة نسبياً. لكن معظم الفرق تعتمد في عملها اليومي على مجموعة من الأنظمة الداخلية: منصات النشر، لوحات المراقبة، أنظمة التذاكر، واجهات API الداخلية، ومراكز الإعدادات.
هذه الأنظمة عادةً لا تملك خادم MCP جاهزاً، لكنها تقدم في الغالب واجهات HTTP API. يشرح هذا المقال كيفية استخدام MCP لربط هذه الأدوات الداخلية بـ Claude Code، ليتمكن من الاستعلام عن المراقبة، وعرض حالة النشر، والتعامل مع التذاكر مباشرةً.
هناك طريقتان لربط الأدوات الداخلية:
الطريقة الأولى: استخدام خادم MCP عام لطلبات HTTP
هناك خوادم MCP عامة في المجتمع تستطيع تحويل أي واجهة REST API إلى أداة MCP. تكتب ملف وصف للـ API، ويحوّله الخادم إلى أدوات يستطيع Claude استدعاءها. مناسب للسيناريوهات ذات البنية البسيطة التي لا تحتاج منطقاً معقداً.
الطريقة الثانية: كتابة خادم MCP مخصص
باستخدام MCP SDK بلغة TypeScript أو Python، تكتب خادماً مخصصاً يمنحك تحكماً كاملاً في تعريف الأدوات، والتحقق من المعاملات، ومعالجة الأخطاء. مناسب للسيناريوهات التي تحتاج دمج عدة واجهات API، أو تحويل البيانات، أو إضافة منطق أعمال.
سنشرح كلتا الطريقتين في هذا المقال، بدءاً من الأبسط.
أخف حل هو استخدام خادم @anthropic-ai/mcp-server-fetch الرسمي، الذي يتيح لـ Claude إرسال طلبات HTTP مباشرةً. الإعداد بسيط للغاية:
{
"mcpServers": {
"fetch": {
"command": "npx",
"args": ["-y", "@anthropic-ai/mcp-server-fetch"]
}
}
}
بعد الإعداد، يستطيع Claude استدعاء واجهات API الداخلية مباشرةً:
ساعدني في التحقق من الحالة الحالية لمنصة النشر https://deploy.internal.com/api/v1/services/user-service
سيرسل Claude طلب GET، ويحصل على الاستجابة، ويعرضها لك.
لكن لهذه الطريقة قيود واضحة:
مناسبة للاستخدام المؤقت، لكنها ليست حلاً طويل المدى.
عندما تحتاج لاستخدام نظام داخلي بشكل متكرر، فإن كتابة خادم MCP مخصص هي الخيار الأفضل. سنوضح ذلك بسيناريو حقيقي: ربط منصة النشر الخاصة بالشركة.
لنفترض أن منصة النشر توفر واجهات API التالية:
GET /api/v1/services — عرض جميع الخدماتGET /api/v1/services/:name/status — عرض حالة خدمة معينةPOST /api/v1/services/:name/deploy — تشغيل عملية نشرGET /api/v1/services/:name/logs — عرض سجلات النشر الأخيرةأولاً، نهيّئ المشروع:
mkdir mcp-deploy && cd mcp-deploy
npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node
npx tsc --init
الكود الأساسي src/index.ts:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const API_BASE = process.env.DEPLOY_API_URL!;
const API_TOKEN = process.env.DEPLOY_API_TOKEN!;
async function api(path: string, method = "GET", body?: unknown) {
const res = await fetch(`${API_BASE}${path}`, {
method,
headers: {
Authorization: `Bearer ${API_TOKEN}`,
"Content-Type": "application/json",
},
body: body ? JSON.stringify(body) : undefined,
});
if (!res.ok) {
throw new Error(`API error: ${res.status} ${await res.text()}`);
}
return res.json();
}
const server = new McpServer({
name: "deploy-platform",
version: "1.0.0",
});
// عرض جميع الخدمات
server.tool("list_services", "列出部署平台上的所有服务及其状态", {}, async () => {
const data = await api("/api/v1/services");
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
});
// عرض حالة خدمة معينة
server.tool(
"service_status",
"查看指定服务的当前部署状态、版本号和健康检查结果",
{ name: z.string().describe("服务名称,如 user-service") },
async ({ name }) => {
const data = await api(`/api/v1/services/${name}/status`);
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
}
);
// عرض سجلات النشر
server.tool(
"deploy_logs",
"查看指定服务最近的部署日志",
{
name: z.string().describe("服务名称"),
limit: z.number().optional().default(10).describe("返回条数,默认 10"),
},
async ({ name, limit }) => {
const data = await api(`/api/v1/services/${name}/logs?limit=${limit}`);
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
}
);
// تشغيل عملية النشر
server.tool(
"trigger_deploy",
"触发指定服务的部署。这是一个写操作,会实际影响生产环境",
{
name: z.string().describe("服务名称"),
version: z.string().describe("要部署的版本号或 git ref"),
},
async ({ name, version }) => {
const data = await api(`/api/v1/services/${name}/deploy`, "POST", {
version,
});
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
}
);
const transport = new StdioServerTransport();
server.connect(transport);
الترجمة والتشغيل:
npx tsc
{
"mcpServers": {
"deploy": {
"command": "node",
"args": ["/path/to/mcp-deploy/dist/index.js"],
"env": {
"DEPLOY_API_URL": "https://deploy.internal.com",
"DEPLOY_API_TOKEN": "your-api-token"
}
}
}
}
ضع الرمز المميز (token) في .claude/settings.local.json (لا يُرفع إلى git)، وعنوان URL في .claude/settings.json (يُرفع إلى git ويُشارك مع الفريق).
بعد الإعداد، تصبح المحادثة طبيعية:
ما حالة user-service الآن؟
→ Claude يستدعي service_status("user-service")
→ النتيجة: قيد التشغيل، الإصدار v2.3.1، آخر نشر منذ ساعتين، فحوصات السلامة ناجحة بالكامل
هل فشلت أي من عمليات النشر الأخيرة؟
→ Claude يستدعي deploy_logs("user-service", 20)
→ يحلل السجلات ويخبرك أن النشر الثالث تم التراجع عنه بسبب انتهاء مهلة فحص السلامة
انشر user-service إلى الإصدار v2.3.2
→ Claude يستدعي trigger_deploy("user-service", "v2.3.2")
→ لأن وصف الأداة يشير إلى "عملية كتابة"، سيطلب Claude تأكيدك أولاً
هذه مسألة تستحق التفكير الجاد.
عمليات القراءة يمكن ربطها بأمان. التحقق من الحالة، وعرض السجلات، والبحث في التذاكر — هذه عمليات بلا آثار جانبية، وحتى لو أخطأ Claude فلا ضرر.
عمليات الكتابة تنقسم إلى حالتين:
عمليات الكتابة منخفضة المخاطر يمكن ربطها، لكن مع التوضيح في وصف الأداة. Claude يطلب تأكيد المستخدم تلقائياً للعمليات المشار إلى آثارها الجانبية. مثل إنشاء تذكرة، أو إرسال رسالة، أو تحديث إعدادات.
عمليات الكتابة عالية المخاطر يُنصح بعدم ربطها. حذف الموارد، وتشغيل التراجع، وتعديل الصلاحيات — هذه عمليات ذات عواقب خطيرة وغير قابلة للتراجع، من الأسلم إبقاؤها يدوية.
إذا كان لا بد من ربط عمليات الكتابة، فافعل شيئين على الأقل:
| النظام | الأدوات المكشوفة | ملاحظات |
|---|---|---|
| منصة النشر (K8s / Kamal) | عرض حالة الخدمات، عرض السجلات، تشغيل النشر | إضافة تأكيد لعمليات الكتابة |
| نظام المراقبة (Grafana / Datadog) | عرض المقاييس، عرض سجل التنبيهات | التحكم في النطاق الزمني للاستعلامات لتجنب سحب بيانات كثيرة |
| نظام التذاكر (Jira / Linear) | البحث في التذاكر، إنشاء تذاكر، تحديث الحالة | إنشاء التذاكر عملية كتابة لكنها منخفضة المخاطر |
| التوثيق الداخلي (Notion / Confluence) | البحث في المستندات، قراءة محتوى الصفحات | انتبه للتصفح المقسّم، لا تسحب الكثير دفعة واحدة |
| مركز الإعدادات (Consul / etcd) | قراءة الإعدادات، مقارنة الفروقات بين البيئات | اقتصر على القراءة فقط، لا تربط عمليات الكتابة |
| CI/CD (GitHub Actions / Jenkins) | عرض حالة البناء، تشغيل البناء | تشغيل البناء يُعد عملية كتابة متوسطة المخاطر |
كتابة أدوات MCP تختلف عن كتابة واجهات API. الـ API مصمم للمبرمجين، بينما الأدوات مصممة للذكاء الاصطناعي. إليك بعض المبادئ المهمة:
اسم الأداة يجب أن يكون واضحاً
✗ get_svc_stat — قد لا يستطيع Claude تخمين معنى الاختصار
✓ service_status — واضح من النظرة الأولى
الوصف يجب أن يُكتب للذكاء الاصطناعي
وصف الأداة ليس توثيقاً للبشر، بل هو الأساس الذي يعتمد عليه Claude لتحديد «متى يجب استدعاء هذه الأداة». يجب أن يوضح الوصف: ماذا تفعل الأداة، وماذا تُرجع، ومتى يجب استخدامها.
✗ "الحصول على حالة الخدمة"
✓ "عرض حالة النشر الحالية والإصدار ونتائج فحص السلامة لخدمة معينة. يُستخدم عندما يسأل المستخدم عما إذا كانت خدمة معينة تعمل بشكل طبيعي"
عرّف المعاملات بوضوح باستخدام zod
المعاملات المزودة بـ .describe() يعرف Claude ما يجب ملؤه فيها. بدون وصف، لا يملك Claude سوى التخمين بناءً على الاسم.
أرجع بيانات منظمة
أدوات MCP تُرجع نصاً، لكن حاول إرجاع JSON منسّق. Claude يتعامل مع البيانات المنظمة بدقة أعلى بكثير من النص العادي.
حافظ على حجم مناسب للأداة
لا تحشر عملية معقدة في أداة واحدة. ولا تقسّم استعلاماً بسيطاً إلى ثلاث أدوات. المبدأ هو: أداة واحدة تنجز عملية واحدة مستقلة وذات معنى.
يمكن وضع كود خادم MCP في عدة أماكن:
داخل مستودع المشروع (الخيار الموصى به للبداية)
your-project/
├── .claude/settings.json
├── mcp-servers/
│ └── deploy/
│ ├── src/index.ts
│ ├── package.json
│ └── tsconfig.json
└── ...
الميزة أن الكود والإعدادات معاً، وأي شخص في الفريق يستنسخ المشروع ويثبّت الاعتماديات يستطيع البدء فوراً.
مستودع مستقل
عندما يحتاج خادم MCP للاستخدام عبر عدة مشاريع، ضعه في مستودع مستقل وانشره كحزمة npm أو صورة Docker.
{
"mcpServers": {
"deploy": {
"command": "npx",
"args": ["-y", "@yourcompany/mcp-deploy-server"]
}
}
}
تثبيت على مستوى النظام
لخوادم MCP العامة على مستوى الشركة (مثل الربط بنظام المصادقة الموحد أو منصة السجلات الموحدة)، ثبّتها على مستوى النظام وأضف الإعدادات في ~/.claude/settings.json.
أكثر المشاكل شيوعاً أثناء تطوير خادم MCP هي «Claude لم يستدعِ أداتي» أو «استدعاها لكن ظهر خطأ».
تأكد أن الخادم يعمل
بعد إعادة تشغيل Claude Code، أدخل /mcp لعرض قائمة خوادم MCP المتصلة. إذا لم يكن خادمك في القائمة، تحقق من صحة command و args.
اختبر الخادم بشكل مستقل
خادم MCP يتواصل عبر stdio، ويمكن اختباره مباشرةً في الطرفية:
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | node dist/index.js
إذا أرجع قائمة الأدوات، فالخادم نفسه يعمل بشكل صحيح.
راجع سجلات استدعاءات Claude للأدوات
يعرض Claude Code مدخلات ومخرجات كل استدعاء أداة. إذا تم تمرير معاملات خاطئة، فعادةً ما يكون السبب أن وصف الأداة أو تعريف المعاملات غير واضح بما يكفي وفهمه Claude بشكل خاطئ.
لنربط كل ما سبق بمثال حقيقي. لنفترض أنك تريد ربط Sentry بـ Claude Code ليتمكن من الاستعلام عن أخطاء الإنتاج مباشرةً.
server.tool(
"search_errors",
"在 Sentry 中搜索最近的错误。用于排查线上问题、查看错误趋势",
{
query: z.string().describe("搜索关键词,如错误信息、函数名"),
hours: z.number().optional().default(24).describe("查看最近多少小时的错误"),
},
async ({ query, hours }) => {
const since = new Date(Date.now() - hours * 3600000).toISOString();
const data = await api(
`/api/0/projects/${ORG}/${PROJECT}/issues/?query=${encodeURIComponent(query)}&start=${since}&sort=date`
);
const summary = data.map((issue: any) => ({
title: issue.title,
count: issue.count,
firstSeen: issue.firstSeen,
lastSeen: issue.lastSeen,
link: issue.permalink,
}));
return {
content: [{ type: "text", text: JSON.stringify(summary, null, 2) }],
};
}
);
server.tool(
"error_details",
"查看 Sentry 中某个错误的详细信息,包括堆栈和最近一次事件",
{ issueId: z.string().describe("Sentry issue ID") },
async ({ issueId }) => {
const [issue, latest] = await Promise.all([
api(`/api/0/issues/${issueId}/`),
api(`/api/0/issues/${issueId}/events/latest/`),
]);
return {
content: [
{
type: "text",
text: JSON.stringify(
{
title: issue.title,
count: issue.count,
users: issue.userCount,
stacktrace: latest.entries?.find(
(e: any) => e.type === "exception"
),
},
null,
2
),
},
],
};
}
);
بعد الربط، تصبح محادثة تتبع المشاكل في الإنتاج كالتالي:
هل هناك أخطاء 500 جديدة في آخر 4 ساعات؟
→ Claude يبحث في Sentry
→ يكتشف 3 مشاكل جديدة، أخطرها أثّرت على 120 مستخدماً
→ يسحب تتبع المكدس تلقائياً ويحدد أنها استثناء مؤشر فارغ
→ يجد الموقع المقابل في الكود ويقدم حلاً للإصلاح
من اكتشاف المشكلة إلى تحديد موقعها في الكود، تتم العملية بأكملها في محادثة واحدة.
شرح هذا المقال كيفية ربط الأدوات الداخلية عبر MCP. الفكرة الأساسية هي: النظام الداخلي لديه HTTP API ← تكتب خادم MCP يغلّفه ← يستطيع Claude استخدامه مباشرةً.
جميع الأمثلة في هذا المقال تغلّف واجهات API موجودة — منصة النشر و Sentry لديهما واجهات أصلاً، وخادم MCP يقوم فقط بالتمرير والتكييف. المقال التالي سيتناول سيناريو مختلفاً: عندما لا توجد واجهة API جاهزة للقدرة التي تحتاجها، كيف تبني خادم MCP من الصفر، وتنفذ المنطق بنفسك، وتدير الحالة، وتتعامل مع تفاعلات متعددة الخطوات معقدة.