خادم MCP للغة Python
خادم MCP للغة Python
قسم بعنوان «خادم MCP للغة Python»تتضمّن حزمة تطوير NextPDF للغة Python خادم Model Context Protocol (MCP) يتيح عمليات استخراج PDF على هيئة أدوات أصلية للوكلاء. يسجّل الوكيل الداعم لـ MCP، مثل Claude Code، الخادم مرة واحدة، ثم يستدعي أدوات NextPDF بالطريقة نفسها التي يستدعي بها أي أداة أخرى.
الخادم طبقة مواءمة خفيفة. تقرأ كل أداة ملف PDF من القرص المحلي، وتستدعي العميل غير المتزامن لنقطة نهاية NextPDF Connect الخاصة بك، ثم تُعيد النتيجة بوصفها سلسلة JSON. لا يحتفظ الخادم بأي منطق أعمال، ولا يخزّن أي بيانات بين الاستدعاءات.
ثبّت حزمة التطوير مع إضافة MCP:
pip install nextpdf[mcp]تُثبّت إضافة mcp حزمة mcp الأصلية من المنبع (القيد mcp>=1.0,<2.0). يتطلّب الخادم Python 3.10 أو أحدث.
شغّل وحدة الخادم من إعدادات عميل MCP لديك. يقرأ المثال أدناه قيمتَي الاتصال كلتيهما من بيئة المضيف، بدلاً من تضمين أي سرّ في ملف الإعدادات (انظر الأمان):
{ "mcpServers": { "nextpdf": { "command": "python", "args": ["-m", "nextpdf.mcp"], "env": { "NEXTPDF_BASE_URL": "https://connect.example.com", "NEXTPDF_API_KEY": "${NEXTPDF_API_KEY}" } } }}تُشغّل نقطة الدخول python -m nextpdf.mcp الدالة main()، التي تبدأ الخادم عبر الإدخال/الإخراج القياسي input/output (stdio) باستخدام asyncio.run(serve()). لا تخلط بينها وبين python -m nextpdf، فهي تُشغّل واجهة سطر الأوامر (CLI)، لا خادم MCP.
المتغيّران NEXTPDF_BASE_URL وNEXTPDF_API_KEY مطلوبان. يُنشئ الخادم العميل عند الحاجة، وذلك عند أول استدعاء لأداة. إذا كان أيٌّ من المتغيّرين فارغاً، فإنه يطلق RuntimeError ويُعيده إلى الوكيل بوصفه خطأ أداة، بدلاً من التسبب في انهيار العملية.
فهرس الأدوات وربطها بحزمة التطوير
قسم بعنوان «فهرس الأدوات وربطها بحزمة التطوير»يسجّل الخادم ثماني أدوات. يستخدم اسم كل أداة البادئة nextpdf_. تُربَط كل أداة بطريقة في فضاء الأسماء ast الخاص بالعميل غير المتزامن (AsyncNextPDF.ast)، باستثناء الأداتين المركّبتين المذكورتين أدناه. يكوّن الخادم هاتين الأداتين من استدعاءات منخفضة المستوى.
| أداة MCP | استدعاء حزمة التطوير | ملاحظات |
|---|---|---|
nextpdf_extract_text | ast.extract_cited_text(pdf_data, page_index=..., headings_only=...) | يُعيد قائمة من CitedTextBlock. |
nextpdf_extract_tables | ast.extract_cited_tables(pdf_data, page_range=...) | يُعيد ExtractCitedTablesResponse. |
nextpdf_get_ast | ast.get_document_ast(pdf_data, page_range_start=0, page_range_end=..., token_budget=...) | يُعيد AstDocument. |
nextpdf_info | ast.get_document_ast(pdf_data) | يُسقِط الخادم ملخّص بيانات وصفية؛ لا توجد نقطة نهاية مخصّصة. |
nextpdf_health | لا شيء | يفحص متغيّرات البيئة فقط؛ لا يُجري أي استدعاء شبكي. |
nextpdf_search | ast.search_ast_nodes(pdf_data, node_type=..., page_index=..., text_query=..., max_results=...) | يُعيد SearchAstNodesResponse. |
nextpdf_get_outline | ast.search_ast_nodes(pdf_data, node_type="heading", max_results=500) | يعيد الخادم تشكيل عُقَد العناوين في مخطّط تفصيلي. |
nextpdf_diff | ast.get_ast_diff(original_pdf_data, modified_pdf_data) | يُعيد GetAstDiffResponse. |
راعِ تفاصيل مُدخلات هذه الأدوات قبل توصيل أي وكيل:
- جميع مُدخلات المسارات (
pdf_pathوoriginal_pdf_pathوmodified_pdf_path) هي مسارات مطلقة لملفات على الجهاز الذي يشغّل الخادم. يمرّر الوكيل مساراً، ويقرأ الخادم البايتات محلياً. لا توجد أداة رفع. - تُصرّح
nextpdf_extract_textبحقلmax_pagesفي مخطّط مُدخلاتها، لكن مُعالج النص لا يمرّره إلى حزمة التطوير. يُحدَّد نطاق الصفحات للنص عبرpage_index(صفحة واحدة مفهرسة من الصفر). استخدمnextpdf_get_astمعmax_pagesعندما تحتاج إلى تقييد مسح يشمل المستند بأكمله. - تحوّل
nextpdf_get_astالقيمةmax_pagesإلى نطاق صفحات شامل[0, max_pages - 1](القيمة الافتراضية لـmax_pagesهي 50). مرّرtoken_budgetلتقييد حجم الشجرة المُعادة. - تُعيد
nextpdf_infoالقيمschema_versionوsource_hashوpage_countوestimated_tokensوroot_node_typeوroot_children_count. تأتي هذه القيم من نموذجAstDocument، حيث إنestimated_tokensخاصية محسوبة (نحو أربعة محارف لكل رمز تقريباً). - تُعيد
nextpdf_get_outlineعنصراً واحداً لكل عنوان يتضمّنidوpage_indexوtextوdepth(تُقرأ منattributes["level"]الخاص بالعقدة، بقيمة افتراضية 1)، إضافةً إلىheading_countوtotal_matchesوtruncated.
تُرفق أدوات الاستخراج الموثّق بالاستشهادات CitationAnchor بكل نتيجة. تتضمّن كل مرساة node_id وpage_index وقيمة bbox مُسوّاة (إحداثيات في النطاق من 0.0 إلى 1.0)، ودرجة confidence (من 0.0 إلى 1.0). ينبغي للوكلاء الذين يحتاجون إلى تتبّع المصدر أن يعرضوا هذه الحقول، لا النص الخام وحده.
معالجة الأخطاء والمهل والحصص
قسم بعنوان «معالجة الأخطاء والمهل والحصص»لا يدع الخادم أي استثناء يتسرّب إلى قناة نقل الوكيل. يلتقط الموزِّع call_tool كل خطأ ويُعيده بصيغة JSON بوصفه TextContent، بحيث يُنتج استدعاء الأداة الفاشل حمولة مُهيكلة يستطيع الوكيل قراءتها بدلاً من قطع الاتصال. صيغ الحمولة هي:
| الحالة | JSON المُعاد |
|---|---|
| اسم أداة غير معروف | {"error": "Unknown tool: <name>"} |
| ملف مُدخل مفقود | {"error": "PDF file not found: <path>"} |
أي صنف فرعي من NextPDFError | {"error": "<message>", "error_type": "<class>", "status_code": <int?>} |
| أي استثناء آخر | {"error": "Unexpected error: <message>"} |
تظهر status_code فقط عندما يحمل الخطأ الأساسي هذه القيمة. تربط حزمة التطوير استجابات HTTP بتسلسل هرمي من الاستثناءات المُصنَّفة، وجذرها جميعاً NextPDFError:
| الاستثناء | حالة HTTP | error_code | متى |
|---|---|---|---|
NextPDFLicenseError | 402 | license/tier-required | تتطلّب نقطة النهاية فئة ترخيص أعلى على جانب الخادم لإجراء العملية. |
AstNoStructTreeError | 422 | ast/no-struct-tree | ملف PDF غير موسوم والاحتياط الاستدلالي غير مُفعَّل على الخادم. |
QuotaExceededError | 429 | quota/exceeded | جرى تجاوز حدّ معدّل أو حصة. يحمل retry_after (بالثواني) عندما يرسل الخادم ترويسة Retry-After. |
AstBuildTimeoutError | 504 | ast/build-timeout | تجاوز بناء AST ميزانية الوقت لدى الخادم. قلّل نطاق الصفحات. |
NextPDFAPIError | حالة 4xx/5xx أخرى | مُقدَّم من الخادم | أي إخفاق آخر على مستوى الـ API. |
استخدم الإرشادات الآتية لتكاملات الوكلاء:
- المهل. يستخدم عميل HTTP مهلة افتراضية ثابتة: 60 ثانية إجمالاً مع مهلة اتصال قدرها 10 ثوانٍ. قد يظهر المستند البطيء أو الكبير إما بوصفه
AstBuildTimeoutError(تخلّى الخادم عن بناء AST)، أو، إذا انتهت مهلة العميل نفسه، بوصفه حمولةUnexpected errorمن طبقة النقل. عندما ترىast/build-timeout، اطلب من الوكيل تضييق النطاق: اخفضmax_pagesفيnextpdf_get_ast، أو اضبطpage_index/page_startوpage_endفي أدوات الاستخراج. - الحصة والتراجع. عند الحالة 429، تُعيد الأداة
error_typeبقيمةQuotaExceededErrorمعstatus_code429. تكون قيمةretry_afterفي كائن الاستثناء. وبما أن الخادم يُسلسِلerrorوerror_typeوstatus_codeفقط، فينبغي للوكيل أن يعامل الحالة 429 إشارةً إلى التوقّف وإعادة المحاولة لاحقاً، بدلاً من تحليل ترويسة إعادة المحاولة من مخرجات الأداة. افرض الحصص عند نقطة نهاية Connect، لا داخل الوكيل. - ملفات PDF غير الموسومة. تعني الحالة 422
ast/no-struct-treeأن ملف PDF المصدر لا يحتوي على شجرة بنية. فعّل الوضع الاستدلالي على الخادم لهذه المستندات، أو وجّهها إلى خطوة وسم قبل الاستخراج.
الأمان: تحديد نطاق مفتاح الـ API ومبدأ الحدّ الأدنى من الامتيازات
قسم بعنوان «الأمان: تحديد نطاق مفتاح الـ API ومبدأ الحدّ الأدنى من الامتيازات»تعامَل مع مفتاح الـ API بوصفه سرّاً، وبالعناية نفسها التي توليها لكلمة مرور قاعدة البيانات.
- لا تضمّن المفتاح أبداً في ملف إعدادات MCP. يشير مثال JSON أعلاه إلى
${NEXTPDF_API_KEY}كي تُحلّ القيمة من بيئة المضيف أو من مدير أسرار وقت الإطلاق. قد يُودَع ملف الإعدادات في إدارة الإصدارات؛ أما السرّ فيجب ألّا يُودَع. - اقصر نطاق المفتاح على عمليات استخراج للقراءة فقط. يستدعي خادم MCP سطح استخراج AST فقط (
extract_cited_textوextract_cited_tablesوget_document_astوsearch_ast_nodesوget_ast_diff). لا يعرض المستندات، ولا يوقّعها، ولا يُنقّحها، ولا يُعدّلها. أصدِر للوكيل مفتاحاً يقتصر نطاقه في الخادم على مسارات القراءة تلك، حتى لا يستطيع وكيل مُخترَق بلوغ عمليات الكتابة أو العمليات ذات الفئة الأعلى. - استخدم مفتاحاً مخصّصاً لكل وكيل. يتيح لك المفتاح المخصّص لكل وكيل إبطال تكامل واحد أو تدويره دون التأثير في غيره، ويجعل سجلّات نقطة النهاية منسوبة إلى وكيل بعينه.
- قيّد نظام الملفات. لأن كل أداة تقرأ مساراً مطلقاً من القرص المحلي، يستطيع الخادم قراءة أي ملف تستطيع عملية المضيف قراءته. شغّله بوصفه مستخدماً غير مميَّز، وقيّد دليل عمله بمجلّد مستندات، ولا تشغّله أبداً بحساب مميَّز.
- فضّل أمان طبقة النقل (TLS). وجّه
NEXTPDF_BASE_URLإلى نقطة نهايةhttps://لأي نشر غير محلي. ترسل حزمة التطوير المفتاح بوصفه رمزBearerفي ترويسةAuthorization، فالنقل بالنص الصريح يكشفه أثناء النقل.
راجِع أمان Connect وعملياته للاطّلاع على ضوابط جانب نقطة النهاية التي تدعم هذه الممارسات على جانب العميل.
اختبار الخادم محلياً قبل توصيل وكيل
قسم بعنوان «اختبار الخادم محلياً قبل توصيل وكيل»تحقّق من الخادم بمعزل عن الوكيل قبل توصيله به. لا يحتاج الفحص الأسرع إلى ملف PDF ولا إلى شبكة:
python -c "from nextpdf.mcp import _tool_definitions; print(len(_tool_definitions()))"يطبع التثبيت السليم 8. إذا رأيت ImportError يذكر الإضافة mcp، فهذا يعني أن التبعية الاختيارية مفقودة. أعِد التثبيت بـpip install nextpdf[mcp].
بعد ذلك، اختبر عبر واجهة سطر الأوامر (CLI) مسارات حزمة التطوير نفسها التي تستخدمها الأدوات. تتصل واجهة سطر الأوامر بنقطة نهايتك باستخدام متغيّري البيئة نفسيهما. اضبطهما مرة واحدة:
export NEXTPDF_BASE_URL="https://connect.example.com"export NEXTPDF_API_KEY="$(cat /run/secrets/nextpdf_api_key)"ثم تأكّد من الإصدار والاتصال ومن استخراج فعلي:
nextpdf versionnextpdf info /path/to/sample.pdfnextpdf extract text /path/to/sample.pdf --headings-onlyيعمل nextpdf version دون بيانات اعتماد ويؤكّد استيراد الحزمة. يختبر nextpdf info استدعاء get_document_ast نفسه الذي يقف خلف nextpdf_get_ast وnextpdf_info. إذا نجح nextpdf info والاستخراج، فهذا يعني أن بيانات الاعتماد ونقطة النهاية صحيحة، وأن أدوات MCP المقابلة ستعمل.
لقيادة بروتوكول MCP مباشرةً، استخدم MCP Inspector الأصلي من المنبع (المُرفق مع حزمة mcp). وجّهه إلى الأمر والبيئة نفسيهما اللذين سيستخدمهما وكيلك، ثم اسرد الأدوات واستدعِها يدوياً. تحقّق من أن nextpdf_health يُبلّغ status: "ok". يُعيد misconfigured عندما يكون NEXTPDF_BASE_URL أو NEXTPDF_API_KEY غير مضبوط، وهذه أسرع طريقة لاكتشاف قيمة بيئة مفقودة قبل أن يستدعي الوكيل أداة فعلية.
مراقبة استدعاءات الأدوات وتنقيحها
قسم بعنوان «مراقبة استدعاءات الأدوات وتنقيحها»يتواصل خادم MCP عبر الإدخال/الإخراج القياسي input/output (stdio)، لذا يحمل خرجه القياسي دفق البروتوكول ويجب أن يبقى نظيفاً. لا يهيّئ الخادم أي تسجيل تطبيق خاص به. قنوات المراقبة الأساسية لديك هي حمولات أخطاء الأدوات المُهيكلة، وواجهة سطر الأوامر، وسجلّات نقطة النهاية الخاصة بك.
- حمولات أخطاء الأدوات هي الإشارة. يُعيد كل استدعاء فاشل كائن JSON يتضمّن
error، وفي حال أخطاء حزمة التطوير،error_typeوstatus_code(انظر معالجة الأخطاء). اجعل مضيف الوكيل يسجّل هذه الحمولات؛ فهي تحدّد الأداة الفاشلة والسبب الدقيق دون أدوات قياس إضافية على الخادم. - أعِد الإنتاج عبر واجهة سطر الأوامر مع تسجيل التنقيح. لا يصدر خادم MCP نفسه أي سجلّات، لكن واجهة سطر الأوامر تستخدم استدعاءات حزمة التطوير نفسها وتُسجّل بالفعل. أعِد إنتاج أداة فاشلة عبر أمر واجهة سطر الأوامر المقابل مع
--log-level debug. تُسجّل واجهة سطر الأوامر إلى stderr مع طوابع زمنية، وتُدوّن آثار تتبّع كاملة للأخطاء غير المتوقّعة. هذه هي الطريقة الأكثر مباشرةً لمعرفة ما يفعله المُعالج دون إرفاق مُنقّح. - الصحة بوصفها مسباراً. استدعِ
nextpdf_healthلتأكيد أن الخادم يرى عنوان URL أساسياً ومفتاح API. تُبلّغ النتيجةsdk_versionوserver_urlوapi_key_configured(قيمة منطقية، لا المفتاح نفسه أبداً)، وstatus. - المراقبة على جانب نقطة النهاية. لأن كل أداة تُربَط بطلب Connect واحد، اربط نشاط الأدوات بسجلّات وصول نقطة النهاية بحسب مفتاح الـ API والطابع الزمني. شغّل نقطة النهاية خلف ضوابط المصادقة والحصص والمراقبة نفسها التي تستخدمها لعملاء الخدمة الآخرين.
استكشاف مشكلات تكامل الوكلاء الشائعة وإصلاحها
قسم بعنوان «استكشاف مشكلات تكامل الوكلاء الشائعة وإصلاحها»| العَرَض | السبب المرجَّح | الحل |
|---|---|---|
يفشل الخادم في البدء مع ImportError بخصوص الإضافة mcp | التبعية الاختيارية mcp غير مثبَّتة | ثبّت بـpip install nextpdf[mcp]. |
يُعيد أول استدعاء أداة {"error": "NEXTPDF_BASE_URL environment variable is required..."} | لم تمرّر كتلة env في MCP عنوان URL الأساسي، أو لم توسّع الصدفة ${NEXTPDF_BASE_URL} | اضبط المتغيّر في بيئة مضيف الوكيل، وتأكّد من أن المُشغِّل يوسّعه. |
يُبلّغ nextpdf_health عن "status": "misconfigured" | أحد المتغيّرين المطلوبين فارغ | وفّر كلاً من NEXTPDF_BASE_URL وNEXTPDF_API_KEY. |
تُعيد كل أداة قائمة على المسار {"error": "PDF file not found: <path>"} | مرّر الوكيل مساراً نسبياً أو مساراً على جانب المضيف لا تستطيع عملية الخادم رؤيته | مرّر مساراً مطلقاً يستطيع مستخدم الخادم قراءته؛ وتحقّق بـnextpdf info <path>. |
تُعيد الأداة error_typeNextPDFLicenseError (الحالة 402) | تحتاج العملية إلى فئة ترخيص أعلى على جانب الخادم | استخدم نقطة نهاية ومفتاحاً مخوّلَين لإجراء العملية. |
تُعيد الأداة error_typeAstNoStructTreeError (الحالة 422) | ملف PDF غير موسوم والاحتياط الاستدلالي متوقّف | فعّل الوضع الاستدلالي على نقطة النهاية، أو وسِم ملف PDF أولاً. |
تُعيد الأداة error_typeQuotaExceededError (الحالة 429) | جرى بلوغ حدّ معدّل أو حصة | توقّف وأعِد المحاولة؛ ارفع حصة نقطة النهاية إذا كان الحدّ منخفضاً جداً. |
تُعيد الأداة error_typeAstBuildTimeoutError (الحالة 504)، أو انتهاء مهلة نقل | المستند أكبر من أن يتّسع لميزانية الوقت | ضيّق النطاق بـmax_pages أو page_index أو page_start/page_end. |
| لا يسجّل الوكيل أي أدوات NextPDF | استدعى الوكيل python -m nextpdf (واجهة سطر الأوامر) بدلاً من python -m nextpdf.mcp | استخدم python -m nextpdf.mcp بوصفه command/args. |
للاطّلاع على الإخفاقات على مستوى نقطة النهاية وفحوص النشر، راجِع استكشاف أخطاء Connect. وللاطّلاع على عمليات حزمة التطوير التي تغلّفها هذه الأدوات، راجِع مرجع واجهة سطر الأوامر ونظرة عامة على حزمة التطوير.