سرورهای MCP راهی پرطرفدار برای دادن قابلیت «فراخوانی ابزار» به مدل‌های زبانی هستند و
جایگزینی برای فراخوانی ابزار به سبک «سازگار با OpenAI» محسوب می‌شوند. با تبدیل
تعریف ابزارهای MCP (فرمت Antropic) به تعریف ابزارهای سازگار با OpenAI، می‌توانید از
سرورهای MCP به‌همراه درگاه آشا استفاده کنید.

اگر اپلیکیشن TypeScript می‌سازید، بستهٔ
@openrouter/mcp همهٔ این کارها را برایتان انجام می‌دهد — به یک
سرور MCP راه دور وصل می‌شود، یک بار احراز هویت می‌کند و ابزارهایی را برمی‌گرداند که
مستقیم در callModel پخش (spread) می‌کنید. در این مثال، از عهدنامهٔ
کلاینتی MCP شرکت Anthropic برای تعامل با سرور فایل‌سیستم MCP استفاده می‌کنیم — همه با
درگاه آشا زیرِ کاپوت.

تعامل با سرورهای MCP پیچیده‌تر از فراخوانی یک endpoint REST است. پروتکل MCP «داری‌حالت»
(stateful) است و به مدیریت نشست (session) نیاز دارد. مثال زیر از عهدنامهٔ کلاینتی MCP
استفاده می‌کند و با همین وجود نسبتاً پیچیده است.

پیش‌نیازها و راه‌اندازی

برای اجرا، اول بسته‌های زیر را نصب کنید:

Terminal
bash
pip install mcp openai python-dotenv

سپس فایل .env را با کلید آشا بسازید (قالب
sk-asha-…):

ENV
.env
ASHA_API_KEY=sk-asha-...

این مثال فرض می‌کند کلاینتی هستید که سرور فایل‌سیستم را با npx
اجرا می‌کند؛ پس Node.js هم نصب باشد. در سیستم‌عامل مک مسیر /Applications/
فرض شده — روی ویندوز یا لینوکس آن را با پوشهٔ دلخواه‌تان (مثلاً
C:UsersyouDocuments یا /home/you/Documents)
جایگزین کنید.

کد اولیه و پیکربندی مدل

Python
mcp-client.py
import asyncio
from typing import Optional
from contextlib import AsyncExitStack

from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

from openai import OpenAI
from dotenv import load_dotenv
import json

load_dotenv()  # load environment variables from .env

MODEL = "~anthropic/claude-sonnet-latest"

SERVER_CONFIG = {
    "command": "npx",
    "args": [
        "-y",
        "@modelcontextprotocol/server-filesystem",
        "/Applications/",
    ],
    "env": None,
}

شناسهٔ مدل را از فهرست مدل‌های آشا انتخاب کنید؛ مدل باید از
پارامتر tools پشتیبانی کند (برای مدل‌های چت و کد برقرار است).

تبدیل ابزارهای MCP به ابزارهای OpenAI

تابع کمکی زیر تعریف ابزار MCP را به تعریف ابزار سازگار با OpenAI تبدیل می‌کند:

Python
mcp-client.py
def convert_tool_format(tool):
    converted_tool = {
        "type": "function",
        "function": {
            "name": tool.name,
            "description": tool.description,
            "parameters": {
                "type": "object",
                "properties": tool.inputSchema["properties"],
                "required": tool.inputSchema["required"],
            },
        },
    }
    return converted_tool

کلاینتی MCP کامل

و این خودِ کلاینتی است — حدود ۱۰۰ خط ناگزیر. توجه کنید که
SERVER_CONFIG داخل کلاینت ثابت نوشته شده؛ بدیهی است می‌توان آن
را برای سرورهای MCP دیگر پارامتریزه کرد. تنها تفاوت با نسخهٔ OpenRouter، آدرس
base_url است که به https://app.asha-ai.ir/v1/
تغییر کرده:

Python
mcp-client.py
class MCPClient:
    def __init__(self):
        self.session: Optional[ClientSession] = None
        self.exit_stack = AsyncExitStack()
        self.openai = OpenAI(
            base_url="https://app.asha-ai.ir/v1/"
        )

    async def connect_to_server(self, server_config):
        server_params = StdioServerParameters(**server_config)
        stdio_transport = await self.exit_stack.enter_async_context(
            stdio_client(server_params)
        )
        self.stdio, self.write = stdio_transport
        self.session = await self.exit_stack.enter_async_context(
            ClientSession(self.stdio, self.write)
        )

        await self.session.initialize()

        # List available tools from the MCP server
        response = await self.session.list_tools()
        print(
            "nConnected to server with tools:",
            [tool.name for tool in response.tools],
        )

        self.messages = []

    async def process_query(self, query: str) -> str:
        self.messages.append({"role": "user", "content": query})

        response = await self.session.list_tools()
        available_tools = [convert_tool_format(tool) for tool in response.tools]

        response = self.openai.chat.completions.create(
            model=MODEL,
            tools=available_tools,
            messages=self.messages,
        )
        self.messages.append(response.choices[0].message.model_dump())

        final_text = []
        content = response.choices[0].message
        if content.tool_calls is not None:
            tool_name = content.tool_calls[0].function.name
            tool_args = content.tool_calls[0].function.arguments
            tool_args = json.loads(tool_args) if tool_args else {}

            # Execute tool call
            try:
                result = await self.session.call_tool(tool_name, tool_args)
                final_text.append(
                    f"[Calling tool {tool_name} with args {tool_args}]"
                )
            except Exception as e:
                print(f"Error calling tool {tool_name}: {e}")
                result = None

            self.messages.append(
                {
                    "role": "tool",
                    "tool_call_id": content.tool_calls[0].id,
                    "name": tool_name,
                    "content": result.content,
                }
            )

            response = self.openai.chat.completions.create(
                model=MODEL,
                max_tokens=1000,
                messages=self.messages,
            )

            final_text.append(response.choices[0].message.content)
        else:
            final_text.append(content.content)

        return "n".join(final_text)

    async def chat_loop(self):
        """Run an interactive chat loop"""
        print("nMCP Client Started!")
        print("Type your queries or 'quit' to exit.")

        while True:
            try:
                query = input("nQuery: ").strip()
                result = await self.process_query(query)
                print("Result:")
                print(result)
            except Exception as e:
                print(f"Error: {str(e)}")

    async def cleanup(self):
        await self.exit_stack.aclose()


async def main():
    client = MCPClient()
    try:
        await client.connect_to_server(SERVER_CONFIG)
        await client.chat_loop()
    finally:
        await client.cleanup()


if __name__ == "__main__":
    import sys
    asyncio.run(main())

اجرا و خروجی

همهٔ کد بالا را در mcp-client.py ذخیره کنید و اجرا کنید:

Terminal
bash
% python mcp-client.py

Secure MCP Filesystem Server running on stdio
Allowed directories: [ '/Applications' ]

Connected to server with tools: ['read_file', 'read_multiple_files', 'write_file'...]

MCP Client Started!
Type your queries or 'quit' to exit.

Query: Do I have microsoft office installed?

Result:
[Calling tool list_allowed_directories with args {}]
I can check if Microsoft Office is installed in the Applications folder:

Query: continue

Result:
[Calling tool search_files with args {'path': '/Applications', 'pattern': 'Microsoft'}]
Now let me check specifically for Microsoft Office applications:

Query: continue

Result:
I can see from the search results that Microsoft Office is indeed installed on your system.
The search found the following main Microsoft Office applications:

1. Microsoft Excel - /Applications/Microsoft Excel.app
2. Microsoft PowerPoint - /Applications/Microsoft PowerPoint.app
3. Microsoft Word - /Applications/Microsoft Word.app
4. OneDrive - /Applications/OneDrive.app (which includes Microsoft SharePoint integ...

مدل از طریق آشا تصمیم می‌گیرد کدام ابزار را صدا بزند، اسمها و آرگومان‌ها را تولید
می‌کند، ابزار روی سرور MCP اجرا می‌شود و نتیجه به‌صورت پیام «tool» به مدل برگردانده
می‌شود تا پاسخ نهایی را بنویسد.

چرا با آشا؟

  • بدون endpoint اضافه: همین
    /v1/chat/completions استاندارد، پارامتر tools
    را به‌صورت کامل به سمت مدل عبور می‌دهد.
  • یک کلید برای همهٔ مدل‌ها: فقط
    MODEL را عوض کنید تا همین کلاینت روی مدل دیگری از
    فهرست مدل‌های آشا اجرا شود.
  • صورت‌حساب به تومان: هزینهٔ توکن‌ها از گزارشِ مصرفِ واقعیِ
    ارائه‌دهنده شارژ می‌شود؛ در داشبورد فعالیت آشا، هر فراخوانی ابزار و تمامِ راندهای
    رفت‌وبرگشت را می‌بینید.
  • کیف پول شفاف: بدون کارت خارجی؛ به‌اندازهٔ مصرفتان بپردازید و سقف
    هزینه برای تیم بگذارید.

خطاهای رایج

npx یا Node پیدا نمی‌شود

Node.js را نصب کنید تا سرور فایل‌سیستم بتواند با npx اجرا شود.

«No API key» یا خطای احراز هویت

مطمئن شوید ASHA_API_KEY در فایل .env هست،
کلید معتبر است و کیف پول موجودی کافی دارد.

مدل ابزارها را پشتیبانی نمی‌کند

مدلی انتخاب کنید که پارامتر tools را پشتیبانی می‌کند (معمولاً
مدل‌های چت و کد). اگر پاسخ خطای «invalid parameter» گرفتید، شناسه را با
فهرست مدل‌ها تطبیق دهید.

محتوای غیرمتنی نتیجهٔ ابزار

برخی سرورها نتیجهٔ غیرمتنی (مثل تصویر) برمی‌گردانند. در این حالت
result.content را قبل از فرستادن به مدل، به متن قابل‌استفاده
تبدیل (serialize) کنید.

جمع‌بندی

سرورهای MCP به‌همراه آشا بدون هیچ تغییر سمت سرور کار می‌کنند: فقط
base_url را روی
https://app.asha-ai.ir/v1/ بگذارید، کلید
sk-asha-… را وارد کنید و ابزارهای MCP را به فرمت OpenAI تبدیل
کنید. برای انتخاب مدل و دیدن قیمت‌ها به مدل‌های آشا سر بزنید.