Stateless MCP server কী এবং এতে কী পরিবর্তন এসেছে?
MCP revision 2026-07-28 থেকে sessions এবং initialize handshake সরিয়ে দেওয়া হয়েছে। আপনার reverse proxy, health checks, timeouts এবং auth-এর ওপর এর প্রভাব সম্পর্কে বিস্তারিত জানুন।
stateless MCP server কী
একটি stateless MCP server প্রতিটি অনুরোধের মাঝে কোনো client-specific state বা অবস্থা সংরক্ষণ করে না। প্রতিটি অনুরোধের সাথেই protocol version, client capabilities এবং সার্ভারের উত্তর দেওয়ার জন্য প্রয়োজনীয় credentials থাকে, তাই যেকোনো মেশিনের যেকোনো process যেকোনো অনুরোধের উত্তর দিতে পারে। MCP (Model Context Protocol, যা হলো agents-এর tools ব্যবহারের wire format) revision 2026-07-28-এ এটিকে একটি নিয়ম হিসেবে নির্ধারণ করেছে, যা initialize handshake এবং এর অধীনে থাকা HTTP session-কে সরিয়ে দিয়েছে। এখানে সবকিছুই সেই wire-এর server side নিয়ে আলোচনা করা হয়েছে, তাই যদি agent side আপনার কাছে নতুন মনে হয়, তবে AI agents শেখার একটি পর্যায়ক্রমিক পথ-এ সেই লুপটি নিয়ে আলোচনা করা হয়েছে যা কোনো HTTP বিস্তারিত জানার আগেই tool কল করার সিদ্ধান্ত নেয়।
এটাই হলো এর মূল অপারেশনাল উদ্দেশ্য। একটি সার্ভার যা প্রতি client-এর জন্য কোনো তথ্য জমা রাখে না, তা কোনো session affinity ছাড়াই সাধারণ load balancer-এর পেছনে থাকতে পারে, deploy-এর সময় কোনো client-এর সংযোগ বিচ্ছিন্ন না করেই restart করা যায় এবং একটির পরিবর্তে চারটি অভিন্ন process হিসেবে চালানো যায়। একটি session-oriented server অতিরিক্ত যন্ত্রপাতি ছাড়া এর কোনোটিই করতে পারে না।
Model Context Protocol হলো একটি stateless protocol: একটি অনুরোধ প্রসেস করার জন্য প্রয়োজনীয় সমস্ত তথ্য সেই অনুরোধের ভেতরেই থাকে। একটি সার্ভার প্রতিটি অনুরোধ স্বাধীনভাবে প্রসেস করে; পূর্ববর্তী কোনো অনুরোধ থেকে কোনো state অনুমান করা উচিত নয়, এমনকি একই connection বা stream-এর ক্ষেত্রেও নয়।
Stateless বলতে সার্ভারে কিছুই সংরক্ষিত হয় না—এমন নয়। আপনার database, queue এবং cache সবই থাকে। এর অর্থ হলো, connection-এর ওপর protocol কোনো state বহন করে না। তাই server-কে কোনো connection, process বা open socket-কে “এই client-এর চলমান কথোপকথন” হিসেবে বিবেচনা করা যাবে না। এই পার্থক্যটি এমন একটি app-এ সবচেয়ে সহজে দেখা যায়, যে app ইতিমধ্যে নিজের data পরিচালনা করে: openGym-এর read-only MCP server app-এর নিজস্ব database-এ থাকা training history সম্পর্কে প্রশ্নের উত্তর দেয়। ওই storage-এর কোনো অংশই নির্ভর করে না যে নির্দিষ্ট request কোন connection দিয়ে এসেছে।
2026-07-28 সংস্করণে যা অপসারণ করা হয়েছে
2026-07-28 হলো 2026 সালের আগস্ট মাস পর্যন্ত স্পেসিফিকেশনের বর্তমান সংস্করণ। 2025-11-25-এর তুলনায় এটি সেশন সাপোর্ট করার জন্য বিদ্যমান পাঁচটি বিষয় অপসারণ করেছে।
initializeরিকোয়েস্ট এবংnotifications/initializedনোটিফিকেশন। এখন আর কোনো হ্যান্ডশেক নেই (SEP-2575)।Mcp-Session-Idহেডার এবং HTTPDELETEব্যবহার করে সেশন টার্মিনেশন (SEP-2567)।- স্বতন্ত্র HTTP
GETস্ট্রিম, যার মাধ্যমে সার্ভার নোটিফিকেশন পুশ করত। এর পরিবর্তে এখনsubscriptions/listenব্যবহৃত হয়, যা একটি সাধারণ POST রিকোয়েস্ট এবং এর রেসপন্স একটি দীর্ঘস্থায়ী স্ট্রিম। - SSE (server-sent events) স্ট্রিম পুনরায় শুরু করার সুবিধা।
Last-Event-IDহেডার এবং প্রতি-ইভেন্ট আইডি এখন আর নেই, তাই স্ট্রিম বিচ্ছিন্ন হলে চলমান রিকোয়েস্টটি হারিয়ে যায় এবং ক্লায়েন্টকে নতুন রিকোয়েস্ট আইডি দিয়ে নতুন করে রিকোয়েস্ট পাঠাতে হয়। ping,logging/setLevelএবংnotifications/roots/list_changed। লগ লেভেল এখন প্রতিটি রিকোয়েস্টের একটি ফিল্ড, যা_meta-এর মধ্যেio.modelcontextprotocol/logLevelহিসেবে থাকে।
একটি মেথড যোগ করা হয়েছে এবং প্রতিটি সার্ভারকে অবশ্যই এটি ইমপ্লিমেন্ট করতে হবে। server/discover একটি কলের মাধ্যমে সার্ভারের সমর্থিত প্রোটোকল সংস্করণ, সক্ষমতা এবং পরিচয় প্রদান করে। এটি হ্যান্ডশেকের সবচেয়ে কাছাকাছি যা অবশিষ্ট আছে, এবং ক্লায়েন্টদের জন্য এটি কল করা ঐচ্ছিক।
কেন প্রোডাকশনে সেশন ট্রান্সপোর্ট চালানো কঠিন ছিল
2025-11-25 এবং এর পূর্ববর্তী ভার্সনগুলোতে, একটি সার্ভার ইনিশিয়ালাইজেশনের সময় একটি সেশন আইডি তৈরি করতে পারত এবং InitializeResult-এ Mcp-Session-Id হেডারের মাধ্যমে তা ফেরত দিত। এরপর ক্লায়েন্টকে প্রতিটি পরবর্তী রিকোয়েস্টে সেই হেডারটি পাঠাতে হতো। নেগোশিয়েট করা প্রোটোকল ভার্সন এবং ক্লায়েন্টের সক্ষমতার তথ্য সার্ভারের মেমরিতে সেই আইডি দিয়ে সংরক্ষিত থাকত। এই প্রতিটি সিদ্ধান্তের একটি অপারেশনাল খরচ ছিল।
- সার্ভার রিস্টার্ট করলে সেশন টেবিল মুছে যেত। স্পেসিফিকেশন অনুযায়ী, সার্ভারকে কোনো মৃত সেশন আইডি বহনকারী রিকোয়েস্টের উত্তরে
404 Not Foundপাঠাতে হতো এবং ক্লায়েন্টকে নতুন একটিInitializeRequestদিয়ে পুনরায় শুরু করতে হতো। প্রতিটি ডিপ্লয়মেন্ট তখন প্রতিটি সংযুক্ত ক্লায়েন্টের জন্য একটি রিকানেক্ট ইভেন্টে পরিণত হতো। - দ্বিতীয় কোনো রেপ্লিকা প্রথম রেপ্লিকার সেশন সম্পর্কে জানত না। স্কেল আউট করার অর্থ ছিল লোড ব্যালেন্সারে স্টিকি রাউটিং ব্যবহার করা, অথবা এমন একটি শেয়ারড সেশন স্টোর রাখা যা প্রতিটি রেপ্লিকা প্রতিটি রিকোয়েস্টের সময় রিড করত।
- সেশন টেবিলটি এমন মেমরি ছিল যা নিষ্ক্রিয় ক্লায়েন্টদের সংখ্যার সাথে বাড়ত।
DELETEঐচ্ছিক ছিল, এবং যে ক্লায়েন্টরা এটি না পাঠিয়ে সংযোগ বিচ্ছিন্ন করত, তাদের এন্ট্রিগুলো সার্ভারে থেকে যেত। - প্রতিটি কানেকশনে লিস্ট রেজাল্ট ভিন্ন হতে পারত, তাই সার্ভারের সামনে ক্যাশিং করা অনিরাপদ ছিল।
সেশন সরিয়ে ফেলার মাধ্যমে এই চারটি সমস্যাই একসাথে দূর হয়। কোনো কনফিগারেশনে হাত দেওয়ার আগে এই পরিবর্তনটি বোঝা জরুরি।
প্রতিটি অনুরোধে এখন যা থাকে
MCP endpoint-এ প্রতিটি POST অনুরোধ স্বতন্ত্র। প্রোটোকল সংস্করণ এবং ক্লায়েন্ট সক্ষমতা (client capabilities) অনুরোধের বডিতে _meta-এর অধীনে থাকে এবং নির্বাচিত ফিল্ডগুলো HTTP হেডার-এ মিরর করা হয়, যাতে কোনো মধ্যবর্তী মাধ্যম (intermediary) JSON পার্স না করেই সেগুলোর ভিত্তিতে রাউটিং করতে পারে।
POST /mcp HTTP/1.1
Content-Type: application/json
Accept: application/json, text/event-stream
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: get_weather
Authorization: Bearer <access token>
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "get_weather",
"arguments": {"location": "Seattle, WA"},
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientInfo": {"name": "ExampleClient", "version": "1.0.0"},
"io.modelcontextprotocol/clientCapabilities": {}
}
}
}প্রতিটি অনুরোধে io.modelcontextprotocol/protocolVersion এবং io.modelcontextprotocol/clientCapabilities থাকা বাধ্যতামূলক। clientInfo বাধ্যতামূলক নয়, তবে ক্লায়েন্টদের এটি পাঠানো উচিত। কোনো প্রয়োজনীয় ফিল্ড না থাকলে অনুরোধটি ত্রুটিপূর্ণ বলে গণ্য হবে, তাই সার্ভারকে অবশ্যই JSON-RPC ত্রুটি -32602 এবং HTTP 400 Bad Request দিয়ে তা প্রত্যাখ্যান করতে হবে।
প্রতিটি অনুরোধে Mcp-Method হেডার থাকা বাধ্যতামূলক। tools/call, resources/read এবং prompts/get-এর ক্ষেত্রে Mcp-Name থাকা আবশ্যক। হেডারের মান অবশ্যই বডির সাথে মিলতে হবে। যদি সার্ভার বডি প্রসেস করে এবং কোনো অমিল পায়, তবে তাকে অবশ্যই 400 Bad Request এবং ত্রুটি কোড -32020, HeaderMismatch দিয়ে তা প্রত্যাখ্যান করতে হবে। এই নিয়মটি থাকার কারণ হলো, হেডার দেখে রাউটিং করা লোড ব্যালেন্সার এবং বডি প্রসেস করা সার্ভার—এই দুটি ভিন্ন উৎস থেকে তথ্য আসে। আপনি যদি এই হেডারগুলোর ভিত্তিতে রাউটিং বা রেট-লিমিটিং করেন, তবে প্রথমে MCP-Protocol-Version যাচাই করুন: আগের সংস্করণগুলোতে হেডার এবং বডির মধ্যে কোনো যাচাইকরণ হতো না, তাই সেই সংস্করণগুলোতে হেডারের মান নির্ভরযোগ্য নয়।
সংস্করণগত অমিল এখন আর হ্যান্ডশেক ব্যর্থতা নয়, বরং সাধারণ প্রতি-অনুরোধ (per-request) ত্রুটি। যে সার্ভার অনুরোধ করা সংস্করণটি সমর্থন করে না, সেটি 400 Bad Request এবং ত্রুটি -32022, UnsupportedProtocolVersion দিয়ে উত্তর দেবে এবং data.supported-এ তার সমর্থিত সংস্করণগুলোর তালিকা প্রদান করবে। ক্লায়েন্ট সেই তালিকা থেকে একটি বেছে নিয়ে পুনরায় চেষ্টা করবে।
স্টেট যেখানে স্থানান্তরিত হয়েছে: টোকেন, কার্সার এবং সাবস্ক্রিপশন
স্টেট হারিয়ে যায়নি। এটি এমন জায়গায় স্থানান্তরিত হয়েছে যেখানে আপনি তা দেখতে এবং লগ করতে পারবেন।
ক্রেডেনশিয়াল প্রতিটি রিকোয়েস্টের সাথে যুক্ত থাকে। পরিচয় সংযুক্ত করার মতো কোনো সেশন নেই, তাই access token প্রতিটি HTTP কলের সাথে যায় এবং প্রতিবার যাচাই করা হয়। বিস্তারিত নিচে authentication সেকশনে দেওয়া হলো।
কার্সারকে তার নিজস্ব অবস্থান বহন করতে হয়। tools/list, resources/list, prompts/list এবং resources/templates/list-এ পেজিনেশনের জন্য একটি opaque কার্সার স্ট্রিং ব্যবহৃত হয় এবং ক্লায়েন্টদের অবশ্যই এটি পার্স বা পরিবর্তন করা উচিত নয়। সিঙ্গেল-প্রসেস সার্ভারে সেশনের মাধ্যমে মেমরিতে অফসেট রাখা সাধারণ ছিল। সেশন না থাকায়, কার্সারটিকে অবশ্যই এমন হতে হবে যাতে যেকোনো রেপ্লিকা তালিকাটি পুনরায় শুরু করতে পারে। তাই কার্সারের ভেতরে অবস্থান এনকোড করে তা সাইন করুন অথবা এমন স্টোরেজে রাখুন যা সব রেপ্লিকা শেয়ার করে। একটি অবৈধ কার্সারের ক্ষেত্রে -32602 রিটার্ন করা উচিত। এটি সাইন করুন কারণ একটি opaque কার্সার হলো ক্লায়েন্ট-প্রদত্ত ইনপুট যা আপনার কোড ডিকোড করে এবং বিশ্বাস করে।
সাবস্ক্রিপশন একটি কানেকশনের নয়, বরং একটি রিকোয়েস্টের অংশ। যে ক্লায়েন্ট পরিবর্তনের নোটিফিকেশন চায়, সে subscriptions/listen পাঠায় এবং তাতে প্রয়োজনীয় টাইপগুলোর ফিল্টার উল্লেখ করে: toolsListChanged, promptsListChanged, resourcesListChanged এবং resourceSubscriptions। সার্ভার notifications/subscriptions/acknowledged দিয়ে উত্তর দেয় এবং সেই রেসপন্স স্ট্রিমটি খোলা রাখে। যদি স্ট্রিমটি বিচ্ছিন্ন হয়ে যায়, সার্ভার কিছুই ধরে রাখে না এবং ক্লায়েন্ট তা ফিরে পেতে পুনরায় subscriptions/listen পাঠায়।
কল-পরবর্তী অ্যাপ্লিকেশন স্টেট একটি এক্সপ্লিসিট হ্যান্ডেলে পরিণত হয়। যখন সার্ভারের সত্যিই কলের মধ্যবর্তী কোনো কিছু মনে রাখার প্রয়োজন হয়, তখন স্পেসিফিকেশনের সমাধান হলো সার্ভার-জেনারেটেড একটি আইডেন্টিফায়ার যা সাধারণ টুল আর্গুমেন্ট হিসেবে ফেরত পাঠানো হয়। এটি টুল স্কিমাতে দেখা যায়, লগ করা যায় এবং এটি কানেকশনের মাধ্যমে উহ্য থাকে না। যে সার্ভারের পেছনে প্রকৃত ইউজার-ডেটা থাকে, যেমন একটি self-hosted MCP email server, তারা সেশনের পরিবর্তে এই প্যাটার্ন ব্যবহার করে: মেইলবক্স বা ড্রাফট আইডেন্টিফায়ার একটি টুল আর্গুমেন্ট হিসেবে কাজ করে, তাই যেকোনো রেপ্লিকা পরবর্তী কলটি গ্রহণ করতে পারে। অনেক টুলের ক্ষেত্রে কোনো হ্যান্ডেলের প্রয়োজনই হয় না: আপনার নিজস্ব SearXNG instance দ্বারা চালিত একটি সার্চ টুল একটি কুয়েরি গ্রহণ করে এবং ফলাফল ফেরত দেয়; পরবর্তী কলের জন্য কোনো কিছু পুনরায় শুরু করার প্রয়োজন হয় না এবং কোন রেপ্লিকা উত্তর দিয়েছে তা জানারও কোনো কারণ থাকে না।
ডিপ্লয়মেন্ট: রিভার্স প্রক্সি, টাইমআউট এবং হেলথ চেক
MCP এন্ডপয়েন্ট হলো এমন একটি পাথ যা POST রিকোয়েস্ট গ্রহণ করে। বেশিরভাগ ট্রাফিকই হলো ছোট রিকোয়েস্ট এবং JSON রেসপন্স, যা যেকোনো প্রক্সি সহজেই সামলাতে পারে। ব্যতিক্রম হলো স্ট্রিমিং রেসপন্স, যেখানে প্রক্সির ডিফল্ট সেটিংস আপনার প্রতিকূলে কাজ করতে পারে। ল্যাপটপ ডেমো থেকে VPS-এ চলমান MCP সার্ভারে স্থানান্তরের সময় এই বিষয়টিই পরিবর্তিত হয়।
location /mcp {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffering off;
proxy_read_timeout 1h;
proxy_send_timeout 1h;
}proxy_buffering off গুরুত্বপূর্ণ কারণ nginx ডিফল্টভাবে প্রক্সি করা রেসপন্সগুলো বাফার করে রাখে, যা SSE ইভেন্টগুলোকে আটকে রাখে যতক্ষণ না বাফার পূর্ণ হয় বা রেসপন্স শেষ হয়। স্পেসিফিকেশন অনুযায়ী সার্ভারগুলোর SSE রেসপন্সে X-Accel-Buffering: no পাঠানো উচিত এবং nginx সেই হেডারটিকে গুরুত্ব দেয়, তাই একটি সঠিক সার্ভার নিজেই আপনার প্রক্সিকে সঠিক নির্দেশনা দেয়। এই ডিরেক্টিভটি সেট করুন, কারণ এটি আপনার নিয়ন্ত্রণের অংশ।
proxy_read_timeout ডিফল্টভাবে 60 সেকেন্ডে সেট করা থাকে। একটি subscriptions/listen স্ট্রিম যা এর চেয়ে বেশি সময় ধরে নিষ্ক্রিয় থাকে, তা আপনার সার্ভার নয় বরং nginx দ্বারা বন্ধ হয়ে যায়। ফলে আপনার লগে প্রসেসটিকে সুস্থ দেখালেও ক্লায়েন্টের দিকে স্ট্রিমটি বিচ্ছিন্ন হয়ে যায়। এটি শুধুমাত্র MCP লোকেশনের জন্য বাড়ান, পুরো সার্ভারের জন্য নয়। নিষ্ক্রিয় সময়ে কিপ-অ্যালাইভ হিসেবে SSE কমেন্ট লাইন (কোলন দিয়ে শুরু হওয়া লাইন) পাঠানোর জন্য সার্ভারগুলোকে উৎসাহিত করা হয়, যা মধ্যবর্তী প্রক্সিগুলোকে স্ট্রিম টাইমআউট করা থেকে বিরত রাখে।
Caddy-এর ক্ষেত্রে কম কনফিগারেশনের প্রয়োজন হয়। এটি নেটওয়ার্ক দক্ষতার জন্য ডিফল্টভাবে আংশিক বাফার করে এবং রেসপন্সে Content-Type: text/event-stream থাকলে সাথে সাথে তা ফ্লাশ করে দেয়, তাই অতিরিক্ত ডিরেক্টিভ ছাড়াই স্ট্রিমিং কাজ করে।
mcp.example.com {
reverse_proxy 127.0.0.1:8080 {
health_uri /healthz
health_interval 10s
}
}হেলথ চেকটি কোথায় পয়েন্ট করা আছে তা খেয়াল করুন। GET ব্যবহার করে MCP এন্ডপয়েন্টে সরাসরি অ্যাক্টিভ চেক চালাবেন না, কারণ যে সার্ভার শুধুমাত্র এই রিভিশনটি ইমপ্লিমেন্ট করে তা GET এবং DELETE এর বিপরীতে 405 Method Not Allowed উত্তর দেয়, আর Caddy-এর ডিফল্ট হেলথ মেথড হলো GET। ফলে প্রক্সি একটি সম্পূর্ণ সুস্থ ব্যাকএন্ডকেও ডাউন হিসেবে চিহ্নিত করবে। প্রক্সির জন্য /healthz এর মতো একটি সাধারণ পাথ ব্যবহার করুন এবং প্রোটোকল চেক করার জন্য আলাদাভাবে POST রিকোয়েস্ট পাঠান।
curl -sS https://mcp.example.com/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-H 'MCP-Protocol-Version: 2026-07-28' \
-H 'Mcp-Method: server/discover' \
-d '{"jsonrpc":"2.0","id":"health-1","method":"server/discover","params":{"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientCapabilities":{}}}}'একটি supportedVersions লিস্ট বহনকারী 200 মানে হলো প্রসেসটি চালু আছে এবং প্রোটোকল অনুযায়ী কাজ করছে। JSON-RPC এরর -32601 সহ একটি 404 মানে হলো প্রসেসটি চালু আছে কিন্তু server/discover সার্ভ করছে না, যা প্রতিটি 2026-07-28 সার্ভারের জন্য ইমপ্লিমেন্ট করা বাধ্যতামূলক। -32022 সহ একটি 400 মানে হলো আপনার চেকার এমন একটি ভার্সন চেয়েছে যা এই বিল্ড সাপোর্ট করে না, যা ডিপেন্ডেন্সি আপগ্রেডের পর শনাক্ত করা জরুরি। ওপেন সোর্স nginx-এ কোনো অ্যাক্টিভ হেলথ চেক নেই, তাই আপস্ট্রিমে প্যাসিভ max_fails এবং fail_timeout ব্যবহার করুন এবং আপনার মনিটরিং সিস্টেম থেকে প্রোটোকল চেকটি চালান।
রোলিং রিস্টার্টের ক্ষেত্রে এখন শুধুমাত্র চলমান রিকোয়েস্টগুলোই প্রভাবিত হয়। ড্রেইন করুন, চলমান POST রিকোয়েস্টগুলো শেষ হতে দিন, নতুন প্রসেস চালু করুন এবং ক্লায়েন্ট ব্যর্থ হওয়া রিকোয়েস্টগুলো পুনরায় পাঠাবে। যে বিষয়টি আপনি এখনো ড্রপ করবেন তা হলো যেকোনো খোলা subscriptions/listen স্ট্রিম, কারণ সেই স্ট্রিমটি একটি নির্দিষ্ট প্রসেসের সাথে সরাসরি কানেকশন। স্টেটলেসনেস সেশন অ্যাফিনিটি দূর করেছে, কিন্তু বর্তমানে খোলা স্ট্রিমের কানেকশন অ্যাফিনিটি দূর করেনি এবং কোনো রাউটিং রুল এটি ঠিক করতে পারে না। ক্লায়েন্ট এই পার্থক্য বুঝতে পারে: একটি স্ট্রিম যা খালি subscriptions/listen রেজাল্ট দিয়ে শেষ হয় তা সফলভাবে বন্ধ হয়েছে, আর যা তা ছাড়া শেষ হয় তা ড্রপ হয়েছে, যাকে ক্লায়েন্ট পুনরায় কানেক্ট করার কারণ হিসেবে গণ্য করতে পারে।
প্রথমবারের মতো ক্যাশিং সম্ভব হয়। লিস্ট মেথড থেকে আসা রেজাল্টগুলো এখন ttlMs এবং cacheScope বহন করে এবং cacheScope: "public" শেয়ার্ড ইন্টারমিডিয়ারিগুলোকে জানায় যে তারা রেসপন্সটি ক্যাশ করতে পারে। এটি শুধুমাত্র তখনই নিরাপদ যখন লিস্ট রেজাল্টগুলো প্রতি কানেকশনে পরিবর্তিত হয় না, যা সেশন রিমুভ করার সরাসরি ফলাফল।
সেশন না থাকলে কেন অথেন্টিকেশন পরিবর্তিত হয়
সেশন থাকলে initialize-এ একবার অথেন্টিকেট করে সেশন আইডিকে পরবর্তী সবকিছুর প্রমাণ হিসেবে ব্যবহার করা সহজ ছিল। এভাবে ব্যবহৃত সেশন আইডি হলো একটি বিয়ারার ক্রেডেনশিয়াল, যার কোনো অডিয়েন্স, মেয়াদ বা বাতিল করার উপায় নেই এবং এটি আপনার সার্ভার নিজেই তৈরি করে। সেশন সরিয়ে ফেললে সেই শর্টকাটটিও চলে যায় এবং এর পরিবর্তে আরও কঠোর ব্যবস্থা গ্রহণ করতে হয়।
একটি প্রোটেক্টেড MCP সার্ভার OAuth 2.1 রিসোর্স সার্ভার হিসেবে কাজ করে। ক্লায়েন্ট থেকে আসা প্রতিটি HTTP রিকোয়েস্টে অবশ্যই Authorization: Bearer <access token> থাকতে হবে এবং সার্ভার প্রতিটি রিকোয়েস্টে টোকেনটি যাচাই করবে। যাচাইকরণ প্রক্রিয়ায় অডিয়েন্স অন্তর্ভুক্ত থাকে: RFC 8707 (Resource Indicators for OAuth 2.0) অনুযায়ী, সার্ভারকে অবশ্যই নিশ্চিত করতে হবে যে টোকেনটি বিশেষভাবে তার জন্যই ইস্যু করা হয়েছে এবং অন্য কোনো কিছুর জন্য তৈরি টোকেন গ্রহণ বা পাস করা যাবে না। ক্লায়েন্টরা সার্ভারের ক্যানোনিক্যাল URI-সহ resource প্যারামিটার পাঠিয়ে সঠিক অডিয়েন্সের অনুরোধ করে।
ডিসকভারি একটি চ্যালেঞ্জের মাধ্যমে সম্পন্ন হয়। যখন কোনো ব্যবহারযোগ্য টোকেন ছাড়া রিকোয়েস্ট আসে, সার্ভার তখন 401 Unauthorized দিয়ে উত্তর দেয়।
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource",
scope="files:read"ক্লায়েন্ট resource_metadata পড়ে, সেই ডকুমেন্টটি সংগ্রহ করে (RFC 9728, OAuth 2.0 Protected Resource Metadata, যা MCP সার্ভারগুলোকে অবশ্যই ইমপ্লিমেন্ট করতে হবে), অথরাইজেশন সার্ভার খুঁজে বের করে এবং ফ্লোটি সম্পন্ন করে। পর্যাপ্ত পারমিশন নেই এমন একটি বৈধ টোকেন 403 Forbidden এবং সেই অপারেশনের জন্য প্রয়োজনীয় error="insufficient_scope" ও স্কোপের সম্মুখীন হয়।
এটি যেভাবে পরিচালনা করবেন তার দুটি ফলাফল রয়েছে। টোকেন ভ্যালিডেশন এখন প্রতি সেশনে একবারের পরিবর্তে প্রতিটি রিকোয়েস্টে ঘটে, তাই প্রতি কলে ইন্ট্রোস্পেকশন এন্ডপয়েন্টে নেটওয়ার্ক রাউন্ড ট্রিপ আপনার ল্যাটেন্সিতে প্রভাব ফেলবে: এমন টোকেন ব্যবহার করুন যা লোকালি সিগনেচার, অডিয়েন্স এবং মেয়াদের ভিত্তিতে যাচাই করা যায়, অথবা টোকেন অনুযায়ী ভ্যালিডেশনের ফলাফল অল্প সময়ের জন্য ক্যাশ করে রাখুন। আর যেহেতু পরিচয় ধরে রাখার মতো কোনো সেশন নেই, তাই প্রতিটি কলে টোকেন থেকে অথরাইজেশন হিসাব করতে হবে। এটি সেশন মডেলের চেয়ে অনেক বেশি স্বচ্ছ এবং এটি এজেন্ট প্রসেসের বাইরে ক্রেডেনশিয়াল রাখার বৃহত্তর অনুশীলনের সাথে সামঞ্জস্যপূর্ণ, যা keeping secrets out of an AI agent-এ আলোচনা করা হয়েছে। স্কোপ কেবল রিকোয়েস্ট আপনার কাছে পৌঁছানোর পর টোকেনটি কী করতে পারবে তা সীমাবদ্ধ করে; যে মেশিনে এজেন্ট চলে, সেখানে harness plugins that add tool permission rules and budget caps নির্ধারণ করে যে কোন কলগুলো আদৌ করা হবে।
এই রিভিশন সম্পর্কে যা সত্য এবং যা সত্য নয়
উপরের সবকিছু 2026-07-28 রিভিশন বর্ণনা করে। এটি চিরকালের জন্য MCP বর্ণনা করে না এবং গত বছর আপনি যে সার্ভারটি ডেপ্লয় করেছিলেন, এটি তার বর্ণনাও নয়।
2025-11-25 এবং তার আগের রিভিশনের ক্লায়েন্ট ও সার্ভারগুলো এখনও হ্যান্ডশেক মডেল অনুসরণ করে। স্পেসিফিকেশন এই রিভিশনগুলোকে লিগ্যাসি (legacy) হিসেবে অভিহিত করে এবং প্রতি-অনুরোধ মেটাডেটা (per-request-metadata) রিভিশনগুলোকে আধুনিক (modern) হিসেবে গণ্য করে। যে সার্ভার শুধুমাত্র এই রিভিশন সমর্থন করে, সেটি কোনো পুরনো ক্লায়েন্টের সাথে যুক্ত হলে MCP এন্ডপয়েন্টে 405 Method Not Allowed থেকে GET বা DELETE উত্তর দেওয়া উচিত, কোনো Mcp-Session-Id হেডার তৈরি বা ইকো না করে তা উপেক্ষা করা উচিত এবং Last-Event-ID উপেক্ষা করা উচিত কারণ স্ট্রিমগুলো পুনরায় শুরু করা যায় না। একটি ডুয়াল-এরা সার্ভার একই এন্ডপয়েন্টে উভয়ই পরিবেশন করতে পারে: একটি আধুনিক _meta বহনকারী অনুরোধ স্টেটলেসভাবে পরিবেশন করা হয় এবং একটি initialize অনুরোধ পুরনো সেশন সেমান্টিকস নির্বাচন করে।
তাই এর কোনো কিছু বিশ্বাস করার আগে রিভিশন স্ট্রিংটি যাচাই করে নিন। যদি আপনার SDK এখনও initialize পাঠায়, তবে আপনার ডেপ্লয়মেন্টের জন্য সেশনগুলো এখনও কার্যকর এবং উপরে উল্লিখিত সেশন-সংক্রান্ত সমস্যাগুলো আপনাকে ম্যানেজ করতে হবে। ক্লায়েন্ট সাইডের ক্ষেত্রেও একই কথা প্রযোজ্য: আপনার নিজের বক্সে থাকা একটি এজেন্ট প্রসেস, যেমন running a coding agent on a VPS-এ বর্ণিত সেটআপ, শুধুমাত্র তখনই এই অর্থে স্টেটলেস হবে যদি এটি যে লাইব্রেরি ব্যবহার করে তা একটি আধুনিক রিভিশন সমর্থন করে। আপনার রানটাইম যে ভার্সন নেগোশিয়েট করে তা পড়ুন, তারপর স্পেসিফিকেশনের সংশ্লিষ্ট রিভিশনটি পড়ুন এবং এই পেজটিকে সামগ্রিক প্রোটোকলের পরিবর্তে একটি নির্দিষ্ট রিভিশনের বর্ণনা হিসেবে বিবেচনা করুন।
FAQ
Stateless MCP server মানে কি আমি কোনো কিছু সংরক্ষণ করতে পারব না?
না। Stateless শব্দটি প্রোটোকলের ক্ষেত্রে প্রযোজ্য, আপনার অ্যাপ্লিকেশনের ক্ষেত্রে নয়। ডেটাবেস, কিউ (queue) এবং ক্যাশ (cache) আগের মতোই কাজ করবে। যা পরিবর্তিত হয় তা হলো, একাধিক কল জুড়ে থাকা স্টেট (state) অবশ্যই একটি স্পষ্ট আইডেন্টিফায়ার দ্বারা রেফারেন্স করতে হবে, যা ক্লায়েন্ট প্রতিটি অনুরোধে পাঠাবে; যেমন টুল আর্গুমেন্টে সার্ভার কর্তৃক প্রদত্ত একটি হ্যান্ডেল। আপনি সংযোগ থেকে কোনো কনটেক্সট অনুমান করতে পারবেন না: স্পেসিফিকেশন অনুযায়ী, একটি সার্ভার সক্ষমতা, প্রোটোকল সংস্করণ বা ক্লায়েন্ট পরিচিতি নির্ধারণের জন্য একই সংযোগের পূর্ববর্তী অনুরোধের ওপর নির্ভর করতে পারবে না, কারণ প্রতিটি অনুরোধে _meta-এর মাধ্যমে সেই তথ্য সরবরাহ করা হয়।
আমার লোড ব্যালেন্সারে কি এখনো sticky session প্রয়োজন?
সাধারণ অনুরোধের জন্য প্রয়োজন নেই। 2026-07-28 রিভিশনের অধীনে প্রতিটি POST তার নিজস্ব প্রোটোকল সংস্করণ, সক্ষমতা এবং ক্রেডেনশিয়াল বহন করে, তাই যেকোনো রেপ্লিকা যেকোনো অনুরোধের উত্তর দিতে পারে এবং round-robin পদ্ধতি ব্যবহার করা নিরাপদ। একমাত্র দীর্ঘস্থায়ী বিষয়টি হলো subscriptions/listen রেসপন্স স্ট্রিম, যা একটি একক প্রসেসের সাথে একটি উন্মুক্ত সংযোগ। সেই প্রসেস শেষ হলে এটিও শেষ হয়ে যায় এবং ক্লায়েন্ট পুনরায় সংযোগ স্থাপনের জন্য subscriptions/listen পাঠায়। এটি সেশন অ্যাফিনিটির পরিবর্তে সংযোগের স্থায়িত্বকাল, এবং কোনো রাউটিং রুল এটি প্রতিরোধ করতে পারে না।
Mcp-Session-Id এবং HTTP GET স্ট্রিমের কী হয়েছে?
SEP-2567 এবং SEP-2575-এর অধীনে 2026-07-28 রিভিশনে উভয়ই সরিয়ে ফেলা হয়েছে। যে সার্ভার শুধুমাত্র এই রিভিশনটি ইমপ্লিমেন্ট করে, সে MCP এন্ডপয়েন্টে 405 Method Not Allowed থেকে GET এবং DELETE-এর উত্তর দেবে এবং Mcp-Session-Id হেডারকে ইকো না করে উপেক্ষা করবে। সার্ভার-ইনিশিয়েটেড পরিবর্তন বিজ্ঞপ্তিগুলো এখন একটি স্বতন্ত্র GET স্ট্রিমের পরিবর্তে subscriptions/listen অনুরোধের রেসপন্স স্ট্রিম দিয়ে প্রবাহিত হয়। যে সার্ভারগুলোকে পুরোনো ক্লায়েন্টদের সেবা দেওয়া চালিয়ে যেতে হবে, তারা এই রিভিশনের পাশাপাশি পূর্ববর্তী রিভিশনের আচরণও ইমপ্লিমেন্ট করবে।
হ্যান্ডশেক ছাড়া MCP সার্ভারের হেলথ চেক কীভাবে করব?
দুই স্তরের পদ্ধতি ব্যবহার করুন। প্রক্সির অ্যাক্টিভ চেকটিকে আপনার অ্যাপ্লিকেশনের একটি সাধারণ HTTP পাথের দিকে নির্দেশ করুন, কারণ MCP এন্ডপয়েন্টে একটি GET সঠিকভাবে 405 রিটার্ন করে এবং একটি সুস্থ ব্যাকএন্ডকে ডাউন হিসেবে চিহ্নিত করতে পারে। এরপর server/discover POST করে প্রোটোকলটি পরীক্ষা করুন, যা প্রতিটি 2026-07-28 সার্ভারকে অবশ্যই ইমপ্লিমেন্ট করতে হবে এবং নিশ্চিত করুন যে উত্তরটি HTTP 200 এবং আপনার ক্লায়েন্টদের ব্যবহৃত প্রোটোকল সংস্করণ প্রদর্শন করছে। JSON-RPC এরর -32601 সহ একটি 404 মানে হলো প্রসেসটি চলছে কিন্তু সেই মেথডটি সার্ভ করছে না, এবং -32022 সহ একটি 400 মানে হলো আপনার অনুরোধ করা সংস্করণটি সেই বিল্ডে সমর্থিত নয়।