WebMCP Contract Portfolio
WebMCP 계약 포트폴리오
Claude가 navigator.modelContext를 통해 직접 운영하는 상업용 금융 라인 보험 앱 — WebMCP(Web Model Context Protocol) API입니다.
*"향후 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).
키 없이
backend/.env의MOCK_LLM=1은 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에서 집계되어 세부 내역으로 표시됩니다 — 계약이 컨텍스트로 가져와지지 않습니다 |
"한도가 가장 큰 두 계약은 무엇인가요?" | SQL에서 |
"향후 30일 내에 만료되는 모든 것을 갱신해줘." | 서버 도구가 배치를 미리 봅니다. 확인하면 하나의 트랜잭션으로 커밋된 후 WebMCP가 결과로 이동합니다 |
"향후 90일 동안의 갱신 보고서를 만들어줘." | 서버에서 생성된 후 |
"FL-0142가 시장 가격과 일치하나요?" | 앱 외부의 벤치마크 데이터 — 페이지에는 이에 대한 경로가 없습니다 |
왼쪽 창 주변의 보라색 테두리는 어시스턴트가 작동 중임을 의미합니다. 오른쪽 하단의 WebMCP 패널에는 등록된 모든 도구가 나열됩니다 — 하나를 클릭하면 Claude가 실제로 받는 JSON 스키마를 볼 수 있습니다 — 그리고 각 호출이 경계를 넘을 때 기록합니다.
모든 것은 수동으로도 작동합니다: 행을 클릭하고, 편집을 누르고, 갱신을 누르세요. 인간과 에이전트는 동일한 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을 통해 각 결과를 기다립니다. 병렬 도구 호출은 동시에 실행되고 API가 기대하는 대로 단일 user 메시지로 반환됩니다.
두 도구 표면, 하나의 도구 목록
Claude는 하나의 평면 목록을 받습니다. 그 중 일부 도구가 브라우저에서 실행되고 일부가 백엔드에서 실행된다는 것을 알지도 신경 쓰지도 않습니다 — 그러나 그 구분이 여기서 가장 중요한 설계 결정입니다.
페이지 도구(WebMCP, navigator.modelContext)는 페이지의 기능입니다. 사용자가 변경이 일어나는 것을 지켜봐야 할 때, 그리고 단일 레코드 작업에 사용하세요. 라이브 React 상태에 대해 실행됩니다.
서버 도구는 FastAPI 프로세스에서 실행되며 브라우저를 건드리지 않습니다. UI를 구동하는 것이 완전히 잘못된 형태일 때 사용하세요:
서버 도구 | UI에 속하지 않는 이유 |
| 페이지를 통해 14개 계약을 갱신하는 것은 모델을 통한 14번의 왕복이며, 그 중 어느 것이든 중간에 멈출 수 있습니다. 한 번의 호출, 하나의 트랜잭션, 전부 아니면 전무입니다. |
| 문서를 조합하는 것은 클릭이 아니라 계산입니다. |
| 시장 요율 데이터는 애플리케이션 외부에 있습니다. 어떤 UI 자동화로도 찾을 수 없습니다. |
그것들을 묶는 패턴은 핸드오프입니다. 서버 작업은 보이지 않습니다 — 그래서 서버 도구는 아티팩트 ID를 반환하고, 어시스턴트는 그런 다음 페이지 도구를 호출하여 화면에 표시합니다:
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작업은 페이지 밖에서 일어나고 결과는 여전히 페이지에 도착합니다. 채팅은 두 가지를 다르게 색칠합니다(보라색 = UI가 움직임, 호박색 = 다른 곳에서 작업이 발생) 그리고 검사기는 별도의 제목 아래에 나열하므로 어느 쪽이 무엇을 했는지 추측할 필요가 없습니다.
대량 변경은 기본적으로 미리 봅니다. run_renewal_batch는 commit=true가 아니면 건식 실행입니다. 대량 변경은 모델이 80% 확신했다고 해서 일어나서는 안 됩니다 — 어시스턴트는 계획을 보여주고 기다립니다.
페이지 도구
도구 | 화면에 미치는 효과 |
| 보이는 테이블을 필터링, 정렬 및 제한합니다 (이것이 에이전트 검색이 보이는 이유입니다) |
| SQL에서 집계하고 세부 내역 보기를 엽니다 |
| 없음 — 전체 레코드를 반환합니다 |
| 보기를 전환합니다 |
| 양식을 채우고 멈춥니다. 인간이 제출합니다. |
| Postgres에 쓰고 새 계약을 엽니다 |
| 행을 그 자리에서 업데이트합니다 |
| 한 기간을 앞으로 연장하고 갱신 플래그를 해제합니다 |
| 서버가 생성한 배치 레코드를 표시합니다 |
| 서버가 생성한 보고서를 표시합니다 |
도구 표면은 비용 결정입니다
search_contracts는 sort_by / sort_dir / limit를 얻었고 summarise_portfolio가 추가된 데는 특정한 이유가 있습니다. *"가장 큰 보험 가입 금액을 가진 두 계약은 무엇인가요?"*라는 질문에 어시스턴트는 원래 search_contracts({})를 호출하고 50개 행을 모두 컨텍스트로 가져와 직접 정렬했습니다 — 6,809 입력 토큰과 두 번의 도구 호출. 정렬과 제한을 SQL로 밀어 넣으면 같은 질문은 518 토큰과 한 번의 호출로 처리되며, 산술은 모델이 아니라 데이터베이스가 수행합니다.
에이전트가 적은 답을 얻기 위해 많은 것을 읽고 있다면, 그것은 프롬프팅 문제가 아니라 누락된 도구입니다.
도구 인수 이름은 API 및 데이터베이스 열과 정확히 일치합니다(전체적으로 snake_case), 그래서 버그가 숨을 매핑 계층이 어디에도 없습니다.
prefill_new_contract_form은 주목할 만한 인간-인-더-루프 사례입니다: 에이전트가 타이핑을 하고, 사람이 결정을 유지합니다. 시스템 프롬프트는 세부 사항이 추론될 때마다 create_contract보다 이것을 선호하도록 Claude에게 지시합니다.
데이터
50개 계약: 메모에 이야기가 있는 12개의 큐레이션된 계약과 38개의 생성된 계약.
생성기(seed_gen.py)는 결정적이며 무작위 데이터 스크립트가 일반적으로 잘못하는 두 가지를 신경 씁니다:
상관된 수치. 보험료는 한도에 대한 요율이며, 제품별 요율 밴드가 있습니다(D&O 0.35–0.75%, Cyber 0.8–1.6%, …), 그리고 공제액은 한도에 비례합니다. 그렇지 않으면 어시스턴트가 포트폴리오에 대해 말하는 어떤 것도 신뢰할 수 없게 들립니다.
현실적인 만료 파이프라인. 기간은 오늘을 기준으로 목표 상태 혼합에 맞춰 배치됩니다 — 대략 10% 만료, 25% 90일 내 만료, 나머지 활성, 그리고 두 개의 초안. 그래서 "무엇을 갱신해야 하나요?"는 항상 실제 질문이며, 6개월 후에 다시 시드해도 완전히 만료된 책이 아니라 살아있는 책이 생성됩니다.
상태(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의 이중 마운트에서 정확히 일곱 개의 도구가 등록된 것을 확인할 수 있다 — 열네 개도 아니고, 0개도 아니다.
참고 사항 및 제한 사항
SEED_TOTAL/seed.py --total은 책 크기를 변경한다. 필터링, 정렬, 제한은 이미 SQL(queries.py)에서 실행되므로, 훨씬 더 큰 책에 필요한 것은 목록 보기의 페이지네이션뿐이다.배치 레코드와 보고서는 메모리에 저장된다(
artifacts.py, 최대 50개). 이는 도메인 데이터라기보다 작업 출력물이다. 실제 배포에서는 대량 변경 레코드가 감사 추적이므로 이를 영속화해야 한다.benchmark_rates는 임의의 숫자를 반환한다. 이는 시장 데이터 구독을 대신하는 것이다 — 핵심은 브라우저가 접근할 수 없는 데이터라는 점이다.새 계약 ID는
max(id) + 1에서 나온다. 동시에 두 번 생성하면 충돌할 수 있다. 데이터베이스 시퀀스가 한 줄짜리 수정이다.대화는 WebSocket 연결당 메모리에 저장되므로, 새로고침하면 새 채팅이 시작된다. 포트폴리오 자체는 Postgres에 있으며 영속된다.
output_config: {effort: "medium"}과 적응형 사고는llm.py에 설정되어 있다. 어시스턴트가 다단계 작업을 더 신중하게 계획하길 원한다면high로 올려라.서버 측 거부 폴백이 활성화되어 있다. 계정이나 SDK 버전이 해당 파라미터를 거부하면,
llm.py는 경고를 기록하고 턴을 실패시키는 대신 일반 경로로 한 번 재시도한다.
This server cannot be installed
Maintenance
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
- AlicenseAqualityAmaintenanceAn 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.6153,172166MIT
- AlicenseAqualityDmaintenanceAn MCP server implementation that integrates Claude with Salesforce, enabling natural language interactions with Salesforce data and metadata.850MIT
- FlicenseBqualityCmaintenanceA customer and product management MCP server using SQLite. It enables Claude Desktop users to manage client and product data through natural language interactions.91
- Flicense-qualityBmaintenanceAn MCP server that enables Claude to deploy full-stack web apps to Cloudflare, including databases, authentication, and file storage, directly through natural language.
Related MCP Connectors
MCP server giving Claude AI access to 22+ NYC public-record databases for real estate due diligence
MCP server for AI agents to plan, verify, and deploy Cloudflare-native apps.
Analytical memory for AI agents: a real Postgres queried in plain English over MCP. One command.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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