Zava Relocation MCP UI Demo
An MCP server that powers an interactive employee relocation intake form, enabling chat-driven completion, document upload/extraction, and local Qwen2.5 7B interpretation.
README
Zava Relocation MCP UI Demo
<a href="demo/zava-relocation-demo.mp4"><img src="demo/zava-relocation-demo-preview.png" alt="Watch the 2-minute Zava Relocation demo" width="600"></a>
Watch the demo: 2-minute narrated walkthrough
Zava Relocation Inc. helps employees move for a new job. This project is a reference demo for building an interactive MCP App with MCP-UI, a local Qwen2.5 7B model, and a synchronized conversational intake form.
The user can chat with Ava, upload an offer letter, or edit the profile directly. Extracted information is applied to the form immediately and highlighted so the user can review what changed.
For a customer-facing teaching walkthrough, see the dedicated MCP UI + LLM form-fill guide.
What the demo demonstrates
- Chat-driven form completion with live field updates
- Optional browser voice mode: speak to Ava and hear Qwen responses aloud
- PDF and DOCX offer-letter extraction in the browser
- Local interpretation with Qwen2.5 7B through Foundry Local
- Five profile sections: contact, employment, move, moving logistics, and preferences
- Grounded moving-option and reimbursement guidance from the fictional Contoso policy PDF
- Progress tracking, highlighted AI updates, reset, and completion state
- Responsive desktop and mobile layouts
- MCP Apps resource/tool linkage through
@mcp-ui/serverand@modelcontextprotocol/ext-apps - A production build that inlines the UI into one HTML resource
Demo boundary: This is a local prototype. It does not persist relocation cases, authenticate users, or submit data to a production HR system. The sample PDFs in
sample-documents/contain fictional data.
Architecture
MCP Apps host
|
| Streamable HTTP: POST /mcp
v
Node + Express MCP server
|-- start_relocation_intake tool
|-- ui://zava-relocation/intake resource
|-- POST /api/chat
v
Foundry Local (same machine)
|
v
Qwen2.5 7B
Browser UI
|-- PDF.js / Mammoth extract document text locally
|-- regex extractor gives immediate form updates
|-- /api/chat sends text and current form to local Qwen
There are two ways to use the UI:
- Standalone mode: Vite serves the React application at
http://localhost:5173. - MCP App mode: An MCP Apps-compatible host connects to
http://localhost:3001/mcp, discoversstart_relocation_intake, and renders the linkedui://zava-relocation/intakeresource.
How MCP-UI is leveraged
This project uses the MCP Apps pattern recommended by MCP-UI:
-
server/index.tscreates anMcpServerand aStreamableHTTPServerTransport. -
The production
dist/index.htmlis loaded into a UI resource usingcreateUIResource. -
registerAppResourcepublishes that resource atui://zava-relocation/intake. -
registerAppToolexposesstart_relocation_intakeand links it to the UI with:_meta: { ui: { resourceUri: relocationUI.resource.uri }, } -
The embedded UI signals readiness with
ui-lifecycle-iframe-readyand can send host messages withwindow.parent.postMessage.
The important distinction is that the MCP server does not render the form itself. It registers the tool and UI resource; the MCP Apps host decides where and how to display that resource.
Foundry Local and Qwen2.5 7B
The assistant uses Qwen2.5 7B through Foundry Local. Foundry Local runs on the same machine as this Node server and exposes an OpenAI-compatible local chat-completions endpoint. No cloud model fallback is configured.
Prerequisites
Foundry Local support depends on the host machine. On Windows, Microsoft documents Windows 11 24H2 or later, .NET 9 or later, and a DirectX 12-capable GPU for the Windows ML runtime.
Install the Foundry Local CLI:
winget install Microsoft.FoundryLocal
Close and reopen PowerShell, then verify the CLI:
foundry --version
List the model aliases available in the local catalog:
foundry model list
Start or download the Qwen model using the alias shown by the catalog. The expected demo alias is:
foundry model run qwen2.5-7b
Keep Foundry Local running while using the demo. The current Foundry Local service endpoint for this project is:
http://127.0.0.1:61563/v1/chat/completions
If the installed catalog uses a different alias or port, configure the Node server before starting it:
$env:FOUNDRY_LOCAL_ENDPOINT = "http://127.0.0.1:<actual-port>/v1/chat/completions"
$env:FOUNDRY_LOCAL_MODEL = "qwen2.5-7b-instruct-cuda-gpu"
Foundry Local assigns the service port dynamically. Use foundry service status or foundry service list to inspect the active service, and use GET http://127.0.0.1:<port>/openai/models to list available model IDs. The exact model ID can vary by hardware; on this machine the available Qwen GPU model is qwen2.5-7b-instruct-cuda-gpu.
What the model receives
server/foundryLocal.ts sends Qwen:
- The latest user message or a document-review instruction
- The current form state
- Up to 8,000 characters of extracted document text
The system prompt asks Qwen to return JSON in this shape:
{
"reply": "I found your new employer and start date.",
"fields": {
"employer": "Northstar Analytics",
"role": "Senior Product Manager",
"startDate": "2026-10-07"
}
}
Only allowlisted form keys are accepted by the server. The model cannot add arbitrary fields to the client state.
Voice mode
Qwen2.5 7B remains a text-only model. Voice mode uses browser capabilities around the existing text pipeline:
microphone
-> browser SpeechRecognition
-> transcript
-> POST /api/chat
-> Foundry Local + Qwen
-> text reply and form fields
-> browser SpeechSynthesis
-> spoken Ava response
Click the microphone button in the composer to speak. When recognition ends, the transcript is submitted through the same chat flow used by typed messages. The Ava voice on/off control enables or disables spoken replies, Voice lets you choose an installed browser voice, and Stop Ava interrupts the current response. The app prefers Microsoft/Edge natural English voices when available, such as Ava, Jenny, Aria, or Sonia. Chrome and Edge provide the best support; microphone permission is required and voice input needs localhost or HTTPS. Voice quality depends on the voices installed and exposed by the browser.
Voice input uses a guided one-field-at-a-time flow. The app identifies the next incomplete required field, asks Qwen to focus on that field, advances the active form section after the answer, and speaks one short next question. This keeps each voice turn easy to remember. Typed chat remains free-form.
Document parsing flow
The browser handles the original file; the file itself is not uploaded to a cloud service:
src/App.tsxvalidates the extension and 10 MB limit.src/documentParser.tsuses PDF.js for PDFs and Mammoth for DOCX files.- The extracted text is sent to local Qwen through
POST /api/chatfor document interpretation. - Qwen returns structured fields. The UI uses a consistent document-review message telling the user to review the form and manually complete any missing information; it does not enumerate missing fields.
- The model fields are applied and highlighted in the form.
Grounded logistics walkthrough
Upload contoso-moving-offers-and-reimbursement-guide.pdf, then ask Ava questions such as “Which option is best for a 250-mile move?” or “Can I use a rented truck for 150 miles?” The browser keeps the extracted policy text as grounding for later chat turns, and the model is instructed to answer policy questions only from that text. The Moving logistics section captures the selected method, approximate distance, reimbursement path, and notes.
The browser-side PDF.js and Mammoth libraries are text extraction utilities only; they do not decide which values belong in the relocation form. Foundry Local/Qwen is the source of truth for PDF/DOCX field extraction. If the model is unavailable, the UI reports the error instead of silently filling document fields with a non-LLM parser.
Code breadcrumbs
| Area | File | Purpose |
|---|---|---|
| Main UI | src/App.tsx |
Chat, form sections, uploads, reset, progress, MCP host messages |
| Styling | src/styles.css |
Zava layout, responsive behavior, light/dark theme variables |
| Form types | src/types.ts |
IntakeForm, FormField, Message, and blank initial state |
| PDF/DOCX parsing | src/documentParser.ts |
Browser-side PDF.js and Mammoth extraction |
| Immediate extraction | src/extraction.ts |
Labeled values, dates, phone, email, and relocation phrase matching |
| Local LLM client | server/foundryLocal.ts |
OpenAI-compatible request, JSON validation, field allowlist |
| MCP server | server/index.ts |
Express routes, MCP transport, tool/resource registration |
| Dev proxy | vite.config.ts |
Proxies browser /api calls to port 3001 |
| Sample files | sample-documents/ |
Fictional offer letters for upload testing |
| Single-file build | vite.config.ts |
vite-plugin-singlefile inlines JavaScript and CSS |
Install and run
Install Node dependencies:
npm install
Standalone development mode
Start Vite and the MCP server together:
npm run dev
Open:
http://localhost:5173
The Vite /api proxy forwards local model requests to port 3001.
MCP Apps mode
Build the UI first. The MCP server embeds the resulting dist/index.html:
npm run build
npm start
Configure the MCP Apps-compatible host with:
http://localhost:3001/mcp
Then call:
start_relocation_intake
The server also exposes a basic health check:
http://localhost:3001/health
Demo workflow
- Start Foundry Local and make the Qwen model available.
- Run
npm run dev. - Click a quick prompt or type a relocation message.
- Watch the matching fields populate and highlight.
- Upload one of the PDFs in
sample-documents/. - Review the extracted and model-enriched fields.
- Use Reset demo to return to a blank state.
Useful chat prompts:
I'm moving from Seattle to Austin for a role at Contoso.My family has 3 people.Employer: FabrikamPosition: Senior Product ManagerI need temporary housing.
Troubleshooting
Could not connect to Foundry Local
Check that Foundry Local is running, that the model has been downloaded/loaded, and that the endpoint matches FOUNDRY_LOCAL_ENDPOINT.
Model not found
Run foundry model list and set FOUNDRY_LOCAL_MODEL to an alias in the installed catalog.
The MCP server says Missing dist/index.html
Run:
npm run build
before npm start.
The form does not find fields in a document
The PDF must contain selectable text. Scanned/image-only PDFs need OCR before PDF.js can extract useful text. Labeled values such as Employee name:, Email address:, New employer:, Job title:, Start date:, Moving from:, and Moving to: are easiest for the deterministic extractor to recognize.
Scripts
| Command | Purpose |
|---|---|
npm run dev |
Start Vite and the MCP server in watch mode |
npm run dev:ui |
Start only Vite |
npm run dev:mcp |
Start only the MCP server in watch mode |
npm run build |
Type-check and create the single-file production UI |
npm start |
Start the MCP server against dist/index.html |
npm run preview |
Preview the Vite production build |
To regenerate the fictional Contoso policy PDF, install the script dependency and run the generator:
python -m pip install -r scripts/requirements.txt
python scripts/generate_contoso_policy_pdf.py
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。