Free

أن أطلب من Claude إعادة كتابة MCP الذي بنيتُه قبل ستة أيام

رفضت Smithery الـ fast-mcp. نقل Claude خادم MCP بكامله إلى الـ SDK الرسميّ في نصف ساعة — كانت إعادة الكتابة رخيصة لأنّ النسخة الأولى كانت رقيقة.


ربط مشروع smarts بـ MCP حدث قبل ستة أيام. طلبتُ من Claude Code أن يُشغِّل ثلاث tools عبر gem اسمه fast-mcp:

  • get_contract_info(chain, address) — اسم العقد، وسم محوّل البروتوكول، أعداد function / event
  • read_contract_state(chain, address, function_name, args?) — استدعاء view / pure منفرد، تخزين مؤقت 60 ثانية
  • get_uniswap_v3_pool(chain, address) — لوحة V3 كاملة: السعر باتجاهَين، liquidity، tick، TVL بالدولار

ذلك الـ commit كان 27ca82e feat(mcp): serve live contract data over MCP via fast-mcp، يوم 2026-04-21 الساعة 1:37 فجراً. حوالي 30 دقيقة. سَلِس.

بعد ستة أيام، الليلة، يوم 2026-04-27 الساعة 20:24، دفعتُ commit آخر: 4df08fa feat(mcp): switch to official mcp gem with Streamable HTTP transport.

ماذا حدث بين الـ commit-ين؟

رفض Smithery للـ fast-mcp

قدّمتُ smarts إلى Smithery (دليل لخوادم MCP) للتسجيل. أعاد ماسحُهم لي 405 فوراً:

POST /mcp/sse → 405 Method Not Allowed

استغرقتُ بعض الوقت كي أفهم: fast-mcp 1.6.0 (آخر release) ما زال ينفّذ نقل HTTP+SSE الخاص بمواصفة MCP 2024-11-05. أمّا عملاء MCP الحديثون فقد انتقلوا إلى Streamable HTTP الخاص بمواصفة 2025-03-26 — endpoint وحيد، وPOST + DELETE على المسار نفسه، دون مسار /sse منفصل.

بمعنى آخر: المواصفة تقدّمت خطوة، والـ gem الأكثر شيوعاً في المجتمع لم يلحق بعد.

لم أكن لأنتظر fast-mcp. طبقة خادم MCP موجودة كي تستهلكها العملاء، وتوافق المواصفة أهمّ من gem يكون أبسط في الكتابة.

استبدال الأساس، حوالي 30 دقيقة

استبدلتُ fast-mcp بـ gem الرسمي من Anthropic، mcp بإصدار 0.14.0. في هذه الجولة فتحتُ thread جديداً داخل Amp (يعمل تحته Claude Opus نفسه، نفس النموذج الذي يستخدمه Claude Code).

ما هبط في الترحيل:

إعادة توصيل النقل

# القديم: fast-mcp يُركَّب تلقائياً على /mcp مع /sse
# الجديد: تركيب صريح، endpoint وحيد
mount StreamableHTTPTransport.new(server, stateless: true), at: "/mcp"

المفتاح هو stateless: true. لا يحتفظ الخادم بحالة per-session في الذاكرة، ما يسمح لـ Puma بالعمل مع workers > 0 والتوسّع أفقيّاً دون sticky session.

إعادة هيكلة Tool API

الشكل القديم لـ fast-mcp:

class GetContractInfoTool < ApplicationTool
  arguments do
    required(:chain).filled(:string)
    required(:address).filled(:string)
  end

  def call(chain:, address:)
    # منطق الأعمال
  end
end

الـ gem الرسمي يدفع سباكة الـ SDK إلى الفئة الأساسيّة:

class ApplicationTool < MCP::Tool
  def self.call(**args, server_context: nil)
    hash = payload(**args)
    MCP::Tool::Response.new([{ type: "text", text: hash.to_json }])
  end
end

class GetContractInfoTool < ApplicationTool
  input_schema(
    properties: {
      chain:   { type: "string" },
      address: { type: "string" }
    },
    required: %w[chain address]
  )

  def self.payload(chain:, address:)
    # منطق الأعمال، يُعيد Hash
  end
end

الفئات الفرعيّة لا تهتمّ سوى بأيّ Hash يُعيده payload(**args)؛ تتولّى الفئة الأساسيّة تغليفه ضمن text block بصيغة JSON داخل MCP::Tool::Response. مكسبان:

  1. سباكة الـ SDK لا تعود تتفرّق في كلّ فئة tool
  2. يمكن للاختبارات التحقّق مباشرةً من Hash الناتج عن Tool.payload(chain:, address:)، دون قشط مغلّف البروتوكول

استبدلتُ DSL الخاصّ بـ dry-schema بحرفيّات JSON Schema خامّ. يبدو هذا تراجعاً، لكنّ input_schema هو ما يستهلكه العميل مباشرةً من مواصفة MCP — كتابته على شكل بدائيّة المواصفة أصدق.

انتقال الاختبارات

433 اختباراً، حوّلتُها من Tool.new.call(...) إلى Tool.payload(...). كلّها خضراء.

مزامنة discovery / docs

  • .well-known/mcp.json مع transport: "streamable-http" و protocol_version: "2025-03-26"
  • README و llms.txt و smithery.yaml — جميعها مُحدَّثة إلى https://smarts.md/mcp
  • علم claude mcp add انتقل من --transport sse إلى --transport http
  • في CLAUDE.md، يشير قسم الـ tech stack الآن إلى mcp بوصفه primary و إلى fast-mcp بوصفه legacy/deprecated

«الجرأة على إعادة الكتابة» هي الرافعة الفعليّة

الدمج الأوّل لـ MCP استغرق 30 دقيقة. إعادة الكتابة استغرقت تقريباً المدّة ذاتها. بينهما ستّة أيّام.

ما تنبّهتُ إليه: في منظومة فتيّة كـ MCP، تأخّر الـ gems عن المواصفة هو القاعدة. fast-mcp ليس سيّئاً — قائم عليه نشيط. المشكلة أنّ البروتوكول نفسه يتطوّر أسرع من تنفيذات الطرف الثالث. gem يبدو هذا العام صلباً قد يصبح بعد ستّة أشهر «ذاك الذي على المواصفة القديمة».

لو كنتُ كتبتُ قبل ستّة أشهر عشرة آلاف سطر من منطق الأعمال فوق fast-mcp، لكنتُ اليوم محبوساً على المواصفة القديمة — لأنّ كلفة إعادة الكتابة ستكون عاليةً إلى درجة لا أُقدِم معها على الفعل.

لكن الواقع كان: طبقة fast-mcp من البداية مجرّد مغلّف رفيع. منطق الأعمال يسكن داخل فئات ApplicationTool الفرعيّة. لذا كانت الكلفة الفعليّة لإعادة الكتابة:

  • تغيير الفئة الأساسيّة (مرّة واحدة)
  • إعادة كتابة DSL الخاص بـ schema في كلّ من فئات tool الأربع (~10 أسطر لكلّ منها)
  • تغيير أسلوب الاستدعاء في الاختبارات (آليّ)
  • تغيير سطر mount واحد في routes
  • استبدال URL في docs بحثاً واستبدالاً

الباهظ هو منطق الأعمال. أمّا مغلّفات البروتوكول فينبغي أن تكون رخيصة وقابلة لإعادة الكتابة. هذا أكبر معروف صنعه لي Claude Code قبل ستّة أيّام — لم يحشُ منطق الأعمال داخل callbacks الخاصّة بـ fast-mcp. بل بنى تجريداً نظيفاً اسمه ApplicationTool يستوعب الـ SDK. لذلك بعد ستّة أيّام لم يحتج تبديل الـ SDK سوى لتغيير الفئة الأساسيّة وإعادة كتابة الـ schema.

ما أتاح لي قطع مسار «الدمج الأوّل → ترقية المواصفة → إعادة الكتابة» في 6 أيّام هو ثبات Claude بوصفه النموذج الأساس — وأيّ agent shell كنتُ فيه ليس عاملاً مهمّاً.

الكود المصدري: https://github.com/defi-io/smarts