Skip to main content
Glama
hossein-finlex

WebMCP Contract Portfolio

WebMCP Contract Portfolio

Коммерческое приложение для страхования финансовых линий, которым Claude управляет напрямую через navigator.modelContext — API WebMCP (Web Model Context Protocol).

Спросите «какие договоры истекают в ближайшие 60 дней?» — и таблица отфильтруется прямо перед вами. Попросите продлить — и срок сдвинется в Postgres и на экране. Ассистент узнаёт, что умеет страница, в рантайме, читая схемы инструментов, которые страница публикует, — никакого скрейпинга DOM, никаких селекторов, никаких скриншотов.


Запуск

Три процесса. Для настоящего ассистента нужен ключ Anthropic API; без него работает всё, кроме модели (см. Без ключа ниже).

# 1. Postgres  (port 5434 — 5432 and 5433 are already taken on this machine)
docker compose up -d

# 2. Backend
cd backend
uv venv .venv && uv pip install --python .venv/bin/python -r requirements.txt
cp .env.example .env          # then put your ANTHROPIC_API_KEY in it
.venv/bin/python seed.py      # 50 contracts
.venv/bin/uvicorn app.main:app --reload --port 8000

# 3. Frontend
npm install
PORT=3002 npm start           # http://localhost:3002

Бэкенд сам наполняет базу данных при первом запуске, так что seed.py нужен только если вы хотите пересоздать данные или изменить размер (--force, --total 200).

Без ключа

  • MOCK_LLM=1 в backend/.env заменяет Claude на скриптованный заглушечный модуль, который говорит на том же самом протоколе. Ответы заранее заготовлены; вызовы инструментов настоящие, так что каждый путь активации по-прежнему работает. Удобно для демонстрации без траты токенов.

  • Без бэкенда вообще приложение всё равно загружается, и панель Прямые вызовы инструментов в сайдбаре вызывает инструменты WebMCP без модели в цикле.


Related MCP server: Salesforce MCP Server

Документация

WebMCP на практике — с какой проблемой на самом деле сталкивается встроенный ассистент, что такое WebMCP и как общаются браузер, бэкенд и модель, с диаграммами последовательности вызовов инструментов и передачи управления от сервера странице. Откройте файл в браузере.

CLAUDE.md — ориентир для работы в этом репозитории: команды, правила слоёв и уже пройденные здесь грабли.


Попробуйте это

Запрос

Что вы должны увидеть

«Какие договоры истекают в ближайшие 60 дней?»

Таблица сужается, панель фильтров становится фиолетовой

«Покажи всё, что связано с Allianz.»

Фильтрация по страховщику

«Найди договор Novaris D&O и открой его.»

Поиск, затем переход к детальному виду

«Продли киберполис Lumen Digital Health на 12 месяцев.»

Срок сдвигается на 12 месяцев, флаг продления сбрасывается, строка мигает

«Создай новый кибердоговор для Cortex Robotics со страховщиком Markel, лимит 3 млн.»

Форма нового договора открывается предзаполненной, но не отправленной

«Подними премию по FL-0146 до 95 000.»

Договор обновляется на месте

«Какова суммарная премия по страховщикам?»

Агрегируется в SQL, показывается разбивкой — договоры в контекст не подтягиваются

«Какие два договора имеют наибольшие лимиты?»

sort_by + limit в SQL; таблица переупорядочивается и показывает ровно два

«Продли всё, что истекает в ближайшие 30 дней.»

Серверный инструмент показывает предпросмотр пакета. Подтвердите — и он зафиксируется одной транзакцией, затем WebMCP переведёт вас к результату

«Собери мне отчёт о продлениях на ближайшие 90 дней.»

Генерируется на сервере, затем show_report выводит его на экран

«Соответствует ли цена по FL-0142 рынку?»

Бенчмарк-данные извне приложения — у страницы нет к ним доступа

Фиолетовая рамка вокруг левой панели означает, что управляет ассистент. Панель WebMCP в правом нижнем углу перечисляет все зарегистрированные инструменты — щёлкните по одному, чтобы увидеть JSON Schema, которую на самом деле получает Claude, — и логирует каждый вызов при пересечении границы.

Всё работает и вручную: щёлкните строку, нажмите Изменить, нажмите Продлить. Человек и агент используют один и тот же API и одно и то же состояние React, так что нет отдельного «режима агента» и нет способа, которым они могли бы разойтись во мнениях.


Архитектура

Интересная часть в том, что агент на самом деле живёт вне страницы — именно так и работает WebMCP: браузер передаёт агенту список инструментов и маршалит его вызовы инструментов обратно.

browser (React)                backend (FastAPI)              Claude
  │  user_message + tool list        │                           │
  │─────────────────────────────────>│  messages.stream(tools=…)  │
  │                                  │──────────────────────────> │
  │          text_delta              │      streamed text         │
  │<─────────────────────────────────│<─────────────────────────── │
  │          tool_use                │   stop_reason=tool_use     │
  │<─────────────────────────────────│<─────────────────────────── │
  │                                                               │
  │  executeTool() → REST → Postgres → React state → repaint      │
  │                                                               │
  │          tool_result             │                           │
  │─────────────────────────────────>│  append, continue loop     │
  │                                  │──────────────────────────> │
  │          turn_end                │   stop_reason=end_turn     │
  │<─────────────────────────────────│<─────────────────────────── │

Claude никогда не видит DOM. Бэкенд не содержит никаких реализаций инструментов — он лишь сообщает, что Claude хочет вызвать. Каждый инструмент выполняется в браузере против живого состояния React.

docker-compose.yml            Postgres 17 on :5434
backend/
├── seed.py                   seeding CLI
└── app/
    ├── main.py               FastAPI: REST + /ws/agent
    ├── db.py                 engine, session dependency, readiness wait
    ├── models.py             SQLModel table + validated API schemas
    ├── repository.py         all SQL lives here
    ├── seed_data.py          12 curated contracts (terms relative to today)
    ├── seed_gen.py           deterministic generator for the rest
    ├── queries.py            filtering, sorting and aggregation in SQL
    ├── server_tools.py       tools that run here, not in the page
    ├── artifacts.py          batch records and reports
    ├── llm.py                Claude client + the mock provider
    └── agent_ws.py           the bridge: routes each tool call to the right side
src/
├── webmcp-polyfill.js        polyfill + agent-side bridge
├── useWebMcpTools.js         registration lifecycle hook
├── api.js                    REST client
├── App.js                    owns state; registers the seven tools
├── agent/agentClient.js      WebSocket client; executes tool calls
└── components/               ContractList · ContractDetail · NewContractForm ·
                              PortfolioSummary · BatchResult · ReportView ·
                              AssistantChat · ToolInspector

Почему ручной агентский цикл

Исполнитель инструментов Anthropic SDK выполняет инструменты в том же процессе. Здесь инструменты живут в браузере пользователя, поэтому agent_ws.py вручную ведёт цикл stop_reason == "tool_use" и ожидает каждый результат через WebSocket. Параллельные вызовы инструментов выполняются конкурентно и возвращаются одним сообщением user, как и ожидает API.

Две поверхности инструментов, один список инструментов

Claude получает один плоский список. Он не знает и ему всё равно, что часть этих инструментов выполняется в браузере, а часть — в бэкенде, но это разделение — самое важное проектное решение здесь.

Инструменты страницы (WebMCP, navigator.modelContext) — это возможности страницы. Используйте их, когда пользователь должен видеть, как происходит изменение, и для работы с одной записью. Они выполняются против живого состояния React.

Серверные инструменты выполняются в процессе FastAPI и никогда не касаются браузера. Используйте их, когда управление интерфейсом было бы в корне неверной формой:

Серверный инструмент

Почему ему не место в интерфейсе

run_renewal_batch

Продление 14 договоров через страницу — это 14 кругов через модель, любой из которых может остановиться на полпути. Один вызов, одна транзакция, всё или ничего.

generate_renewal_report

Сборка документа — это вычисление, а не клики.

benchmark_rates

Данные о рыночных ставках живут вне приложения. Никакая автоматизация интерфейса их бы не нашла.

Связывающий их паттерн — передача управления. Серверная работа невидима — поэтому серверный инструмент возвращает идентификатор артефакта, и ассистент затем вызывает инструмент страницы, чтобы вывести его на экран:

run_renewal_batch(expiring_within_days=30)      ← server: previews, changes nothing
   → "4 contracts, €413,400. Shall I commit?"
run_renewal_batch(..., commit=true)             ← server: one transaction
   → batch_id: BATCH-0002
show_batch_result(batch_id="BATCH-0002")        ← page:  navigates the user there

Работа происходит вне страницы; результат всё равно попадает на страницу. Чат окрашивает их по-разному (фиолетовый = интерфейс сдвинулся, янтарный = работа произошла в другом месте), а инспектор перечисляет их под отдельными заголовками, так что то, какая сторона что сделала, никогда не приходится угадывать.

Массовые изменения по умолчанию показываются предпросмотром. run_renewal_batch — это пробный прогон, если не указано commit=true. Массовое изменение не должно происходить потому, что модель была на 80% уверена, что оно нужно, — ассистент показывает план и ждёт.

Инструменты страницы

Инструмент

Эффект на экране

search_contracts

Фильтрует, сортирует и ограничивает видимую таблицу (поэтому поиск агента виден)

summarise_portfolio

Агрегирует в SQL и открывает вид разбивки

get_contract

Никакого — возвращает полную запись

navigate

Переключает вид

prefill_new_contract_form

Заполняет форму и останавливается. Отправляет человек.

create_contract

Пишет в Postgres, открывает новый договор

update_contract

Обновляет строку на месте

renew_contract

Сдвигает один срок вперёд, сбрасывает флаг продления

show_batch_result

Показывает созданную сервером запись пакета

show_report

Показывает созданный сервером отчёт

Поверхность инструментов — это решение о стоимости

search_contracts получил sort_by / sort_dir / limit, а summarise_portfolio был добавлен по конкретной причине. На вопрос «какие два договора имеют наибольшую страховую сумму?» ассистент изначально вызывал search_contracts({}), подтягивал все 50 строк в контекст и сортировал их сам — 6 809 входных токенов и два вызова инструмента. С сортировкой и лимитом, вынесенными в SQL, тот же вопрос стоит 518 токенов и один вызов, а арифметику выполняет база данных, а не модель.

Если ваш агент много читает, чтобы ответить на малое, — это не хватает инструмента, а не проблема промптинга.

Имена аргументов инструментов точно совпадают с API и колонками базы данных (везде snake_case), так что нигде нет слоя маппинга, в котором мог бы спрятаться баг.

prefill_new_contract_form — это случай human-in-the-loop, на который стоит обратить внимание: печатает агент, решение остаётся за человеком. Системный промпт говорит Claude предпочитать его create_contract всякий раз, когда какая-то деталь была выведена.


Данные

50 договоров: 12 курируемых с историей в примечаниях, плюс 38 сгенерированных.

Генератор (seed_gen.py) детерминирован и заботится о двух вещах, которые скрипт случайных данных обычно делает неправильно:

  • Скоррелированные цифры. Премия — это ставка от лимита, с диапазоном ставок по продукту (D&O 0,35–0,75%, Cyber 0,8–1,6%, …), а франшизы масштабируются вместе с лимитом. Иначе всё, что ассистент говорит о портфеле, не звучит правдоподобно.

  • Реалистичный конвейер истечений. Сроки размещаются относительно сегодняшнего дня под целевое распределение статусов — примерно 10% истекших, 25% истекающих в ближайшие 90 дней, остальные активные, плюс два черновика. Так что «что нужно продлить?» — всегда настоящий вопрос, и повторное наполнение через полгода всё ещё даёт живой на вид портфель, а не полностью истёкший.

Статус (active / expiring / expired / draft) вычисляется из срока, а не хранится, поэтому не может разойтись. renewal_pending — отдельный флаг, который ставит брокер.

Все застрахованные компании вымышлены. Названия страховщиков — реальные участники рынка, используемые так, как их использует любая брокерская демонстрация; ничто здесь не представляет реальный полис.


Полифилл

src/webmcp-polyfill.js выполняет две отдельные задачи, и различие важно:

Сторона страницы (собственно полифилл). Нативный navigator.modelContext пока доступен не везде. Если его нет, файл устанавливает заглушку, реализующую предлагаемую поверхность — registerTool, unregisterTool, provideContext — которая логирует каждую регистрацию и каждый вызов в консоль DevTools. Приложение никогда не падает, а бейдж в шапке сообщает, какая версия у вас.

Сторона агента (мост). Нет API, ориентированного на страницу, для «быть агентом», поэтому модуль также зеркалирует каждый зарегистрированный инструмент и предоставляет listTools() / executeTool() поверх. agentClient.js использует этот мост и ничего больше. Зеркало поддерживается как в нативных, так и в браузерах с полифиллами, поэтому поведение идентично в обоих случаях.

Из консоли DevTools:

await webmcp.listTools()
await webmcp.executeTool('search_contracts', { product: 'Cyber', status: 'expiring' })
await webmcp.executeTool('renew_contract', { contract_id: 'FL-0142', months: 24 })

Ловушка React, о которой стоит знать

Очевидный способ зарегистрировать инструмент — неверен:

useEffect(() => {
  const h = registerTool({ name: 'x', execute: () => doThingWith(contracts) });
  return () => h.unregister();
}, []);                       // `contracts` is frozen at mount forever

Повторная регистрация при каждом изменении состояния также неверна — браузер будет видеть постоянное изменение всего набора инструментов, и выполняющийся вызов может быть вырван из-под агента.

useWebMcpTools.js регистрирует один раз с устойчивой косвенностью: зарегистрированный execute разрешает реальный обработчик из ref, который обновляется при каждом рендере. Регистрация стабильна; обработчики всегда видят текущее состояние. При двойном монтировании React StrictMode можно убедиться, что зарегистрировано ровно семь инструментов, а не четырнадцать и не ноль.

Примечания и ограничения

  • SEED_TOTAL / seed.py --total изменяют размер книги. Фильтрация, сортировка и ограничение уже выполняются в SQL (queries.py), поэтому для гораздо большей книги нужна только пагинация в представлении списка.

  • Пакетные записи и отчёты хранятся в памяти (artifacts.py, максимум 50). Это вывод задания, а не доменные данные; в реальном развёртывании их следует сохранять, поскольку запись массового изменения является журналом аудита.

  • benchmark_rates возвращает выдуманные числа. Он заменяет подписку на рыночные данные — суть в том, что это данные, к которым у браузера нет доступа.

  • Новые идентификаторы контрактов берутся из max(id) + 1. Два одновременных создания могут конфликтовать; исправление в одну строку — последовательность базы данных.

  • Разговор хранится в памяти для каждого WebSocket-соединения, поэтому перезагрузка начинает новый чат. Сам портфель находится в Postgres и сохраняется.

  • output_config: {effort: "medium"} с адаптивным мышлением установлен в llm.py; повысьте его до high, если хотите, чтобы ассистент более тщательно планировал многошаговую работу.

  • Включены запасные варианты отказа на стороне сервера. Если ваша учётная запись или версия SDK отклоняет параметр, llm.py записывает предупреждение и повторяет попытку один раз по обычному пути, а не завершает ход с ошибкой.

F
license - not found
-
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • A
    license
    A
    quality
    A
    maintenance
    An MCP server implementation that integrates Claude with Salesforce, enabling natural language interactions with Salesforce data and metadata for querying, modifying, and managing objects and records.
    6
    15
    3,172
    166
    MIT
  • F
    license
    B
    quality
    C
    maintenance
    A customer and product management MCP server using SQLite. It enables Claude Desktop users to manage client and product data through natural language interactions.
    9
    1

View all related MCP servers

Related MCP Connectors

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/hossein-finlex/web-mcp-hello'

If you have feedback or need assistance with the MCP directory API, please join our Discord server