يتعلم وكلاء الذكاء الاصطناعي استخدام الويب، ومعظمهم يفعل ذلك بالطريقة الصعبة: تحليل شجرة DOM لموقعك، وتخمين الزر الذي يجب النقر عليه، والتعطل في كل مرة تغيّر فيها فئة CSS. يقلب WebMCP هذا النموذج رأساً على عقب. فبدلاً من إجبار الوكلاء على كشط صفحاتك، يقوم تطبيق الويب الخاص بك بالإعلان عن أدوات منظمة — ابحث في هذا الكتالوج، اجلب صفحة التفاصيل تلك، أرسل هذا النموذج — ويمكن لأي وكيل متوافق مع بروتوكول MCP يعمل في المتصفح استدعاءها مباشرة، بمدخلات ومخرجات منمّطة.
WebMCP هو مقترح يُطوَّر داخل مجموعة W3C Web Machine Learning Community Group، ويدعمه مهندسون من مايكروسوفت وجوجل. يعرّف المقترح واجهة برمجية في المتصفح (navigator.modelContext) تسجّل الصفحة من خلالها أدواتها. وبينما تعمل المتصفحات على شحن الدعم الأصلي خلف أعلام تجريبية، يوفّر مشروع @mcp-b بديلاً جاهزاً للإنتاج (polyfill) وروابط React يمكنك استخدامها اليوم.
في هذا الدرس ستضيف WebMCP إلى مشروع Next.js يعتمد App Router: تسجيل أدوات بحث قرائية فقط، ونشر موارد منظمة، وبناء أداة لالتقاط العملاء المحتملين مع خطوة تأكيد بشري. هذه هي البنية نفسها العاملة في الإنتاج على noqta.tn، حيث تتيح 8 أدوات WebMCP لوكلاء الذكاء الاصطناعي البحث في خدماتنا وتصفح المحتوى وطلب عروض الأسعار.
المتطلبات الأساسية
قبل البدء، تأكد من توفر ما يلي:
- Node.js إصدار 20 أو أحدث
- مشروع Next.js 14 أو أحدث يستخدم App Router (أو شغّل
npx create-next-app@latest) - إلمام أساسي بخطافات React ولغة TypeScript
- مكتبة Zod مثبّتة (سنستخدمها لمخططات الأدوات)
- متصفح Chrome أو أي متصفح مبني على Chromium للاختبار عبر إضافة MCP-B
ما الذي ستبنيه
ستبني موقعاً تجريبياً لعرض المنتجات يكشف أربع قدرات لوكلاء الذكاء الاصطناعي:
search_products— أداة قرائية فقط تبحث في كتالوج المنتجات بكلمة مفتاحيةget_product_details— أداة قرائية فقط تعيد التفاصيل الكاملة لمنتج واحد- مورد المنتجات — مستند JSON منظم وقابل للعنونة يمكن للوكلاء قراءته دون استدعاء أداة
request_quote— أداة كتابية تلتقط عميلاً محتملاً، محمية خلف تأكيد صريح من المستخدم عبر آلية الاستيضاح في WebMCP
في النهاية، سيتمكن وكيل ذكاء اصطناعي متصل بصفحتك (عبر إضافة MCP-B للمتصفح أو متصفح وكيلي) من الإجابة عن أسئلة مثل "اعثر لي على حاسوب محمول ضمن الميزانية واطلب عرض سعر له" باستدعاء أدواتك بدلاً من كشط HTML.
الخطوة 1: إعداد المشروع
أنشئ مشروع Next.js جديداً إن لم يكن لديك واحد:
npx create-next-app@latest webmcp-demo --typescript --app --tailwind
cd webmcp-demoثبّت حزم WebMCP ومكتبة Zod:
npm install @mcp-b/global @mcp-b/react-webmcp zodإذا كان مشروعك يستخدم Zod 4 بالفعل، فقد تواجه تعارضاً في التبعيات لأن @mcp-b تثبّت حالياً على Zod 3. ثبّت باستخدام العلم القديم: npm install @mcp-b/global @mcp-b/react-webmcp --legacy-peer-deps. ستعمل الأدوات بشكل صحيح أثناء التشغيل.
تلعب الحزمتان دورين متمايزين:
@mcp-b/globalهي البديل (polyfill). استيرادها مرة واحدة يضيفnavigator.modelContextإلى المتصفح بحيث يعمل تسجيل الأدوات حتى قبل وصول الدعم الأصلي في المتصفحات.@mcp-b/react-webmcpتوفّر خطافات React — useWebMCPللأدوات، وuseWebMCPResourceللموارد، وuseElicitationلتأكيدات المستخدم — وتتولى التسجيل والتنظيف عند التركيب والفك.
الخطوة 2: إنشاء طبقة بيانات المنتجات
تحتاج الأدوات إلى شيء تعمل عليه. أنشئ كتالوجاً صغيراً في الذاكرة في lib/products.ts:
// lib/products.ts
export type Product = {
slug: string;
name: string;
category: string;
price: number;
description: string;
inStock: boolean;
};
export const products: Product[] = [
{
slug: "aero-14-laptop",
name: "Aero 14 Ultrabook",
category: "laptops",
price: 1299,
description: "A 14-inch ultrabook with 32GB RAM and 18-hour battery life.",
inStock: true,
},
{
slug: "titan-desk-pro",
name: "Titan Desk Pro",
category: "desks",
price: 549,
description: "Electric standing desk with dual motors and memory presets.",
inStock: true,
},
{
slug: "quiet-buds-x",
name: "QuietBuds X",
category: "audio",
price: 199,
description: "Noise-cancelling earbuds with adaptive transparency mode.",
inStock: false,
},
];
export function searchProducts(query: string): Product[] {
const q = query.toLowerCase();
return products.filter(
(p) =>
p.name.toLowerCase().includes(q) ||
p.category.toLowerCase().includes(q) ||
p.description.toLowerCase().includes(q)
);
}
export function getProduct(slug: string): Product | undefined {
return products.find((p) => p.slug === slug);
}في تطبيق حقيقي، ستستعلم هذه الدوال من قاعدة بياناتك أو تستدعي مسارات API — طبقة الأدوات التي سنبنيها لاحقاً لا تهتم بمصدر البيانات.
الخطوة 3: تسجيل أول أداة قرائية
أنشئ مكوّن عميل في components/WebMCPProvider.tsx. تعيش أدوات WebMCP في المتصفح، لذا يجب أن يبدأ هذا الملف بتوجيه use client:
// components/WebMCPProvider.tsx
"use client";
import "@mcp-b/global";
import { useWebMCP } from "@mcp-b/react-webmcp";
import { z } from "zod";
import { searchProducts } from "@/lib/products";
// عرّف المخططات على مستوى الوحدة لتبقى ثابتة عبر عمليات إعادة التصيير.
const searchInput = {
query: z.string().describe('Search keyword, e.g. "laptop" or "desk"'),
};
const productSchema = z.object({
slug: z.string(),
name: z.string(),
category: z.string(),
price: z.number(),
inStock: z.boolean(),
});
const searchOutput = {
results: z.array(productSchema),
};
export default function WebMCPProvider() {
useWebMCP({
name: "search_products",
description:
"Search the product catalog by keyword. Returns matching products with name, price, and stock status.",
inputSchema: searchInput,
outputSchema: searchOutput,
annotations: { readOnlyHint: true, openWorldHint: false },
handler: async ({ query }) => {
const results = searchProducts(query).map((p) => ({
slug: p.slug,
name: p.name,
category: p.category,
price: p.price,
inStock: p.inStock,
}));
return { results };
},
formatOutput: (o) => JSON.stringify(o.results, null, 2),
});
return null;
}هناك تفاصيل هنا أهم مما تبدو عليه:
- المخططات خارج المكوّن. إذا عرّفت مخططات Zod داخل المكوّن، فسيُعاد إنشاؤها مع كل إعادة تصيير، وقد يعيد الخطاف تسجيل الأداة مراراً. المخططات على مستوى الوحدة تحافظ على استقرار التسجيل.
- حقل
descriptionهو موجّهك للوكيل. يقرر الوكيل استدعاء أداتك بناءً على هذا النص وحده. اكتبه كما لو كنت توثّق واجهة برمجية لمطوّر مبتدئ: ماذا تفعل، وماذا تعيد، وأمثلة ملموسة للمدخلات. - حقل
annotationsيوجّه سلوك الوكيل. يخبرreadOnlyHint: trueالعملاء بأن هذه الأداة بلا آثار جانبية، فيمكن للوكلاء استدعاؤها بحرية. أماopenWorldHint: falseفيعني أن الأداة تعمل على مجموعة بيانات مغلقة — كتالوجك — وليس الويب المفتوح. - دالة
formatOutputتتحكم في التمثيل النصي المقروء للبشر المُرسل إلى جانب المخرجات المنظمة.
الخطوة 4: إضافة أداة التفاصيل
داخل المكوّن نفسه، سجّل أداة ثانية أسفل الأولى. يجب أن تعيد الأدوات الفاشلة أخطاء منظمة بدلاً من رمي استثناءات، حتى يتمكن الوكيل من التعافي:
const detailsInput = {
slug: z.string().describe('Product slug, e.g. "aero-14-laptop"'),
};
const detailsOutput = {
error: z.string().optional(),
name: z.string().optional(),
category: z.string().optional(),
price: z.number().optional(),
description: z.string().optional(),
inStock: z.boolean().optional(),
};useWebMCP({
name: "get_product_details",
description:
"Get full details of a product by its slug. Use search_products first to find slugs.",
inputSchema: detailsInput,
outputSchema: detailsOutput,
annotations: { readOnlyHint: true, openWorldHint: false },
handler: async ({ slug }) => {
const product = getProduct(slug);
if (!product) {
return { error: `Product "${slug}" not found. Try search_products first.` };
}
return {
name: product.name,
category: product.category,
price: product.price,
description: product.description,
inStock: product.inStock,
};
},
formatOutput: (o) => JSON.stringify(o, null, 2),
});لاحظ أن رسالة الخطأ تخبر الوكيل بما يجب فعله لاحقاً — "جرّب search_products أولاً" — محوّلةً الطريق المسدود إلى مسار تعافٍ. رسائل الخطأ الجيدة في الأدوات هي موجّهات للوكلاء في هيئة أخرى.
الخطوة 5: نشر مورد
الأدوات تجيب عن الأسئلة؛ أما الموارد فتنشر المستندات. المورد هو حمولة قرائية فقط قابلة للعنونة بمعرّف URI. يمكن للوكلاء قراءتها مباشرة دون تركيب استعلام، وهو أمر مثالي للفهارس والكتالوجات ومعلومات الشركة.
أضف خطاف useWebMCPResource إلى المزوّد:
import { useWebMCP, useWebMCPResource } from "@mcp-b/react-webmcp";
import { products } from "@/lib/products";
// داخل جسم المكوّن:
useWebMCPResource({
uri: "shop://products/index",
name: "Product Catalog Index",
description: "Complete list of products with slugs, prices, and stock status.",
mimeType: "application/json",
read: async (uri) => ({
contents: [
{
uri: uri.href,
text: JSON.stringify(products, null, 2),
},
],
}),
});يمكنك أيضاً الإعلان عن معرّفات URI ذات معاملات باستخدام مقاطع قوالب، وقراءة المعاملات داخل دالة الاستدعاء:
useWebMCPResource({
uri: "shop://products/by-category/{category}",
name: "Products by Category",
description: "Products filtered by category name.",
mimeType: "application/json",
read: async (uri, params) => {
const category = (params?.category as string) || "";
const filtered = products.filter((p) => p.category === category);
return {
contents: [{ uri: uri.href, text: JSON.stringify(filtered, null, 2) }],
};
},
});قاعدة عملية جيدة: إذا كان الوكيل سيستدعي الأداة نفسها بالمعاملات نفسها في كل جلسة لمجرد التموضع، فانشر تلك البيانات كمورد بدلاً من ذلك.
الخطوة 6: بناء أداة كتابية بتأكيد بشري
الأدوات القرائية آمنة بطبيعتها. أما الأدوات الكتابية — إرسال النماذج، إنشاء عملاء محتملين، إرسال الرسائل — فتحتاج بوابة موافقة. إجابة WebMCP هي الاستيضاح (elicitation): تتوقف الأداة في منتصف التنفيذ وتطلب من العميل المتصل جمع مدخلات من المستخدم البشري.
أضف خطاف useElicitation ودالة تأكيد مساعدة:
import { useElicitation, useWebMCP } from "@mcp-b/react-webmcp";
// داخل جسم المكوّن:
const { elicitInput } = useElicitation();
const confirmWithUser = async (title: string, preview: object) => {
const details = JSON.stringify(preview, null, 2);
// المسار المفضّل: يعرض استيضاح WebMCP واجهة تأكيد أصلية
// في عميل MCP المتصل.
if (typeof window !== "undefined" && window.navigator?.modelContext) {
try {
const result = await elicitInput({
message: `${title}\n\nReview and confirm:\n\n${details}`,
requestedSchema: {
type: "object",
properties: {
confirm: {
type: "boolean",
title: "Confirm",
description: "Approve and send this request.",
},
},
required: ["confirm"],
},
});
return result.action === "accept" && result.content?.confirm === true;
} catch {
// الرجوع إلى window.confirm أدناه.
}
}
// البديل: مربع حوار تأكيد المتصفح العادي.
if (typeof window !== "undefined" && typeof window.confirm === "function") {
return window.confirm(`${title}\n\n${details}\n\nSend?`);
}
// لا توجد واجهة تأكيد متاحة: ارفض الأثر الجانبي.
return false;
};والآن الأداة الكتابية نفسها:
const quoteInput = {
productSlug: z.string().describe("Slug of the product to quote"),
email: z.string().email().describe("Contact email for the quote"),
quantity: z.number().min(1).describe("Number of units"),
};
const quoteOutput = {
success: z.boolean(),
message: z.string().optional(),
error: z.string().optional(),
};
useWebMCP({
name: "request_quote",
description:
"Request a price quote for a product. Requires user confirmation before sending. Use this only after the user has expressed intent to get a quote.",
inputSchema: quoteInput,
outputSchema: quoteOutput,
annotations: { readOnlyHint: false, openWorldHint: false },
handler: async ({ productSlug, email, quantity }) => {
const product = getProduct(productSlug);
if (!product) {
return { success: false, error: `Unknown product "${productSlug}"` };
}
const approved = await confirmWithUser("Send quote request?", {
product: product.name,
email,
quantity,
estimatedTotal: product.price * quantity,
});
if (!approved) {
return { success: false, error: "User declined the quote request." };
}
const res = await fetch("/api/quotes", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ productSlug, email, quantity }),
});
if (!res.ok) {
return { success: false, error: `Server error: ${res.status}` };
}
return { success: true, message: "Quote request sent. Check your email." };
},
});الموقف الأمني هنا مقصود ويستحق الاستيعاب:
- الوكيل يقترح، والإنسان يقرر. لا ترسل الأداة أي شيء أبداً دون تأكيد منطقي صريح من المستخدم.
- تعرض المعاينة بالضبط ما سيُرسل — المنتج والبريد الإلكتروني والكمية والإجمالي التقديري — فالمستخدم يؤكد حقائق لا انطباعات.
- عند غياب واجهة تأكيد، ترفض الأداة. الفشل الآمن أفضل من الإرسال الصامت.
- نقطة النهاية على الخادم تتحقق من كل شيء رغم ذلك. تعمل أدوات المتصفح داخل جلسة المستخدم وبملفات تعريف ارتباطه؛ ويجب أن تطبّق واجهتك البرمجية المصادقة والتحقق وتحديد المعدل نفسها المطبقة على أي استدعاء من جهة العميل.
الخطوة 7: ربط المزوّد بتخطيط التطبيق
ركّب المزوّد مرة واحدة في الجذر لتسجَّل الأدوات في كل صفحة:
// app/layout.tsx
import WebMCPProvider from "@/components/WebMCPProvider";
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="ar" dir="rtl">
<body>
<WebMCPProvider />
{children}
</body>
</html>
);
}ولأن useWebMCP ينظّف نفسه عند فك التركيب، يمكنك أيضاً تسجيل أدوات سياقية خاصة بالصفحة داخل مسارات فردية — مثلاً أداة get_current_article توجد فقط في صفحات المقالات وتعيد المحتوى الذي يقرأه المستخدم حالياً. الأدوات على مستوى الجذر تصف ما يستطيع موقعك فعله؛ والأدوات على مستوى الصفحة تصف مكان المستخدم الحالي. الجمع بينهما يمنح الوكلاء خريطة وعلامة "أنت هنا" في آن واحد.
اختبار التنفيذ
شغّل خادم التطوير:
npm run devثم تحقق من تسجيل الأدوات. افتح وحدة تحكم المتصفح على localhost:3000 وافحص البديل:
// في وحدة تحكم المتصفح:
navigator.modelContext !== undefined; // يجب أن تكون trueللاختبار الشامل، ثبّت إضافة MCP-B من متجر Chrome الإلكتروني. تعمل الإضافة كعميل MCP يكتشف الأدوات في أي صفحة مفتوحة. افتح لوحتها الجانبية على موقعك التطويري وسترى search_products وget_product_details وrequest_quote مدرجة مع أوصافها.
تعمل الإضافة أيضاً كجسر بين أدوات الصفحة ومضيفي MCP الأصليين. هذا يعني أن وكلاء سطح المكتب مثل Claude Code أو Claude Desktop يمكنهم استدعاء أدوات الصفحة المفتوحة في Chrome — يصبح موقعك فعلياً خادم MCP دون استضافة واحد.
جرّب تدفقاً واقعياً في محادثة الإضافة: اسأل "اعثر لي على ألترابوك واطلب عرض سعر لوحدتين إلى test@example.com". سيسلسل الوكيل القدير search_products ثم get_product_details ثم request_quote — وسترى مطالبة الاستيضاح تظهر طالبةً موافقتك قبل إرسال أي شيء.
استكشاف الأخطاء وإصلاحها
لا تظهر الأدوات في العميل. تأكد من تنفيذ import "@mcp-b/global" قبل أي تسجيل، ومن أن مكوّن المزوّد يتركّب فعلاً (يجب أن يكون داخل body وأن يكون مكوّن عميل). افحص وحدة التحكم بحثاً عن أخطاء hydration قد تفك تركيبه.
تُسجَّل الأدوات مرتين في التطوير. يركّب وضع React Strict Mode المكوّنات مرتين في بنيات التطوير. تتعامل خطافات @mcp-b مع التنظيف بشكل صحيح، لكن إن سجّلت أدوات يدوياً عبر navigator.modelContext.registerTool فعليك إلغاء التسجيل عند فك التركيب بنفسك — سبب إضافي لتفضيل الخطافات.
تعارض تبعيات عند التثبيت. تثبّت حزم @mcp-b على Zod 3 بينما تستخدم مشاريع كثيرة في 2026 إصدار Zod 4. استخدم --legacy-peer-deps مع npm أو اقبل تحذير الحل مع pnpm. أبقِ مخططات أدواتك على إصدار Zod الذي تتوقعه @mcp-b.
تغييرات المعالج لا تسري. يلتقط الخطاف معالجك عند التسجيل. في التطوير مع Fast Refresh يُحل الأمر عادة تلقائياً؛ وفي الإنتاج تأكد من أن القيم الديناميكية التي يحتاجها معالجك تأتي من مراجع (refs) أو حالة على مستوى الوحدة، لا من إغلاقات قديمة.
استدعاء الاستيضاح يرمي خطأ. لا يطبّق كل عميل MCP آلية الاستيضاح بعد. غلّف elicitInput دائماً بـ try/catch وارجع إلى window.confirm كما في الخطوة 6.
اعتبارات أمنية
يرث WebMCP نموذج أمان المتصفح، لكن الوكلاء يضيفون أنماط فشل جديدة تستحق التصميم لأجلها:
- عامل مدخلات الأدوات كغير موثوقة. تحقق منها بـ Zod (تفعل الخطافات ذلك عنك) ولا تُدرج المدخلات أبداً في HTML أو الاستعلامات.
- ضيّق نطاق الأدوات الكتابية. أداة واحدة لكل إجراء، ومعاملات قليلة، وتأكيد صريح. تجنّب أدوات "نفّذ هذا" العامة.
- تذكّر سياق الجلسة. تعمل الأدوات بملفات تعريف ارتباط المستخدم المسجّل وصلاحياته. وكيل يستدعي
request_quoteلا يمكن تمييزه عن المستخدم وهو ينقر الزر — وهذا بالضبط سبب أهمية بوابة التأكيد. - حدّد المعدل على الخادم. يمكن لوكيل في حلقة استدعاء الأدوات أسرع بكثير من نقر الإنسان. تحتاج مسارات API لديك إلى الحماية نفسها المطبقة على أي عميل آلي.
الخطوات التالية
- أضف أدوات سياقية لكل صفحة تكشف المحتوى المعروض حالياً، ليتمكن الوكلاء من الإجابة عن "لخّص هذه الصفحة" من بيانات منظمة بدلاً من DOM
- انشر مورد كتالوج أدوات (مستند JSON يسرد كل أدواتك ومواردك) ليتموضع الوكلاء بقراءة واحدة
- استكشف درس بناء خادم MCP بلغة TypeScript لمقارنة النهج الخادمي مع WebMCP في المتصفح
- اقرأ دليلنا بناء عميل MCP بلغة TypeScript لفهم النصف الآخر من البروتوكول
- تابع مستودع مجموعة W3C Web Machine Learning لمواصفة
navigator.modelContextالأصلية المتطورة
الخلاصة
يعكس WebMCP العلاقة بين مواقع الويب ووكلاء الذكاء الاصطناعي: فبدلاً من قيام الوكلاء بهندسة عكسية لواجهتك، ينشر موقعك واجهة برمجية منمّطة وموثقة وواعية بالصلاحيات تعيش داخل الصفحة نفسها. ومع @mcp-b/react-webmcp تكون كلفة التنفيذ منخفضة بشكل لافت — مكوّن عميل، وحفنة من مخططات Zod، وأوصاف مكتوبة بعناية.
لقد بنيت أداة بحث في الكتالوج، وأداة تفاصيل، وموارد ذات معاملات، وأداة كتابية محمية بموافقة بشرية. النمط نفسه يتوسع إلى مواقع إنتاج حقيقية: على noqta.tn تشغّل هذه البنية بالضبط البحث في الخدمات واكتشاف المحتوى وطلبات عروض الأسعار لأي وكيل ذكاء اصطناعي يزور الموقع. الويب الوكيلي قادم بسرعة، والمواقع التي تكشف أدوات منظمة هي التي سيتمكن الوكلاء فعلاً من استخدامها — بدقة وأمان وبقاء المستخدم مسيطراً.