[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"article-sozdanie-pervogo-mcp-servera-prakticheskoe-rukovodstvo":3},{"article":4,"author":50},{"id":5,"category_id":6,"title":7,"slug":8,"excerpt":9,"content_md":10,"content_html":11,"locale":12,"author_id":13,"published":14,"published_at":15,"meta_title":7,"meta_description":16,"focus_keyword":17,"og_image":18,"canonical_url":18,"robots_meta":19,"created_at":15,"updated_at":15,"tags":20,"category_name":30,"related_articles":31},"da000000-0000-0000-0000-000000000011","a0000000-0000-0000-0000-000000000016","Создание первого MCP-сервера: практическое руководство для разработчиков","sozdanie-pervogo-mcp-servera-prakticheskoe-rukovodstvo","Пошаговое руководство по созданию сервера Model Context Protocol (MCP) на TypeScript и Python, подключению к Claude Desktop или Cursor и развёртыванию в продакшене.","## Что такое Model Context Protocol (MCP)?\n\n**Model Context Protocol (MCP)** — это открытый стандарт, созданный компанией Anthropic, который определяет способ взаимодействия AI-приложений с внешними источниками данных и инструментами. MCP можно сравнить с **USB-C для AI-интеграций**: подобно тому как USB-C предоставляет единый универсальный разъём для зарядки, передачи данных и видеовывода, MCP предоставляет единый универсальный протокол для подключения AI-моделей к базам данных, API, файловым системам и любым другим сервисам. MCP устраняет необходимость создания отдельных интеграций между каждым AI-приложением и каждым инструментом.\n\nВ 2026 году MCP стал доминирующим стандартом AI-коммуникаций. По прогнозам Gartner, **40% корпоративных приложений будут включать AI-агентов к концу 2026 года**, и именно MCP делает это практически возможным в масштабе. До появления MCP подключение AI-модели к N инструментам требовало N отдельных интеграций. При M AI-приложениях и N инструментах требовалось M x N адаптеров. MCP сводит это к M + N: каждое AI-приложение реализует один MCP-клиент, каждый инструмент реализует один MCP-сервер.\n\n## Архитектура MCP: хосты, клиенты, серверы и транспорты\n\nПонимание архитектуры MCP необходимо прежде, чем писать код. Протокол определяет четыре ключевые роли:\n\n| Компонент | Роль | Пример |\n|-----------|------|--------|\n| **Хост** | AI-приложение, с которым взаимодействует пользователь | Claude Desktop, Cursor, VS Code Copilot |\n| **Клиент** | Обработчик протокола MCP внутри хоста | Встроен в хост-приложение |\n| **Сервер** | Предоставляет инструменты, ресурсы и промпты через MCP | Ваш кастомный сервер (то, что мы будем создавать) |\n| **Транспорт** | Коммуникационный слой между клиентом и сервером | stdio, HTTP+SSE, Streamable HTTP |\n\nПоток коммуникации работает следующим образом:\n\n1. **Хост** запускается и инициализирует MCP-**клиент** для каждого настроенного сервера.\n2. **Клиент** подключается к **серверу** через **транспорт** (stdio для локальных, HTTP для удалённых).\n3. **Клиент** отправляет запрос `initialize`, согласовывая версию протокола и возможности.\n4. **Сервер** отвечает списком доступных **инструментов**, **ресурсов** и **промптов**.\n5. Когда AI-модели нужны внешние данные или действия, **клиент** вызывает соответствующий метод сервера.\n6. **Сервер** выполняет операцию и возвращает результаты через сообщения JSON-RPC 2.0.\n\n### Инструменты vs Ресурсы vs Промпты\n\nMCP-серверы могут предоставлять три типа возможностей:\n\n- **Инструменты (Tools)** — функции, которые AI-модель может вызывать. Они принимают структурированный ввод и возвращают структурированный вывод. Пример: `query_database(sql: string)` или `send_email(to: string, subject: string, body: string)`.\n- **Ресурсы (Resources)** — данные, которые AI-модель может читать. Идентифицируются URI и возвращают контент. Пример: `file:\u002F\u002F\u002Fpath\u002Fto\u002Fdocument.md` или `postgres:\u002F\u002Flocalhost\u002Fmydb\u002Fusers`.\n- **Промпты (Prompts)** — переиспользуемые шаблоны промптов, предоставляемые сервером. Помогают стандартизировать взаимодействие AI с доменом сервера. Пример: шаблон `summarize_ticket` для Jira MCP-сервера.\n\n## Пошагово: создание MCP-сервера на TypeScript\n\nПостроим практический MCP-сервер, предоставляющий инструмент для запросов к базе данных SQLite. Это распространённый сценарий: предоставить AI-модели безопасный доступ только на чтение к данным приложения.\n\n### Шаг 1: Инициализация проекта\n\n```bash\nmkdir mcp-sqlite-server && cd mcp-sqlite-server\nnpm init -y\nnpm install @modelcontextprotocol\u002Fsdk better-sqlite3\nnpm install -D typescript @types\u002Fnode @types\u002Fbetter-sqlite3\nnpx tsc --init\n```\n\nНастройте `tsconfig.json`:\n\n```json\n{\n  \"compilerOptions\": {\n    \"target\": \"ES2022\",\n    \"module\": \"Node16\",\n    \"moduleResolution\": \"Node16\",\n    \"outDir\": \".\u002Fdist\",\n    \"rootDir\": \".\u002Fsrc\",\n    \"strict\": true,\n    \"esModuleInterop\": true,\n    \"declaration\": true\n  },\n  \"include\": [\"src\u002F**\u002F*\"]\n}\n```\n\n### Шаг 2: Реализация сервера\n\nСоздайте `src\u002Findex.ts`:\n\n```typescript\nimport { McpServer } from \"@modelcontextprotocol\u002Fsdk\u002Fserver\u002Fmcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol\u002Fsdk\u002Fserver\u002Fstdio.js\";\nimport Database from \"better-sqlite3\";\nimport { z } from \"zod\";\n\n\u002F\u002F Инициализация базы данных\nconst db = new Database(process.env.DB_PATH || \".\u002Fdata.db\", {\n  readonly: true,\n});\n\n\u002F\u002F Создание MCP-сервера\nconst server = new McpServer({\n  name: \"sqlite-query\",\n  version: \"1.0.0\",\n});\n\n\u002F\u002F Регистрация инструмента для запросов к БД\nserver.tool(\n  \"query\",\n  \"Выполнить SQL-запрос только на чтение к базе данных SQLite\",\n  {\n    sql: z.string().describe(\"SQL SELECT-запрос для выполнения\"),\n  },\n  async ({ sql }) => {\n    \u002F\u002F Безопасность: разрешены только SELECT-запросы\n    const normalized = sql.trim().toUpperCase();\n    if (!normalized.startsWith(\"SELECT\")) {\n      return {\n        content: [\n          {\n            type: \"text\",\n            text: \"Ошибка: разрешены только SELECT-запросы.\",\n          },\n        ],\n        isError: true,\n      };\n    }\n\n    try {\n      const rows = db.prepare(sql).all();\n      return {\n        content: [\n          {\n            type: \"text\",\n            text: JSON.stringify(rows, null, 2),\n          },\n        ],\n      };\n    } catch (error) {\n      return {\n        content: [\n          {\n            type: \"text\",\n            text: `Ошибка запроса: ${(error as Error).message}`,\n          },\n        ],\n        isError: true,\n      };\n    }\n  }\n);\n\n\u002F\u002F Регистрация инструмента для получения списка таблиц\nserver.tool(\n  \"list_tables\",\n  \"Получить список всех таблиц в базе данных со схемами\",\n  {},\n  async () => {\n    const tables = db\n      .prepare(\n        `SELECT name, sql FROM sqlite_master\n         WHERE type='table' AND name NOT LIKE 'sqlite_%'\n         ORDER BY name`\n      )\n      .all();\n\n    return {\n      content: [\n        {\n          type: \"text\",\n          text: JSON.stringify(tables, null, 2),\n        },\n      ],\n    };\n  }\n);\n\n\u002F\u002F Регистрация ресурса для схемы базы данных\nserver.resource(\n  \"schema\",\n  \"sqlite:\u002F\u002Fschema\",\n  async (uri) => {\n    const tables = db\n      .prepare(\n        `SELECT name, sql FROM sqlite_master\n         WHERE type='table' AND name NOT LIKE 'sqlite_%'`\n      )\n      .all();\n\n    return {\n      contents: [\n        {\n          uri: uri.href,\n          mimeType: \"application\u002Fjson\",\n          text: JSON.stringify(tables, null, 2),\n        },\n      ],\n    };\n  }\n);\n\n\u002F\u002F Запуск сервера с транспортом stdio\nasync function main() {\n  const transport = new StdioServerTransport();\n  await server.connect(transport);\n  console.error(\"SQLite MCP-сервер запущен на stdio\");\n}\n\nmain().catch(console.error);\n```\n\n### Шаг 3: Сборка и локальное тестирование\n\n```bash\nnpx tsc\necho '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"initialize\",\"params\":{\"protocolVersion\":\"2025-03-26\",\"capabilities\":{},\"clientInfo\":{\"name\":\"test\",\"version\":\"1.0\"}}}' | node dist\u002Findex.js\n```\n\nВы должны увидеть JSON-RPC ответ с возможностями сервера.\n\n### Шаг 4: Тот же сервер на Python\n\nДля Python-разработчиков — эквивалентная реализация с использованием официального MCP Python SDK:\n\n```python\n# server.py\nimport sqlite3\nimport json\nfrom mcp.server.fastmcp import FastMCP\n\nmcp = FastMCP(\"sqlite-query\")\n\nDB_PATH = \"data.db\"\n\n@mcp.tool()\ndef query(sql: str) -> str:\n    \"\"\"Выполнить SQL-запрос только на чтение к базе данных SQLite.\"\"\"\n    normalized = sql.strip().upper()\n    if not normalized.startswith(\"SELECT\"):\n        raise ValueError(\"Разрешены только SELECT-запросы.\")\n\n    conn = sqlite3.connect(DB_PATH)\n    conn.row_factory = sqlite3.Row\n    try:\n        cursor = conn.execute(sql)\n        rows = [dict(row) for row in cursor.fetchall()]\n        return json.dumps(rows, indent=2, default=str)\n    finally:\n        conn.close()\n\n@mcp.tool()\ndef list_tables() -> str:\n    \"\"\"Получить список всех таблиц в базе данных со схемами.\"\"\"\n    conn = sqlite3.connect(DB_PATH)\n    conn.row_factory = sqlite3.Row\n    try:\n        cursor = conn.execute(\n            \"SELECT name, sql FROM sqlite_master \"\n            \"WHERE type='table' AND name NOT LIKE 'sqlite_%'\"\n        )\n        tables = [dict(row) for row in cursor.fetchall()]\n        return json.dumps(tables, indent=2)\n    finally:\n        conn.close()\n\n@mcp.resource(\"sqlite:\u002F\u002Fschema\")\ndef get_schema() -> str:\n    \"\"\"Вернуть схему базы данных как ресурс.\"\"\"\n    conn = sqlite3.connect(DB_PATH)\n    conn.row_factory = sqlite3.Row\n    try:\n        cursor = conn.execute(\n            \"SELECT name, sql FROM sqlite_master WHERE type='table'\"\n        )\n        return json.dumps([dict(r) for r in cursor.fetchall()], indent=2)\n    finally:\n        conn.close()\n\nif __name__ == \"__main__\":\n    mcp.run(transport=\"stdio\")\n```\n\nУстановка зависимостей и запуск:\n\n```bash\npip install mcp[cli]\npython server.py\n```\n\n## Подключение к Claude Desktop\n\nClaude Desktop нативно поддерживает MCP-серверы. Для подключения отредактируйте файл конфигурации:\n\n**macOS:** `~\u002FLibrary\u002FApplication Support\u002FClaude\u002Fclaude_desktop_config.json`\n**Windows:** `%APPDATA%\\Claude\\claude_desktop_config.json`\n\n```json\n{\n  \"mcpServers\": {\n    \"sqlite-query\": {\n      \"command\": \"node\",\n      \"args\": [\"\u002Fabsolute\u002Fpath\u002Fto\u002Fdist\u002Findex.js\"],\n      \"env\": {\n        \"DB_PATH\": \"\u002Fabsolute\u002Fpath\u002Fto\u002Fyour\u002Fdata.db\"\n      }\n    }\n  }\n}\n```\n\nДля Python-версии:\n\n```json\n{\n  \"mcpServers\": {\n    \"sqlite-query\": {\n      \"command\": \"python\",\n      \"args\": [\"\u002Fabsolute\u002Fpath\u002Fto\u002Fserver.py\"]\n    }\n  }\n}\n```\n\nПерезапустите Claude Desktop. В интерфейсе чата должна появиться иконка молотка, обозначающая доступные MCP-инструменты. Теперь можно задавать Claude вопросы вроде «Какие таблицы есть в базе?» или «Покажи 10 последних пользователей по дате регистрации», и он будет использовать ваш MCP-сервер для прямых запросов к базе данных.\n\n## Подключение к Cursor\n\nCursor также поддерживает MCP-серверы. Добавьте конфигурацию в `.cursor\u002Fmcp.json` в корне проекта:\n\n```json\n{\n  \"mcpServers\": {\n    \"sqlite-query\": {\n      \"command\": \"node\",\n      \"args\": [\".\u002Fdist\u002Findex.js\"],\n      \"env\": {\n        \"DB_PATH\": \".\u002Fdata.db\"\n      }\n    }\n  }\n}\n```\n\nПосле сохранения перезапустите Cursor. MCP-инструменты появятся в панели AI-ассистента и смогут вызываться при генерации и отладке кода.\n\n## Тестирование и отладка MCP-сервера\n\n### Использование MCP Inspector\n\nMCP Inspector — официальный инструмент отладки. Он предоставляет веб-интерфейс для взаимодействия с вашим сервером:\n\n```bash\nnpx @modelcontextprotocol\u002Finspector node dist\u002Findex.js\n```\n\nОткроется браузерный интерфейс по адресу `http:\u002F\u002Flocalhost:5173`, где вы можете:\n\n- Просматривать все зарегистрированные инструменты, ресурсы и промпты\n- Вызывать инструменты с произвольными входными данными и проверять ответы\n- Мониторить поток JSON-RPC сообщений в реальном времени\n- Тестировать обработку ошибок, отправляя некорректные запросы\n\n### Модульное тестирование с SDK\n\nНапишите автоматизированные тесты с использованием in-memory транспорта:\n\n```typescript\nimport { McpServer } from \"@modelcontextprotocol\u002Fsdk\u002Fserver\u002Fmcp.js\";\nimport { InMemoryTransport } from \"@modelcontextprotocol\u002Fsdk\u002FinMemory.js\";\nimport { Client } from \"@modelcontextprotocol\u002Fsdk\u002Fclient\u002Findex.js\";\n\ndescribe(\"SQLite MCP Server\", () => {\n  let client: Client;\n\n  beforeEach(async () => {\n    const [clientTransport, serverTransport] =\n      InMemoryTransport.createLinkedPair();\n    const server = createServer(); \u002F\u002F ваша фабрика серверов\n    await server.connect(serverTransport);\n    client = new Client({ name: \"test\", version: \"1.0\" });\n    await client.connect(clientTransport);\n  });\n\n  it(\"должен вернуть список таблиц\", async () => {\n    const result = await client.callTool({\n      name: \"list_tables\",\n      arguments: {},\n    });\n    expect(result.content[0].text).toContain(\"users\");\n  });\n\n  it(\"должен отклонить не-SELECT запросы\", async () => {\n    const result = await client.callTool({\n      name: \"query\",\n      arguments: { sql: \"DROP TABLE users\" },\n    });\n    expect(result.isError).toBe(true);\n  });\n});\n```\n\n### Распространённые проблемы при отладке\n\n| Проблема | Причина | Решение |\n|----------|---------|---------|\n| Сервер не отображается в Claude Desktop | Ошибка в пути или синтаксисе JSON | Проверьте JSON, используйте абсолютные пути |\n| Ошибки «Tool not found» | Сервер не зарегистрировал инструменты до подключения | Регистрируйте инструменты до вызова `server.connect()` |\n| Таймаут при вызове инструментов | Долгие операции без отчёта о прогрессе | Добавьте уведомления о прогрессе через `server.sendProgress()` |\n| Вывод в stderr ломает протокол | console.log пишет в stdout (stdio транспорт) | Используйте `console.error()` для логирования с stdio |\n| Соединение обрывается при простое | Таймаут транспорта | Реализуйте heartbeat или используйте HTTP-транспорт |\n\n## Лучшие практики для продакшена\n\n1. **Валидация входных данных**: всегда валидируйте и санитизируйте ввод инструментов. Используйте Zod-схемы (TypeScript) или Pydantic-модели (Python) для строгой проверки типов.\n\n2. **Только чтение по умолчанию**: начинайте с доступа только на чтение. Добавляйте возможности записи только при явной необходимости, и всегда требуйте подтверждения для деструктивных операций.\n\n3. **Обработка ошибок**: возвращайте структурированные сообщения об ошибках с `isError: true`. Никогда не раскрывайте внутренние стек-трейсы или строки подключения к БД.\n\n4. **Логирование**: логируйте все вызовы инструментов с временными метками, входными данными и длительностью выполнения. Используйте stderr для логов (не stdout) при stdio-транспорте.\n\n5. **Ограничение частоты**: реализуйте ограничения частоты вызовов для каждого инструмента, чтобы зацикленные AI-агенты не перегрузили бэкенд.\n\n6. **Таймауты**: устанавливайте таймауты на все обработчики инструментов. AI-модель может вызвать инструмент, запускающий дорогой запрос — защитите инфраструктуру.\n\n7. **Разделение окружений**: используйте переменные окружения для всей конфигурации. Никогда не хардкодьте URL баз данных, API-ключи или пути к файлам.\n\n8. **Версионирование**: следуйте семантическому версионированию для MCP-сервера. Хендшейк `initialize` включает согласование версий — ломающие изменения требуют мажорной версии.\n\n## FAQ\n\n**В: Чем MCP отличается от function calling?**\nО: Function calling (используемый OpenAI, Anthropic и другими) определяет инструменты инлайново в каждом API-запросе. MCP выносит определения инструментов в отдельные серверы, которые любой MCP-совместимый хост может обнаружить и использовать. Function calling — на уровне запроса; MCP — постоянный протокол со стейтфул-сессиями.\n\n**В: Можно ли использовать MCP с моделями, отличными от Claude?**\nО: Да. MCP — открытый протокол. OpenAI, Google DeepMind и Microsoft внедрили поддержку MCP в свои платформы по состоянию на начало 2026 года. Любое AI-приложение, реализующее MCP-клиент, может подключиться к любому MCP-серверу.\n\n**В: MCP работает только с локальными инструментами?**\nО: Нет. Хотя stdio-транспорт предназначен для локальных серверов, HTTP+SSE и Streamable HTTP транспорты поддерживают удалённые MCP-серверы. Вы можете развернуть MCP-серверы как облачные сервисы, доступные по сети.\n\n**В: Как MCP обрабатывает аутентификацию?**\nО: Протокол поддерживает OAuth 2.0 для удалённых серверов. Локальные stdio-серверы наследуют контекст безопасности хост-процесса. Для корпоративных развёртываний MCP-шлюзы могут централизовать аутентификацию и авторизацию.\n\n**В: На каких языках можно строить MCP-серверы?**\nО: Официальные SDK существуют для TypeScript, Python, Java, Kotlin, C# и Swift. Сообщество поддерживает SDK для Rust, Go, Ruby и PHP. Протокол языконезависим — любой язык, способный читать\u002Fписать JSON-RPC через stdio или HTTP, может реализовать MCP-сервер.","\u003Ch2 id=\"model-context-protocol-mcp\">Что такое Model Context Protocol (MCP)?\u003C\u002Fh2>\n\u003Cp>\u003Cstrong>Model Context Protocol (MCP)\u003C\u002Fstrong> — это открытый стандарт, созданный компанией Anthropic, который определяет способ взаимодействия AI-приложений с внешними источниками данных и инструментами. MCP можно сравнить с \u003Cstrong>USB-C для AI-интеграций\u003C\u002Fstrong>: подобно тому как USB-C предоставляет единый универсальный разъём для зарядки, передачи данных и видеовывода, MCP предоставляет единый универсальный протокол для подключения AI-моделей к базам данных, API, файловым системам и любым другим сервисам. MCP устраняет необходимость создания отдельных интеграций между каждым AI-приложением и каждым инструментом.\u003C\u002Fp>\n\u003Cp>В 2026 году MCP стал доминирующим стандартом AI-коммуникаций. По прогнозам Gartner, \u003Cstrong>40% корпоративных приложений будут включать AI-агентов к концу 2026 года\u003C\u002Fstrong>, и именно MCP делает это практически возможным в масштабе. До появления MCP подключение AI-модели к N инструментам требовало N отдельных интеграций. При M AI-приложениях и N инструментах требовалось M x N адаптеров. MCP сводит это к M + N: каждое AI-приложение реализует один MCP-клиент, каждый инструмент реализует один MCP-сервер.\u003C\u002Fp>\n\u003Ch2 id=\"mcp\">Архитектура MCP: хосты, клиенты, серверы и транспорты\u003C\u002Fh2>\n\u003Cp>Понимание архитектуры MCP необходимо прежде, чем писать код. Протокол определяет четыре ключевые роли:\u003C\u002Fp>\n\u003Ctable>\u003Cthead>\u003Ctr>\u003Cth>Компонент\u003C\u002Fth>\u003Cth>Роль\u003C\u002Fth>\u003Cth>Пример\u003C\u002Fth>\u003C\u002Ftr>\u003C\u002Fthead>\u003Ctbody>\n\u003Ctr>\u003Ctd>\u003Cstrong>Хост\u003C\u002Fstrong>\u003C\u002Ftd>\u003Ctd>AI-приложение, с которым взаимодействует пользователь\u003C\u002Ftd>\u003Ctd>Claude Desktop, Cursor, VS Code Copilot\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>\u003Cstrong>Клиент\u003C\u002Fstrong>\u003C\u002Ftd>\u003Ctd>Обработчик протокола MCP внутри хоста\u003C\u002Ftd>\u003Ctd>Встроен в хост-приложение\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>\u003Cstrong>Сервер\u003C\u002Fstrong>\u003C\u002Ftd>\u003Ctd>Предоставляет инструменты, ресурсы и промпты через MCP\u003C\u002Ftd>\u003Ctd>Ваш кастомный сервер (то, что мы будем создавать)\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>\u003Cstrong>Транспорт\u003C\u002Fstrong>\u003C\u002Ftd>\u003Ctd>Коммуникационный слой между клиентом и сервером\u003C\u002Ftd>\u003Ctd>stdio, HTTP+SSE, Streamable HTTP\u003C\u002Ftd>\u003C\u002Ftr>\n\u003C\u002Ftbody>\u003C\u002Ftable>\n\u003Cp>Поток коммуникации работает следующим образом:\u003C\u002Fp>\n\u003Col>\n\u003Cli>\u003Cstrong>Хост\u003C\u002Fstrong> запускается и инициализирует MCP-\u003Cstrong>клиент\u003C\u002Fstrong> для каждого настроенного сервера.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Клиент\u003C\u002Fstrong> подключается к \u003Cstrong>серверу\u003C\u002Fstrong> через \u003Cstrong>транспорт\u003C\u002Fstrong> (stdio для локальных, HTTP для удалённых).\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Клиент\u003C\u002Fstrong> отправляет запрос \u003Ccode>initialize\u003C\u002Fcode>, согласовывая версию протокола и возможности.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Сервер\u003C\u002Fstrong> отвечает списком доступных \u003Cstrong>инструментов\u003C\u002Fstrong>, \u003Cstrong>ресурсов\u003C\u002Fstrong> и \u003Cstrong>промптов\u003C\u002Fstrong>.\u003C\u002Fli>\n\u003Cli>Когда AI-модели нужны внешние данные или действия, \u003Cstrong>клиент\u003C\u002Fstrong> вызывает соответствующий метод сервера.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Сервер\u003C\u002Fstrong> выполняет операцию и возвращает результаты через сообщения JSON-RPC 2.0.\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch3>Инструменты vs Ресурсы vs Промпты\u003C\u002Fh3>\n\u003Cp>MCP-серверы могут предоставлять три типа возможностей:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Cstrong>Инструменты (Tools)\u003C\u002Fstrong> — функции, которые AI-модель может вызывать. Они принимают структурированный ввод и возвращают структурированный вывод. Пример: \u003Ccode>query_database(sql: string)\u003C\u002Fcode> или \u003Ccode>send_email(to: string, subject: string, body: string)\u003C\u002Fcode>.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Ресурсы (Resources)\u003C\u002Fstrong> — данные, которые AI-модель может читать. Идентифицируются URI и возвращают контент. Пример: \u003Ccode>file:\u002F\u002F\u002Fpath\u002Fto\u002Fdocument.md\u003C\u002Fcode> или \u003Ccode>postgres:\u002F\u002Flocalhost\u002Fmydb\u002Fusers\u003C\u002Fcode>.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Промпты (Prompts)\u003C\u002Fstrong> — переиспользуемые шаблоны промптов, предоставляемые сервером. Помогают стандартизировать взаимодействие AI с доменом сервера. Пример: шаблон \u003Ccode>summarize_ticket\u003C\u002Fcode> для Jira MCP-сервера.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2 id=\"mcp-typescript\">Пошагово: создание MCP-сервера на TypeScript\u003C\u002Fh2>\n\u003Cp>Построим практический MCP-сервер, предоставляющий инструмент для запросов к базе данных SQLite. Это распространённый сценарий: предоставить AI-модели безопасный доступ только на чтение к данным приложения.\u003C\u002Fp>\n\u003Ch3>Шаг 1: Инициализация проекта\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-bash\">mkdir mcp-sqlite-server &amp;&amp; cd mcp-sqlite-server\nnpm init -y\nnpm install @modelcontextprotocol\u002Fsdk better-sqlite3\nnpm install -D typescript @types\u002Fnode @types\u002Fbetter-sqlite3\nnpx tsc --init\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Настройте \u003Ccode>tsconfig.json\u003C\u002Fcode>:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-json\">{\n  \"compilerOptions\": {\n    \"target\": \"ES2022\",\n    \"module\": \"Node16\",\n    \"moduleResolution\": \"Node16\",\n    \"outDir\": \".\u002Fdist\",\n    \"rootDir\": \".\u002Fsrc\",\n    \"strict\": true,\n    \"esModuleInterop\": true,\n    \"declaration\": true\n  },\n  \"include\": [\"src\u002F**\u002F*\"]\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3>Шаг 2: Реализация сервера\u003C\u002Fh3>\n\u003Cp>Создайте \u003Ccode>src\u002Findex.ts\u003C\u002Fcode>:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-typescript\">import { McpServer } from \"@modelcontextprotocol\u002Fsdk\u002Fserver\u002Fmcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol\u002Fsdk\u002Fserver\u002Fstdio.js\";\nimport Database from \"better-sqlite3\";\nimport { z } from \"zod\";\n\n\u002F\u002F Инициализация базы данных\nconst db = new Database(process.env.DB_PATH || \".\u002Fdata.db\", {\n  readonly: true,\n});\n\n\u002F\u002F Создание MCP-сервера\nconst server = new McpServer({\n  name: \"sqlite-query\",\n  version: \"1.0.0\",\n});\n\n\u002F\u002F Регистрация инструмента для запросов к БД\nserver.tool(\n  \"query\",\n  \"Выполнить SQL-запрос только на чтение к базе данных SQLite\",\n  {\n    sql: z.string().describe(\"SQL SELECT-запрос для выполнения\"),\n  },\n  async ({ sql }) =&gt; {\n    \u002F\u002F Безопасность: разрешены только SELECT-запросы\n    const normalized = sql.trim().toUpperCase();\n    if (!normalized.startsWith(\"SELECT\")) {\n      return {\n        content: [\n          {\n            type: \"text\",\n            text: \"Ошибка: разрешены только SELECT-запросы.\",\n          },\n        ],\n        isError: true,\n      };\n    }\n\n    try {\n      const rows = db.prepare(sql).all();\n      return {\n        content: [\n          {\n            type: \"text\",\n            text: JSON.stringify(rows, null, 2),\n          },\n        ],\n      };\n    } catch (error) {\n      return {\n        content: [\n          {\n            type: \"text\",\n            text: `Ошибка запроса: ${(error as Error).message}`,\n          },\n        ],\n        isError: true,\n      };\n    }\n  }\n);\n\n\u002F\u002F Регистрация инструмента для получения списка таблиц\nserver.tool(\n  \"list_tables\",\n  \"Получить список всех таблиц в базе данных со схемами\",\n  {},\n  async () =&gt; {\n    const tables = db\n      .prepare(\n        `SELECT name, sql FROM sqlite_master\n         WHERE type='table' AND name NOT LIKE 'sqlite_%'\n         ORDER BY name`\n      )\n      .all();\n\n    return {\n      content: [\n        {\n          type: \"text\",\n          text: JSON.stringify(tables, null, 2),\n        },\n      ],\n    };\n  }\n);\n\n\u002F\u002F Регистрация ресурса для схемы базы данных\nserver.resource(\n  \"schema\",\n  \"sqlite:\u002F\u002Fschema\",\n  async (uri) =&gt; {\n    const tables = db\n      .prepare(\n        `SELECT name, sql FROM sqlite_master\n         WHERE type='table' AND name NOT LIKE 'sqlite_%'`\n      )\n      .all();\n\n    return {\n      contents: [\n        {\n          uri: uri.href,\n          mimeType: \"application\u002Fjson\",\n          text: JSON.stringify(tables, null, 2),\n        },\n      ],\n    };\n  }\n);\n\n\u002F\u002F Запуск сервера с транспортом stdio\nasync function main() {\n  const transport = new StdioServerTransport();\n  await server.connect(transport);\n  console.error(\"SQLite MCP-сервер запущен на stdio\");\n}\n\nmain().catch(console.error);\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3>Шаг 3: Сборка и локальное тестирование\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-bash\">npx tsc\necho '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"initialize\",\"params\":{\"protocolVersion\":\"2025-03-26\",\"capabilities\":{},\"clientInfo\":{\"name\":\"test\",\"version\":\"1.0\"}}}' | node dist\u002Findex.js\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Вы должны увидеть JSON-RPC ответ с возможностями сервера.\u003C\u002Fp>\n\u003Ch3>Шаг 4: Тот же сервер на Python\u003C\u002Fh3>\n\u003Cp>Для Python-разработчиков — эквивалентная реализация с использованием официального MCP Python SDK:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-python\"># server.py\nimport sqlite3\nimport json\nfrom mcp.server.fastmcp import FastMCP\n\nmcp = FastMCP(\"sqlite-query\")\n\nDB_PATH = \"data.db\"\n\n@mcp.tool()\ndef query(sql: str) -&gt; str:\n    \"\"\"Выполнить SQL-запрос только на чтение к базе данных SQLite.\"\"\"\n    normalized = sql.strip().upper()\n    if not normalized.startswith(\"SELECT\"):\n        raise ValueError(\"Разрешены только SELECT-запросы.\")\n\n    conn = sqlite3.connect(DB_PATH)\n    conn.row_factory = sqlite3.Row\n    try:\n        cursor = conn.execute(sql)\n        rows = [dict(row) for row in cursor.fetchall()]\n        return json.dumps(rows, indent=2, default=str)\n    finally:\n        conn.close()\n\n@mcp.tool()\ndef list_tables() -&gt; str:\n    \"\"\"Получить список всех таблиц в базе данных со схемами.\"\"\"\n    conn = sqlite3.connect(DB_PATH)\n    conn.row_factory = sqlite3.Row\n    try:\n        cursor = conn.execute(\n            \"SELECT name, sql FROM sqlite_master \"\n            \"WHERE type='table' AND name NOT LIKE 'sqlite_%'\"\n        )\n        tables = [dict(row) for row in cursor.fetchall()]\n        return json.dumps(tables, indent=2)\n    finally:\n        conn.close()\n\n@mcp.resource(\"sqlite:\u002F\u002Fschema\")\ndef get_schema() -&gt; str:\n    \"\"\"Вернуть схему базы данных как ресурс.\"\"\"\n    conn = sqlite3.connect(DB_PATH)\n    conn.row_factory = sqlite3.Row\n    try:\n        cursor = conn.execute(\n            \"SELECT name, sql FROM sqlite_master WHERE type='table'\"\n        )\n        return json.dumps([dict(r) for r in cursor.fetchall()], indent=2)\n    finally:\n        conn.close()\n\nif __name__ == \"__main__\":\n    mcp.run(transport=\"stdio\")\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Установка зависимостей и запуск:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-bash\">pip install mcp[cli]\npython server.py\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"claude-desktop\">Подключение к Claude Desktop\u003C\u002Fh2>\n\u003Cp>Claude Desktop нативно поддерживает MCP-серверы. Для подключения отредактируйте файл конфигурации:\u003C\u002Fp>\n\u003Cp>\u003Cstrong>macOS:\u003C\u002Fstrong> \u003Ccode>~\u002FLibrary\u002FApplication Support\u002FClaude\u002Fclaude_desktop_config.json\u003C\u002Fcode>\n\u003Cstrong>Windows:\u003C\u002Fstrong> \u003Ccode>%APPDATA%\\Claude\\claude_desktop_config.json\u003C\u002Fcode>\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-json\">{\n  \"mcpServers\": {\n    \"sqlite-query\": {\n      \"command\": \"node\",\n      \"args\": [\"\u002Fabsolute\u002Fpath\u002Fto\u002Fdist\u002Findex.js\"],\n      \"env\": {\n        \"DB_PATH\": \"\u002Fabsolute\u002Fpath\u002Fto\u002Fyour\u002Fdata.db\"\n      }\n    }\n  }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Для Python-версии:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-json\">{\n  \"mcpServers\": {\n    \"sqlite-query\": {\n      \"command\": \"python\",\n      \"args\": [\"\u002Fabsolute\u002Fpath\u002Fto\u002Fserver.py\"]\n    }\n  }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Перезапустите Claude Desktop. В интерфейсе чата должна появиться иконка молотка, обозначающая доступные MCP-инструменты. Теперь можно задавать Claude вопросы вроде «Какие таблицы есть в базе?» или «Покажи 10 последних пользователей по дате регистрации», и он будет использовать ваш MCP-сервер для прямых запросов к базе данных.\u003C\u002Fp>\n\u003Ch2 id=\"cursor\">Подключение к Cursor\u003C\u002Fh2>\n\u003Cp>Cursor также поддерживает MCP-серверы. Добавьте конфигурацию в \u003Ccode>.cursor\u002Fmcp.json\u003C\u002Fcode> в корне проекта:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-json\">{\n  \"mcpServers\": {\n    \"sqlite-query\": {\n      \"command\": \"node\",\n      \"args\": [\".\u002Fdist\u002Findex.js\"],\n      \"env\": {\n        \"DB_PATH\": \".\u002Fdata.db\"\n      }\n    }\n  }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>После сохранения перезапустите Cursor. MCP-инструменты появятся в панели AI-ассистента и смогут вызываться при генерации и отладке кода.\u003C\u002Fp>\n\u003Ch2 id=\"mcp\">Тестирование и отладка MCP-сервера\u003C\u002Fh2>\n\u003Ch3>Использование MCP Inspector\u003C\u002Fh3>\n\u003Cp>MCP Inspector — официальный инструмент отладки. Он предоставляет веб-интерфейс для взаимодействия с вашим сервером:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-bash\">npx @modelcontextprotocol\u002Finspector node dist\u002Findex.js\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Откроется браузерный интерфейс по адресу \u003Ccode>http:\u002F\u002Flocalhost:5173\u003C\u002Fcode>, где вы можете:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>Просматривать все зарегистрированные инструменты, ресурсы и промпты\u003C\u002Fli>\n\u003Cli>Вызывать инструменты с произвольными входными данными и проверять ответы\u003C\u002Fli>\n\u003Cli>Мониторить поток JSON-RPC сообщений в реальном времени\u003C\u002Fli>\n\u003Cli>Тестировать обработку ошибок, отправляя некорректные запросы\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch3>Модульное тестирование с SDK\u003C\u002Fh3>\n\u003Cp>Напишите автоматизированные тесты с использованием in-memory транспорта:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-typescript\">import { McpServer } from \"@modelcontextprotocol\u002Fsdk\u002Fserver\u002Fmcp.js\";\nimport { InMemoryTransport } from \"@modelcontextprotocol\u002Fsdk\u002FinMemory.js\";\nimport { Client } from \"@modelcontextprotocol\u002Fsdk\u002Fclient\u002Findex.js\";\n\ndescribe(\"SQLite MCP Server\", () =&gt; {\n  let client: Client;\n\n  beforeEach(async () =&gt; {\n    const [clientTransport, serverTransport] =\n      InMemoryTransport.createLinkedPair();\n    const server = createServer(); \u002F\u002F ваша фабрика серверов\n    await server.connect(serverTransport);\n    client = new Client({ name: \"test\", version: \"1.0\" });\n    await client.connect(clientTransport);\n  });\n\n  it(\"должен вернуть список таблиц\", async () =&gt; {\n    const result = await client.callTool({\n      name: \"list_tables\",\n      arguments: {},\n    });\n    expect(result.content[0].text).toContain(\"users\");\n  });\n\n  it(\"должен отклонить не-SELECT запросы\", async () =&gt; {\n    const result = await client.callTool({\n      name: \"query\",\n      arguments: { sql: \"DROP TABLE users\" },\n    });\n    expect(result.isError).toBe(true);\n  });\n});\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3>Распространённые проблемы при отладке\u003C\u002Fh3>\n\u003Ctable>\u003Cthead>\u003Ctr>\u003Cth>Проблема\u003C\u002Fth>\u003Cth>Причина\u003C\u002Fth>\u003Cth>Решение\u003C\u002Fth>\u003C\u002Ftr>\u003C\u002Fthead>\u003Ctbody>\n\u003Ctr>\u003Ctd>Сервер не отображается в Claude Desktop\u003C\u002Ftd>\u003Ctd>Ошибка в пути или синтаксисе JSON\u003C\u002Ftd>\u003Ctd>Проверьте JSON, используйте абсолютные пути\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>Ошибки «Tool not found»\u003C\u002Ftd>\u003Ctd>Сервер не зарегистрировал инструменты до подключения\u003C\u002Ftd>\u003Ctd>Регистрируйте инструменты до вызова \u003Ccode>server.connect()\u003C\u002Fcode>\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>Таймаут при вызове инструментов\u003C\u002Ftd>\u003Ctd>Долгие операции без отчёта о прогрессе\u003C\u002Ftd>\u003Ctd>Добавьте уведомления о прогрессе через \u003Ccode>server.sendProgress()\u003C\u002Fcode>\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>Вывод в stderr ломает протокол\u003C\u002Ftd>\u003Ctd>console.log пишет в stdout (stdio транспорт)\u003C\u002Ftd>\u003Ctd>Используйте \u003Ccode>console.error()\u003C\u002Fcode> для логирования с stdio\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>Соединение обрывается при простое\u003C\u002Ftd>\u003Ctd>Таймаут транспорта\u003C\u002Ftd>\u003Ctd>Реализуйте heartbeat или используйте HTTP-транспорт\u003C\u002Ftd>\u003C\u002Ftr>\n\u003C\u002Ftbody>\u003C\u002Ftable>\n\u003Ch2 id=\"\">Лучшие практики для продакшена\u003C\u002Fh2>\n\u003Col>\n\u003Cli>\n\u003Cp>\u003Cstrong>Валидация входных данных\u003C\u002Fstrong>: всегда валидируйте и санитизируйте ввод инструментов. Используйте Zod-схемы (TypeScript) или Pydantic-модели (Python) для строгой проверки типов.\u003C\u002Fp>\n\u003C\u002Fli>\n\u003Cli>\n\u003Cp>\u003Cstrong>Только чтение по умолчанию\u003C\u002Fstrong>: начинайте с доступа только на чтение. Добавляйте возможности записи только при явной необходимости, и всегда требуйте подтверждения для деструктивных операций.\u003C\u002Fp>\n\u003C\u002Fli>\n\u003Cli>\n\u003Cp>\u003Cstrong>Обработка ошибок\u003C\u002Fstrong>: возвращайте структурированные сообщения об ошибках с \u003Ccode>isError: true\u003C\u002Fcode>. Никогда не раскрывайте внутренние стек-трейсы или строки подключения к БД.\u003C\u002Fp>\n\u003C\u002Fli>\n\u003Cli>\n\u003Cp>\u003Cstrong>Логирование\u003C\u002Fstrong>: логируйте все вызовы инструментов с временными метками, входными данными и длительностью выполнения. Используйте stderr для логов (не stdout) при stdio-транспорте.\u003C\u002Fp>\n\u003C\u002Fli>\n\u003Cli>\n\u003Cp>\u003Cstrong>Ограничение частоты\u003C\u002Fstrong>: реализуйте ограничения частоты вызовов для каждого инструмента, чтобы зацикленные AI-агенты не перегрузили бэкенд.\u003C\u002Fp>\n\u003C\u002Fli>\n\u003Cli>\n\u003Cp>\u003Cstrong>Таймауты\u003C\u002Fstrong>: устанавливайте таймауты на все обработчики инструментов. AI-модель может вызвать инструмент, запускающий дорогой запрос — защитите инфраструктуру.\u003C\u002Fp>\n\u003C\u002Fli>\n\u003Cli>\n\u003Cp>\u003Cstrong>Разделение окружений\u003C\u002Fstrong>: используйте переменные окружения для всей конфигурации. Никогда не хардкодьте URL баз данных, API-ключи или пути к файлам.\u003C\u002Fp>\n\u003C\u002Fli>\n\u003Cli>\n\u003Cp>\u003Cstrong>Версионирование\u003C\u002Fstrong>: следуйте семантическому версионированию для MCP-сервера. Хендшейк \u003Ccode>initialize\u003C\u002Fcode> включает согласование версий — ломающие изменения требуют мажорной версии.\u003C\u002Fp>\n\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch2 id=\"faq\">FAQ\u003C\u002Fh2>\n\u003Cp>\u003Cstrong>В: Чем MCP отличается от function calling?\u003C\u002Fstrong>\nО: Function calling (используемый OpenAI, Anthropic и другими) определяет инструменты инлайново в каждом API-запросе. MCP выносит определения инструментов в отдельные серверы, которые любой MCP-совместимый хост может обнаружить и использовать. Function calling — на уровне запроса; MCP — постоянный протокол со стейтфул-сессиями.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>В: Можно ли использовать MCP с моделями, отличными от Claude?\u003C\u002Fstrong>\nО: Да. MCP — открытый протокол. OpenAI, Google DeepMind и Microsoft внедрили поддержку MCP в свои платформы по состоянию на начало 2026 года. Любое AI-приложение, реализующее MCP-клиент, может подключиться к любому MCP-серверу.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>В: MCP работает только с локальными инструментами?\u003C\u002Fstrong>\nО: Нет. Хотя stdio-транспорт предназначен для локальных серверов, HTTP+SSE и Streamable HTTP транспорты поддерживают удалённые MCP-серверы. Вы можете развернуть MCP-серверы как облачные сервисы, доступные по сети.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>В: Как MCP обрабатывает аутентификацию?\u003C\u002Fstrong>\nО: Протокол поддерживает OAuth 2.0 для удалённых серверов. Локальные stdio-серверы наследуют контекст безопасности хост-процесса. Для корпоративных развёртываний MCP-шлюзы могут централизовать аутентификацию и авторизацию.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>В: На каких языках можно строить MCP-серверы?\u003C\u002Fstrong>\nО: Официальные SDK существуют для TypeScript, Python, Java, Kotlin, C# и Swift. Сообщество поддерживает SDK для Rust, Go, Ruby и PHP. Протокол языконезависим — любой язык, способный читать\u002Fписать JSON-RPC через stdio или HTTP, может реализовать MCP-сервер.\u003C\u002Fp>\n","ru","b0000000-0000-0000-0000-000000000001",true,"2026-03-28T10:44:33.439258Z","Пошаговое руководство по созданию MCP-сервера на TypeScript и Python. Подключение к Claude Desktop или Cursor, тестирование через MCP Inspector, развёртывание в продакшене.","mcp сервер руководство",null,"index, follow",[21,26],{"id":22,"name":23,"slug":24,"created_at":25},"c0000000-0000-0000-0000-000000000008","AI","ai","2026-03-28T10:44:21.513630Z",{"id":27,"name":28,"slug":29,"created_at":25},"c0000000-0000-0000-0000-000000000002","TypeScript","typescript","Инженерия",[32,38,44],{"id":33,"title":34,"slug":35,"excerpt":36,"locale":12,"category_name":30,"published_at":37},"d0200000-0000-0000-0000-000000000013","Почему Бали становится хабом импакт-технологий Юго-Восточной Азии в 2026 году","pochemu-bali-stanovitsya-khabom-impakt-tekhnologiy-2026","Бали занимает 16-е место среди стартап-экосистем Юго-Восточной Азии. Растущая концентрация Web3-разработчиков, ИИ-стартапов в области устойчивого развития и компаний в сфере эко-тревел-технологий формирует нишу столицы импакт-технологий региона.","2026-03-28T10:44:37.953039Z",{"id":39,"title":40,"slug":41,"excerpt":42,"locale":12,"category_name":30,"published_at":43},"d0200000-0000-0000-0000-000000000012","Защита данных в ASEAN: чек-лист разработчика для мультистранового комплаенса","zashchita-dannykh-asean-chek-list-razrabotchika-komplaens","Семь стран ASEAN имеют собственные законы о защите данных с разными моделями согласия, требованиями к локализации и штрафами. Практический чек-лист для разработчиков мультистрановых приложений.","2026-03-28T10:44:37.944001Z",{"id":45,"title":46,"slug":47,"excerpt":48,"locale":12,"category_name":30,"published_at":49},"d0200000-0000-0000-0000-000000000011","Цифровая трансформация Индонезии на $29 миллиардов: возможности для софтверных компаний","tsifrovaya-transformatsiya-indonezii-29-milliardov-vozmozhnosti-dlya-kompaniy","Рынок IT-услуг Индонезии вырастет с $24,37 млрд в 2025 году до $29,03 млрд в 2026 году. Облачная инфраструктура, искусственный интеллект, электронная коммерция и дата-центры обеспечивают самый быстрый рост в Юго-Восточной Азии.","2026-03-28T10:44:37.917095Z",{"id":13,"name":51,"slug":52,"bio":53,"photo_url":18,"linkedin":18,"role":54,"created_at":55,"updated_at":55},"Open Soft Team","open-soft-team","The engineering team at Open Soft, building premium software solutions from Bali, Indonesia.","Engineering Team","2026-03-28T08:31:22.226811Z"]