@lousho/build-ai-agent/mcp، وحزمة
@modelcontextprotocol/sdk اعتمادية نظيرة (peer dependency) اختيارية: ثبّتها لاستخدام أي من الاتجاهين.
connectMcp وloadMcpTools وserveMcp مُصدَّرة من
@lousho/build-ai-agent/mcp؛ وتصدّرها كذلك جذر الحزمة و@lousho/build-ai-agent/tools.
أما createAgent({ mcpServers }) فلا يحتاج إلى أي استيراد من المسار الفرعي.
استخدام خوادم MCP في وكيل
يقبلcreateAgent({ mcpServers }) الخريطة نفسها. تتصل الخوادم عند await agent.ready()
أو تلقائيًا عند أول send() / stream()؛
وتُضاف أدوات كل خادم باسم <server>__<tool> (مثل docs__search).
الخادم الذي يتعذر اتصاله يُفشل ذلك الاستدعاء، ويحاول الاستدعاء التالي مرة أخرى.
يقطع agent.close() الاتصال بها (ويوقف عمليات stdio)؛ وأي استدعاء أداة لاحق
يعيد الاتصال. وبلا mcpServers لا تفعل ready() ولا close() شيئًا. تختار بادئة النموذج vendor/ المزوّد؛ ومع OpenRouter استخدم openrouter/<vendor>/<model> (مثل openrouter/openai/gpt-4o-mini).
command وargs؛ وتُضاف env إلى
البيئة الافتراضية (PATH وما شابهه)، فهي ليست بديلًا عنها. أما مدخلات HTTP
فتستخدم نقل HTTP القابل للبث (streamable HTTP) وترسل الترويسات الثابتة headers مع كل
طلب. ويمكن لمدخل HTTP أيضًا تسجيل الدخول بـ OAuth، كما تصفه مواصفات تفويض MCP: أضف oauth: { redirectUri } فيسجّل مشغّلٌ دخول الوكيل مرة واحدة؛ انظر خوادم MCP مع OAuth.
الخريطة هي نفسها التي يعلنها ملف المواصفات؛ انظر
الإعدادات (قسم mcpServers) لصيغة YAML.
مشاركة الخوادم عبر connectMcp()
لمشاركة الخوادم بين عدة وكلاء، أو لاختيار طريقة معالجة الإخفاقات، استدعِ
connectMcp(servers, options?) ومرّر tools الناتجة عنها بنفسك. تتصل بكل
خادم وتسرد أدواته قبل أن تكتمل، فتكون الأدوات معروفة سلفًا.
الخيارات:
onError: القيمة'throw'(الافتراضية) ترفض الوعد حين يتعذر اتصال خادم، بعد إغلاق الخوادم الأخرى؛ والقيمة'skip'تستبعد ذلك الخادم وتحذّر عبرlogger.lazy(الافتراضيةtrue): بعدclose()أو انقطاع الاتصال، يعيد استدعاء الأداة التالي الاتصال. أما معfalseفيفشل ذلك الاستدعاء. وسرد الأدوات يحتاج إلى اتصال، فلا تؤخّرlazyأول اتصال أبدًا.logger: يتلقى تحذيرات الخوادم والأدوات المتخطّاة (الافتراضي: لا شيء).tokens: مخزن الرموز (AgentStore.tokens) للخوادم التي لهاoauth؛ وهو مطلوب حين يكون لأي خادمoauth.
{ tools, close(), status() }؛ وتربط status() كل خادم بإحدى القيم
'idle' أو 'connected' أو 'failed' أو 'needs-auth' (خادم له oauth لم يسجّل التطبيق دخوله إليه).
tools هي Record<string, ToolDescriptor> مفتاحها <server>__<tool> —
خريطة، لا المصفوفة التي تصنعها نتائج defineTool(). وتقبل
createAgent({ tools }) الصورتين معًا وتجمعهما: tools: [myTool, mcp.tools]
تسجّل عناصر المصفوفة بأسمائها وعناصر الخريطة بمفاتيحها، فتجتمع أدواتك
الخاصة وأدوات MCP في قائمة واحدة. (والنشر في خريطة واحدة،
tools: { ...mcp.tools, my_tool: myTool }، يعمل أيضًا.)
mcp.tools خريطة، وtools تقبل المصفوفة أيضًا، فتختلط الصورتان:
tools: [mcp.tools, weatherTool] أو
tools: [...createFsTools(workspace), ...Object.values(mcp.tools)] (يحمل كل
واصف MCP اسم <server>__<tool> الخاص به، فيعمل Object.values أيضًا).
الموافقة على أدوات MCP
تصف خوادم MCP كل أداة بتوصيفات (annotations):readOnlyHint و
destructiveHint وidempotentHint وopenWorldHint وtitle. وهي
تلميحات، لكن SDK تستخدمها قاعدةً افتراضية لـالموافقات:
الأداة ذات readOnlyHint: true تعمل؛ والأداة ذات destructiveHint: true، أو
التي لا ترسل destructiveHint (الافتراضي في مواصفة MCP أنها مدمِّرة)،
توقف التشغيل حتى يوافق إنسان؛ وذات destructiveHint: false تعمل. وعليه فالأداة
بلا توصيفات تطلب الموافقة. تبقى التوصيفات الخام في
descriptor.metadata.mcp.annotations، ويصير title هو displayName.
اضبط approval في مدخل الخادم (mcpServers أو createAgent أو connectMcp()) أو
في loadMcpTools(client, name, { approval }):
'annotations'(الافتراضية): كما سبق.'always'/'never': اطلب الموافقة لكل الأدوات / لا تطلبها لأي منها.- دالة
({ name, annotations }) => booleanتقرر لكل أداة (nameهو اسم الأداة المجرّد؛ وannotationsهي{}حين لا يرسل الخادم شيئًا). في الشيفرة فقط؛ أما ملف المواصفات فيقبل السلاسل الثلاث.
allow أو deny أو ask.
أدوات من عميل اتصلتَ به بنفسك
اتصل بنفسك بـClient من @modelcontextprotocol/sdk ثم حمّل
أدواته بـloadMcpTools()، ومرّر النتيجة إلى createAgent() (أو
سجّلها في ToolRegistry):
loadMcpTools أيضًا من جذر الحزمة ومن
@lousho/build-ai-agent/tools.
تقديم وكيل عبر MCP
تعكسserveMcp() عمل loadMcpTools(): فهي تعرض وكيلًا (واختياريًا بعض
أدواته) على أنه خادم MCP، فيستطيع Claude Code وCursor وغيرهما من عملاء MCP
استدعاءه.
- بلا حالة. كل استدعاء لأداة الوكيل محادثة جديدة.
- الإلغاء. إلغاء طلب MCP يُجهض تشغيل الوكيل
(
agent.send(message, { signal })). - الأخطاء. يعود إخفاق الوكيل نتيجةَ MCP فيها
isError: true. - الموافقات. لا يمكن الموافقة على الأدوات المشروطة بموافقة عبر MCP. التشغيل الذي
يتوقف للموافقة يعيد
isError: trueمع رسالة تقول ذلك. والأدوات المعلَّمة بـneedsApprovalلا تُعرض مباشرة ما لم تمرّرallowApprovalTools: true؛ وإن فعلت، شغّلها العملاء دون أي بوابة بشرية. - stdio. لا يُكتب إلى stdout غير بروتوكول MCP؛ وتذهب التحذيرات إلى stderr.
- HTTP. يرتبط افتراضيًا بالعنوان
127.0.0.1. أضفauth: { type: 'bearer', token }لاشتراط ترويسةAuthorization: Bearer؛ والارتباط بمضيف غير loopback دونauthيسجّل تحذيرًا.
التوصيفات
تحملtools/list توصيفات MCP ليعرف العملاء ما تفعله الأداة. الأداة
التي تحتاج إلى موافقة تُعلَن بـreadOnlyHint: false, destructiveHint: true؛
وحدّد التلميحات بنفسك عبر annotations (تُرسَل كما هي). والأداة التي بلا تلميحات لا ترسل
شيئًا، فيظل وكيل Lousho الذي يستهلك هذا الخادم يسأل قبل تشغيلها (انظر
approval في connectMcp())؛ أما readOnlyHint: true فتعمل دون سؤال. والأداة التي
تحتاج إلى موافقة لا تُعلَن أبدًا للقراءة فقط. الأدوات المضمّنة read_file وlist_dir و
glob وgrep وtodo_read وcurrent_date وday_name للقراءة فقط.
من سطر الأوامر
يقدّمlousho mcp ملف مواصفات وكيل (عبر stdio افتراضيًا):
.mcp.json في Claude Code):
الحدود
- لا يمكن الموافقة على الأدوات المشروطة بموافقة عبر MCP حين تقدّم وكيلًا: التشغيل الذي يتوقف للموافقة يعيد
isError: true، والأدوات المعلَّمة بـneedsApprovalلا تُعرض ما لم تُضبطallowApprovalTools. - يرسل عملاء HTTP الترويسات الثابتة
headersمع كل طلب. أما OAuth (oauth) فيملكه التطبيق: منحة واحدة لكل خادم للوكيل كله، يسجّل دخولها مشغّل؛ ولا توجد منح MCP لكل مستخدم بعد. - استدعاءات
serveMcp()بلا حالة: كل استدعاء لأداة الوكيل محادثة جديدة، ولا يقبل نقل HTTP غيرPOST.