В последние годы автоматизация бизнес-процессов действительно часто начиналась с набора Python-скриптов для веб-скрейпинга. Типовой стек — requests + BeautifulSoup: быстро стартует, решает локальную задачу, а потом незаметно превращается в критичный кусок интеграционного контура. Данные извлекаются из DOM без формального контракта, ошибки обрабатываются частично, а результат возвращается в виде строки, которую потом пытаются интерпретировать ERP, CRM или ИИ-агенты.
Пока такой скрипт живёт в ноутбуке разработчика, это терпимо. Но как только он начинает участвовать в корпоративном пайплайне, особенно рядом с Odoo, внутренней CRM, PII или финансовыми данными, архитектурные проблемы становятся вполне практическими: таймауты, невалидные payload, дрейф DOM, отсутствие трассировки, невозможность отката.
С появлением reasoning-моделей нового поколения и ростом требований к безопасности этот разрыв стал ещё заметнее. Если LLM получает доступ к неструктурированному инструменту, который принимает строку и возвращает строку, вы фактически создаёте слабое звено между моделью и бизнес-системой. Именно здесь FastMCP даёт более взрослую архитектуру: строгий контракт, типизация, контролируемые ошибки и предсказуемое поведение.
При обработке payload-ов свыше 500 000 строк используйте асинхронные очереди, например
queue_job, чтобы избежать таймаутов XML-RPC и Out-Of-Memory ошибок на уровне Odoo или FastAPI.
Критические ограничения "Script-Kiddie" скрейпинга в корпоративной среде
В рамках аудитов dlab.md по требованиям GDPR Article 32, EU AI Act, а также национальных контуров вроде RO e-Factura и SAF-T, мы регулярно видим один и тот же сценарий: старый Python-скрейпер подключают напрямую к ИИ-пайплайну или ERP-интеграции, минуя типизацию, аудит и контроль доступа.
Ниже — типичный пример такого кода:
# Устаревший скрейпинг: отсутствие типизации и контроля ошибок
def get_weather(city_name):
url = f"https://google.com/search?q=weather+in+{city_name}"
headers = {"User-Agent": "Mozilla/5.0"}
try:
r = requests.get(url, headers=headers)
if r.status_code == 200:
soup = BeautifulSoup(r.text, 'html.parser')
temp = soup.find("span", id="wob_tm")
condition = soup.find("span", id="wob_dc")
if temp and condition:
return f"Human-readable text: The weather in {city_name} is {condition.text} with {temp.text}C."
else:
return "Failed to parse DOM."На первый взгляд это просто "быстрый utility". На практике — источник нестабильности.
Что здесь ломается в продакшене
- Двусмысленный вход:
city_nameне валидируется и не нормализуется. Для LLM это особенно опасно: строковый параметр легко становится точкой prompt-injection, а для бизнес-логики — источником неоднозначности.Washington— это штат, округ или город? - Неструктурированный вывод: возврат обычной строки вместо JSON-объекта ломает downstream-обработку. Один сервис ждёт
float, другой — код состояния, третий — timestamp. В итоге всё держится на хрупком парсинге текста. - Нет формального протокола ошибок: вызывающая сторона не получает нормальный статус, код ошибки или контекст для retry. Автоматический rollback становится почти невозможным.
- Зависимость от DOM: достаточно одного изменения HTML-разметки на стороне внешнего сайта, и интеграция начинает молча возвращать мусор или
None. - Проблемы с безопасностью и комплаенсом: если такой скрипт участвует в обработке PII, финансовых данных или данных клиентов из ЕС, отсутствие контроля доступа, журналирования и валидации уже конфликтует с практикой Zero-Trust и требованиями GDPR Article 32.
Если у вас в компании уже есть подобные "временные" интеграции, полезно параллельно посмотреть материал Data Protection by Design: Почему ваши скрипты автоматизации обходятся в €20 миллионов. Там мы разбираем, как именно такие скрипты превращаются в источник регуляторной и операционной ответственности.
Этот пример показывает типичный анти-паттерн интеграции, а не рекомендуемый способ работы с внешними источниками. Если скрейпер затрагивает PII, финансовые документы или данные клиентов из ЕС, его нужно выводить из "серой зоны" и переводить на формальный сервисный контракт.
FastMCP: формальный контракт вместо хрупкого скрипта
Переход к архитектуре Model Context Protocol с использованием FastMCP решает проблему не "магией ИИ", а нормальной инженерной дисциплиной. Вместо строк на входе и строк на выходе вы задаёте явную схему данных, правила валидации и предсказуемый формат ошибок.
Это особенно важно, когда инструмент вызывает не человек, а агент или LLM. Модели хорошо работают с формальными контрактами и плохо — с двусмысленными утилитами, которые "обычно возвращают нормальный текст".
# Корпоративный стандарт: FastMCP + Pydantic
from mcp.server.fastmcp import FastMCP
import httpx
from pydantic import BaseModel, Field
mcp = FastMCP("Enterprise_Weather_Adapter")
class WeatherData(BaseModel):
temperature_celsius: float = Field(..., description="Current deterministic temperature")
condition: str = Field(..., description="Standardized weather condition index")
resolution_status: str = Field(default="SUCCESS", description="Internal agent state")
@mcp.tool()
async def fetch_weather_deterministic(lat: float, lon: float) -> WeatherData:
"""
Получение погодных данных по координатам с жесткой типизацией.
Исключает prompt-injection и двусмысленность строковых запросов.
"""
url = f"https://api.open-meteo.com/v1/forecast?latitude={lat}&longitude={lon}¤t_weather=true"
async with httpx.AsyncClient() as client:
response = await client.get(url)
response.raise_for_status()
data = response.json()
return WeatherData(
temperature_celsius=float(data["current_weather"]["temperature"]),
condition=str(data["current_weather"]["weathercode"])
)Здесь важно не только то, что код "современнее". Важно, что он вводит контракт между вызывающей стороной и сервисом.
Что меняется архитектурно
-
Строгая типизация входа и выхода:
latиlon— это числа, а не произвольный текст. Ответ — объектWeatherData, а не строка, которую кто-то потом будет разбирать регулярным выражением. - Предсказуемая обработка ошибок: если параметр невалиден, клиент получает формальную ошибку, а не расплывчатое "что-то пошло не так".
- Изоляция инструмента: MCP отделяет модель от прямого доступа к внутренним системам. Это соответствует Zero-Trust подходу: LLM не должна иметь произвольный доступ к ERP, CRM или файловой системе.
-
Трассируемость: каждый вызов можно логировать, коррелировать по
request_idи включать в аудит. - Готовность к масштабированию: такой сервис уже можно ставить за API gateway, ограничивать rate limit, выносить в отдельный runtime и подключать к очередям.
Если вы строите защищённый контур для ИИ-агентов, рекомендую также посмотреть Раскрытие потенциала Claude 3.5 с помощью безопасных интеграций Model Context Protocol. Там подробнее разобран сам паттерн безопасного подключения моделей к корпоративным данным.
Почему это важно именно для enterprise-среды
В enterprise-среде проблема почти никогда не ограничивается "получить данные с сайта". Обычно вокруг такого скрипта уже есть:
- Odoo или другая ERP;
- внутренняя CRM;
- очереди задач;
- журналирование;
- требования CISO по сегментации сети;
- требования CFO по воспроизводимости операций;
- требования compliance-команды по GDPR, AI Act и локальным фискальным форматам.
И вот здесь разница между скриптом и MCP-сервисом становится принципиальной. Скрипт — это неуправляемый инструмент. MCP-сервис — это интеграционный компонент с контрактом, границами ответственности и понятным жизненным циклом.
[ Odoo Core ] <---- (XML-RPC) ----> [ FastMCP ] <---- (Async HTTP) ----> [ External API ]На практике мы обычно рекомендуем ещё один слой защиты:
[ Odoo ] -> [ Queue / Retry Worker ] -> [ FastMCP ] -> [ API Gateway ] -> [ External Source ]Такая схема снижает риск каскадных отказов. Если внешний API отвечает медленно или нестабильно, Odoo не блокирует пользовательскую сессию и не держит долгую синхронную транзакцию. Это особенно важно для больших импортов, nightly jobs и сценариев, где payload может занимать сотни мегабайт.
Пример формальной ошибки вместо "тихой" поломки
Одна из сильных сторон MCP — нормальный протокол ошибок. Это кажется мелочью, пока вы не начинаете расследовать инцидент в 02:00.
{
"jsonrpc": "2.0",
"error": {
"code": -32602,
"message": "Invalid params",
"data": {
"details": "Input should be a valid number, unable to parse string as a float for coordinate field 'lat'."
}
},
"id": "req_8f7b2c9a"
}Для интегратора это означает три вещи сразу:
- ошибку можно однозначно классифицировать;
- можно настроить retry только там, где он действительно нужен;
- журнал аудита сохраняет контекст инцидента, а не просто факт падения.
Это уже ближе к требованиям реального enterprise-контура, особенно если вы готовитесь к проверкам по безопасности или строите ИИ-интеграции под требования EU AI Act Compliance 2026: Техническое руководство для разработчиков и интеграторов.
Где FastMCP особенно оправдан
Не каждый скрипт нужно немедленно превращать в отдельный сервис. Но есть понятные признаки, что момент уже наступил:
- скрипт вызывается несколькими системами;
- результат влияет на финансовые операции, документы или клиентские данные;
- есть требования к аудиту и журналированию;
- интеграция должна переживать сбои внешнего API;
- инструмент начинает использоваться ИИ-агентом;
- нужно разграничить права доступа и изолировать источник данных.
Если хотя бы два-три пункта уже актуальны, оставаться на уровне ad-hoc скрипта обычно дороже, чем один раз собрать нормальный MCP-бекенд.
Практический сценарий миграции
Обычно мы советуем не переписывать всё сразу. Более безопасный путь выглядит так:
- Инвентаризация скриптов: определить, какие из них реально участвуют в критичных процессах.
- Выделение контрактов: описать входные параметры, формат ответа, коды ошибок, ограничения по времени и объёму payload.
- Обёртка в FastMCP: сначала вынести один инструмент в MCP без изменения бизнес-логики.
- Добавление валидации и аудита: Pydantic-схемы,
request_id, структурированные логи, retry policy. - Изоляция доступа: поставить сервис за gateway, ограничить сетевые маршруты, убрать прямой доступ LLM к внутренним системам.
- Переход на асинхронную обработку: для тяжёлых задач — очередь, worker и контролируемый rollback.
Если миграция идёт параллельно с заменой старой ERP или интеграционного слоя, полезно учитывать риски заранее. По этой теме у нас есть отдельный материал: Миграция с устаревших систем (1C, SAP) на Odoo 19: Оценка рисков и Дорожная карта.
Роль Zero-Trust, rollback и air-gapping
Вокруг ИИ-интеграций часто слишком много разговоров про модели и слишком мало — про границы доступа. А именно они потом определяют масштаб инцидента.
Для сервисов, которые работают с PII, финансовыми документами, внутренними справочниками или данными клиентов, базовый набор требований обычно такой:
- Zero-Trust: ни один агент, сервис или пользователь не получает доступ "по умолчанию";
- Rollback Protocols: каждая операция должна быть либо воспроизводимой, либо обратимой;
- Air-gapping или логическая изоляция: критичные контуры не должны зависеть от прямого доступа модели к внутренней сети;
- Аудит и корреляция событий: без этого невозможно ни расследование, ни доказуемое соответствие требованиям безопасности.
Если вы готовите инфраструктуру к работе на рынках ЕС, имеет смысл начать с Zero-Trust IT Audit: Как обезопасить бизнес-процессы перед выходом на рынки Европы. Это хороший первый шаг до того, как подключать LLM к внутренним данным.
Где dlab.md обычно подключается
Для компаний, работающих на рынках ЕС и Восточной Европы, переход с устаревших скриптов на MCP-архитектуру — это уже не вопрос удобства. Это вопрос управляемости, ответственности и соответствия требованиям безопасности.
В проектах такого класса dlab.md обычно помогает в трёх точках:
- аудит существующих Python-скриптов и интеграций;
- проектирование FastMCP-сервисов с формальным контрактом;
-
связка
Odoo -> очередь -> MCP -> внешние APIс журналированием, retry и контролируемым rollback.
Отдельно отмечу важный момент: если MCP-сервис будет работать рядом с внутренней CRM или ERP, архитектуру нужно проектировать так, чтобы модель никогда не получала прямой доступ к чувствительным данным. По сути, MCP должен быть не "тонким мостом", а контролируемым шлюзом. Этот подход мы подробнее разбирали в статье Подключение ИИ-Агентов к Внутренней CRM: Разбор Архитектуры MCP.
Вывод
Старый Python-скрейпер — это нормальная отправная точка для прототипа. Но плохая основа для enterprise-интеграции, особенно если в контуре уже есть Odoo, ИИ-агенты, PII или требования европейского комплаенса.
FastMCP не решает все проблемы автоматически. Зато он даёт то, чего у скриптов обычно нет: формальный контракт, типизацию, контролируемые ошибки, изоляцию и нормальную операционную модель. А это уже база, на которой можно строить масштабируемый и проверяемый backend, а не надеяться, что очередное изменение DOM "как-нибудь переживём".
Эта статья посвящена рефакторингу Python-скрейперов в MCP-сервисы и не заменяет отдельную проверку правомерности самого сбора данных с конкретных сайтов или API. Перед внедрением FastMCP в контур с PII, финансовыми документами или данными клиентов из ЕС нужно отдельно проверить лицензионные ограничения источника, требования GDPR Article 32 и внутренние политики сетевой изоляции.