
আগের পোস্টে নিজে হাতে বানানো একটা এজেন্টকে trigger-action platform-এর সাথে তুলনা করেছিলাম। এই সিরিজের একদম প্রথম পোস্ট থেকে একটা লাইন এখনও ব্যাখ্যা করা হয়নি: MCP, যেটাকে এজেন্ট হঠাৎ কাজের হয়ে ওঠার একটা কারণ হিসেবে উল্লেখ করা হয়েছিল, কিন্তু আসলে এটা কী তা বলা হয়নি। এবার সেটা ঠিকভাবে খুলে দেখার সময়।
MCP, বা Model Context Protocol, একটা open standard যা দিয়ে একটা এআই মডেলকে tool আর data source-এর সাথে জোড়া যায়, এমনভাবে যে একটা tool একবারই বানানো হয়, MCP server হিসেবে, আর MCP-সঙ্গত যেকোনো অ্যাপ্লিকেশন সেই নির্দিষ্ট জোড়ার জন্য আলাদা কোনো কাস্টম integration কোড ছাড়াই সেটা ব্যবহার করতে পারে। Anthropic এটা এনেছিল ২০২৪-এর নভেম্বরে; কয়েক মাসের মধ্যেই OpenAI আর Google দুটোই এটা গ্রহণ করে, আর ২০২৫-এর ডিসেম্বরে Anthropic নিজেই এই প্রোটোকলটা দান করে দেয় একটা নিরপেক্ষ Linux Foundation প্রজেক্টে, Agentic AI Foundation, যেটা Block আর OpenAI-এর সাথে মিলে co-founded। “এখনই কেন” — এক বাক্যে এটাই: একটা কোম্পানির নিয়ন্ত্রণে থাকা প্রোটোকল পুরো ইন্ডাস্ট্রি একসাথে রক্ষণাবেক্ষণ করে এমন infrastructure হয়ে গেছে।
এটা আসলে কোন সমস্যা সমাধান করে
এই সিরিজে এতদিন দেখানো প্রতিটা tool — get_weather, convert_currency, add_expense — সরাসরি একটা কোডের ভেতরে বানানো হয়েছিল, ঠিক সেই একটা প্রজেক্টের জন্য যতটুকু দরকার ততটুকু আকারে। একটা প্রজেক্টের জন্য এটা ঠিকই আছে। কিন্তু দ্বিতীয় কোনো প্রজেক্ট একই আবহাওয়ার lookup চাইলেই সমস্যা শুরু: ফাংশন কপি করো, schema কপি করো, description কপি করো, তারপর দুটো কপি চিরকাল হাতে মিলিয়ে রাখো। যত tool আর যত প্রজেক্ট সেটা চায়, কাস্টম integration-এর সংখ্যা দুটোর সাথেই একসাথে বাড়তে থাকে।
flowchart TB
subgraph "MCP ছাড়া: প্রতি জোড়ার জন্য আলাদা কোড"
App1["App ১"] -->|কাস্টম কোড| ToolA["Tool A"]
App1 -->|কাস্টম কোড| ToolB["Tool B"]
App2["App ২"] -->|কাস্টম কোড| ToolA
App2 -->|কাস্টম কোড| ToolB
end
subgraph "MCP দিয়ে: একটা server, যেকোনো client"
AppX["App ১"] -->|MCP| ServerA["Tool A, MCP server হিসেবে"]
AppY["App ২"] -->|MCP| ServerA
AppX -->|MCP| ServerB["Tool B, MCP server হিসেবে"]
AppY -->|MCP| ServerB
endFlow-এর সারসংক্ষেপ: MCP ছাড়া, প্রতিটা app-এর প্রতিটা tool-এর জন্য নিজস্ব কাস্টম integration কোড লাগে, তাই app আর tool দুটোই বাড়লে সংযোগের সংখ্যা একসাথে বাড়ে। MCP দিয়ে, প্রতিটা tool একবারই একটা server হিসেবে প্রকাশ করা হয়, আর protocol-টা বোঝে এমন যেকোনো app সেই জোড়ার জন্য নতুন কোনো integration কোড না লিখেই সেটা ব্যবহার করতে পারে।

দুই পাশে আসলে কী থাকে
একটা MCP server হলো ছোট্ট একটা প্রোগ্রাম, যা একটা standard protocol-এর মাধ্যমে এক বা একাধিক tool (আর চাইলে data “resource” আর পুনর্ব্যবহারযোগ্য prompt-ও) প্রকাশ করে। একটা MCP client থাকে কোনো অ্যাপ্লিকেশনের ভেতরে — Claude Desktop, Claude Code, কোনো IDE, আপনার নিজের এজেন্ট — আর জানে কীভাবে spec মেনে চলা যেকোনো server-এর সাথে কথা বলতে হয়। Host হলো মানুষ আসলে যে অ্যাপ্লিকেশন ব্যবহার করে; এতে এক বা একাধিক client থাকে, প্রতিটা একটা করে server-এর সাথে যুক্ত।
একটা রেস্টুরেন্টের কথা ভাবুন। Host হলো রেস্টুরেন্টটাই, যেখানে আপনি বসে খাচ্ছেন। Client হলো ওয়েটার, যে জানে কোন সাপ্লায়ারের কাছ থেকে কী আনতে হয়। আর Server হলো প্রতিটা আলাদা সাপ্লায়ার — সবজির দোকান, মাছের আড়ত — যাদের প্রত্যেকেরই অর্ডার নেওয়ার একই রকম পদ্ধতি, যে রেস্টুরেন্টের ওয়েটারই আসুক না কেন। একই সাপ্লায়ার শহরের যেকোনো রেস্টুরেন্টকে সরবরাহ করতে পারে, কারণ অর্ডার করার নিয়মটা সবার জন্য একই।
এর কোনোটাই tool calling নিয়ে পোস্টে যা শেখানো হয়েছিল তা বদলে দেয় না। MCP দিয়ে প্রকাশ করা একটা tool মডেলের কাছে এখনও নাম, description আর schema হিসেবেই পৌঁছায়, ঠিক সেই পোস্টে হাতে বানানো tools array-এর মতোই — MCP শুধু সেই definition প্রকাশ আর নেওয়ার একটা standard উপায় ঠিক করে দেয়, প্রতিটা প্রজেক্টে আলাদা করে লেখার বদলে। Prompt engineering নিয়ে পোস্টের স্পষ্ট, ভালোভাবে সীমাবদ্ধ description লেখার প্রতিটা নিয়ম MCP-এর tool-এর জন্যও ঠিক ততটাই প্রযোজ্য, যতটা সরাসরি লেখা একটার জন্য।
একটা আসল MCP Server
npm install @modelcontextprotocol/sdk zod দিয়ে SDK ইনস্টল করুন, ফাইলটা mcp/weather-server.ts নামে সেভ করুন, আর npx tsx mcp/weather-server.ts দিয়ে চালান। একা চালালে এটা শুধু বসে থাকবে, শোনার জন্য — পরের অংশে দেখানো হবে আসলে এর সাথে কীভাবে কথা বলতে হয়।
// mcp/weather-server.ts
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { z } from 'zod';
const weatherData: Record<string, { condition: string; temp_c: number }> = {
Dhaka: { condition: 'humid and cloudy', temp_c: 31 },
Chittagong: { condition: 'light rain', temp_c: 29 },
};
const server = new McpServer({ name: 'weather-server', version: '1.0.0' });
server.registerTool(
'get_weather',
{
title: 'Get Weather',
description: 'Get the current weather condition and temperature in Celsius for a named city.',
inputSchema: { city: z.string() },
},
async ({ city }) => {
const data = weatherData[city];
const result = data ? { city, ...data } : { error: `no weather data for ${city}` };
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
},
);
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
}
main();
Tool calling নিয়ে পোস্টের get_weather tool-টার সাথে মিলিয়ে দেখুন: একই নাম, একই description, একই একটা input field। এখানে inputSchema হাতে লেখা JSON Schema-র বদলে Zod validator-এর একটা সাধারণ object নেয়, আর SDK নিজেই সেটা রূপান্তর করে দেয় — এজন্যই পরে tools/list-এ একটা ঠিকঠাক JSON Schema দেখা যায়, সাথে $schema field, হাতে টাইপ করা না, generate করা। Tool-টা stdin আর stdout দিয়ে কথা বলে StdioServerTransport ব্যবহার করে, এজন্যই সরাসরি চালালে এই process-টাকে কিছুই করছে না বলে মনে হয়: এটা কোনো client-এর সংযোগ আর protocol-এ কথা বলার অপেক্ষায় আছে, নিজে থেকে terminal-এ কিছু প্রিন্ট করছে না।

আসলে এর সাথে কথা বলা
Claude Desktop, Claude Code, কোনো IDE-এর মতো একটা আসল MCP host এটাকে connection হিসেবে যোগ করার পর নিজেই এই process চালু করে আর পুরো কথোপকথন সামলে নেয়। পুরো host ইনস্টল না করে protocol-টা নিজের চোখে কাজ করতে দেখতে, একই SDK-তে একটা client-ও আছে, যা সরাসরি server চালু করে তার সাথে কথা বলতে পারে:
// mcp/test-client.ts
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
async function main() {
const transport = new StdioClientTransport({ command: 'npx', args: ['tsx', 'mcp/weather-server.ts'] });
const client = new Client({ name: 'test-client', version: '1.0.0' });
await client.connect(transport);
const tools = await client.listTools();
console.log(tools);
const result = await client.callTool({ name: 'get_weather', arguments: { city: 'Dhaka' } });
console.log(result);
await client.close();
}
main();
এটা চালালে weather-server.ts একটা child process হিসেবে চালু হয়, আসল MCP handshake হয়, ঠিক একটা tool-এর তালিকা আসে, Zod schema JSON Schema-তে বদলানো অবস্থায়, আর ঢাকার আবহাওয়া ফেরত আসে standard content আকারে — host অ্যাপ্লিকেশনগুলো যে একই mechanism ব্যবহার করে, শুধু চ্যাট উইন্ডোর বদলে একটা script দিয়ে চালানো।
যে ভুলে ছোট fix না, পুরো অংশ নতুন করে লিখতে হলো
weather-server.ts-এর প্রথম সংস্করণ ফাইলের একদম top-level-এ খালি await server.connect(transport); দিয়ে শেষ হতো। Type-check ঠিকই পাস করেছিল। Client দিয়ে চালাতে গিয়ে সংযোগ কিছু হওয়ার আগেই বন্ধ হয়ে গেল। আসল কারণটা MCP-র সাথে সম্পর্কিত না: এই ফাইলটা যেভাবে চালানো হয় তাতে এটা এমন একটা module format-এ রূপান্তরিত হয় যা top-level await সমর্থন করে না, তাই server শোনার সুযোগ পাওয়ার আগেই process ক্র্যাশ করে গেল। একই দুই লাইন একটা async function main()-এর ভেতরে মুড়ে ডাকলেই পুরোটা ঠিক হয়ে গেল — এটা protocol-এর কোনো বিষয় না, module system-এর একটা খুঁটিনাটি, কিন্তু ঠিক সেই জায়গায় ঘটে যেখানে একটা MCP server-এর entrypoint সাধারণত বসে, তাই আগে থেকে জানা থাকা ভালো।
MCP নিয়ে সাধারণ ভুল
- Entrypoint-এ top-level
awaitলেখা। Server চালু করার কোড একটা async function-এর ভেতরে মুড়ুন, এখানেmain()যেভাবে করেছে, যাতে module system যেভাবেই রূপান্তর করুক না কেন ফাইলটা একইভাবে চলে। - MCP-তে অস্পষ্ট description-কে ছোট সমস্যা ধরে নেওয়া, সরাসরি লেখার চেয়ে। এটা prompt engineering নিয়ে পোস্টে আলোচনা করা সেই একই description field — একটা খারাপ বাক্য এখন শুধু একটা প্রজেক্ট না, এই server-এর সাথে যুক্ত হওয়া প্রতিটা প্রজেক্টকেই প্রভাবিত করে।
- কী প্রকাশ করছে তা না দেখে তৃতীয় পক্ষের কোনো MCP server-এর সাথে যুক্ত হওয়া। কোনো tool-এর description এমন লেখা যা সেই server নিয়ন্ত্রণ করে, আর মডেল সেটা নির্দেশ হিসেবেই পড়ে, ঠিক আপনার লেখাটার মতোই। অচেনা কোনো server-এর tool-কে অবিশ্বস্ত অন্য যেকোনো input-এর মতোই সতর্কতার সাথে যাচাই করে নিন, জুড়ে দেওয়ার আগে।
- যে tool-এর server আগে থেকেই আছে, সেটার জন্য নতুন করে বানানো। GitHub, Slack, বিভিন্ন database-এর মতো সাধারণ সেবার জন্য public registry-তে আগে থেকেই server আছে; নতুন করে লেখার আগে একবার দেখে নিন।
এরপর
MCP সমাধান করে এজেন্ট কীভাবে তার নিজের না-হওয়া একটা tool-এ পৌঁছায়। হাতে থাকা tool নিয়ে এজেন্ট নিজে কতদূর একা যেতে পারবে — একবারে একটা ধাপ, মানুষ পাশে থেকে, নাকি কয়েকটা ধাপ কোনো নজরদারি ছাড়াই — এটা আলাদা একটা প্রশ্ন, আর উত্তর দেওয়ার মতো পরেরটা।
প্রায়শই জিজ্ঞাসিত প্রশ্নাবলী (FAQ)
MCP কি এই সিরিজের আগের tool-calling mechanism-এর বদলি?
না। MCP দিয়ে প্রকাশ করা একটা tool মডেলের কাছে এখনও নাম, description আর schema হিসেবেই পৌঁছায়, ঠিক tool-calling পোস্টে হাতে বানানো tools array-এর মতো। MCP সেই definition প্রকাশ আর নেওয়ার একটা standard উপায়, ভেতরে আলাদা কোনো mechanism না।
নিজের MCP server বানাতে হবে কি?
শুধু এমন কোনো tool-এর জন্য যার server আগে থেকে নেই। Public registry-তে GitHub, Slack-এর মতো সাধারণ সেবার জন্য হাজারো server আগে থেকেই আছে, তাই আগে সেখানে খোঁজা সাধারণত নিজে একটা বানিয়ে রক্ষণাবেক্ষণ করার চেয়ে দ্রুত।
যেকোনো MCP server-এর সাথে যুক্ত হওয়া কি নিরাপদ?
এমনিতে না। কোনো server-এর tool description এমন লেখা যা সেই server নিয়ন্ত্রণ করে, আর মডেল সেই লেখাটা নির্দেশ হিসেবেই পড়ে, ঠিক কোনো বিশ্বস্ত prompt-এর মতোই। অচেনা কোনো server আসলে কী প্রকাশ করছে, যুক্ত করার আগে সেটা দেখে নিন — আপনার নিয়ন্ত্রণে নেই এমন যেকোনো input-এর জন্যই এই সতর্কতা প্রযোজ্য।
কাস্টম ওয়েব অ্যাপ বা এআই অটোমেশন বানাতে চান?
আমি স্টার্টআপ ও আধুনিক ব্যবসার জন্য স্কেলেবল ফুল-স্ট্যাক ওয়েব অ্যাপ্লিকেশন (Next.js, React, Node, PostgreSQL) এবং ইন্টেলিজেন্ট এআই এজেন্ট সলিউশন তৈরি করি। আপনার প্রজেক্টের আইডিয়া নিয়ে কথা বলা যাক!
আর্টিকেলটি কি আপনার ভালো লেগেছে?
তন্ময়ের কাজকে সাপোর্ট করতে তালি (Clap) দিয়ে উৎসাহিত করুন!
জাহিদ হাসান তন্ময়
সফটওয়্যার ডেভেলপার
MERN ফুল-স্ট্যাক ডেভেলপার এবং AI এজেন্ট ডেভেলপার, ঢাকা, বাংলাদেশ। ওয়েব ডেভেলপমেন্ট, রিঅ্যাক্ট, লারাভেল এবং আমার লার্নিং জার্নি নিয়ে লিখছি।