---
name: vpa360-import
description: Import Valbridge appraisal workbooks (BMO Trust Appraisal and other supported xlsx feeds) into VPA360 via the Import Tool's MCP server. Use when the user asks to upload, validate, preprocess, or import an xlsx of appraisal/property data, or to check the status of a previously-uploaded import.
---

# VPA360 Import Tool — MCP skill

This skill drives the **Valbridge VPA360 Import Tool** through its MCP server.
The tool ingests `.xlsx` workbooks (currently BMO Trust Appraisal),
preprocesses them into VPA360's intermediate format, and reports per-cell
errors. There is no VPA backend connectivity in MVP — successful imports
surface a downloadable intermediate JSON for hand-off.

## When to use this skill

Invoke when the user is doing any of:

- "Upload / import / process this workbook into VPA360."
- "Check whether <file> would import cleanly."
- "What's the status of import `<tracking_id>`?"
- "Show me the errors / unmapped values from that import."
- Any task involving a Valbridge appraisal `.xlsx` and VPA360.

Do **not** use for anything outside that scope (general spreadsheet editing,
arbitrary data analysis, etc.).

## Authentication

The user must hold a VPA360 access token. Pass it everywhere the tools or
the underlying transport require:

- Once at MCP transport time, however the host client expects (typically as
  a Bearer header on the SSE connect).
- In every tool call, as the `token` argument — required by all three
  tools.

If the user has not provided a token, **ask them for it once** before
calling any tool. Do not invent or guess tokens. Do not echo the token back
to the user in chat output.

The connection URL itself is the host operator's job to share; treat it as
deployment configuration outside this skill.

## Available tools

### `upload_xlsx`

Upload a workbook for preprocessing. Returns a `tracking_id` to poll.

| Field | Type | Required | Notes |
|---|---|---|---|
| `token` | string | yes | VPA360 bearer token |
| `filename` | string | yes | Original filename, must end in `.xlsx` |
| `content_base64` | string | yes | Base64-encoded file bytes |

Constraints:

- Maximum file size: **25 MB** (compressed). Exceeding this returns 413
  before processing. Surface the limit to the user before encoding a large
  file.
- The decompressed workbook also has caps (200 MB total, 100 MB per
  member) to defeat zip-bomb uploads.

Returns `{"tracking_id": "<32-hex>", "state": "UPLOADED"}`.

### `get_import_status`

Poll the state of an upload session.

| Field | Required | Notes |
|---|---|---|
| `token` | yes | bearer |
| `tracking_id` | yes | from `upload_xlsx` |

State machine:

```
UPLOADED → PREPROCESSING → READY_FOR_VPA   (success — terminal)
                          ↘ PREPROCESS_FAILED  (errors — terminal)
```

Poll roughly every 2 seconds while the state is `UPLOADED` or
`PREPROCESSING`. Stop polling once state is terminal.

Returns the session record including `state`, `feed`, `error_count`,
`warning_count`, `error_summary`, `available_reports`, `intermediate_available`.

### `get_import_report`

Fetch the JSON report for a terminal session, optionally with the full
intermediate document.

| Field | Required | Notes |
|---|---|---|
| `token` | yes | bearer |
| `tracking_id` | yes | terminal state required |
| `include_intermediate` | no, default `false` | only meaningful when state is `READY_FOR_VPA` |

Calling this **finalizes** the session for retention purposes. The report
contains:

- `feed` — which preprocessor handled the workbook (`bmo`, `cre`, `lease_abstract`, `unknown`, `unsafe`).
- `ok` — true iff no blocking errors.
- `summary.posit_count` / `warning_count` / `error_count`.
- `errors[]` — each with `code`, `message`, and `provenance` (`sheet`, `row`, `cell` like `B7`, `column`).
- `warnings[]` — non-blocking. Includes unmapped enum/HVAC/date values; raw text is preserved in the intermediate JSON.

When `include_intermediate=true` and state is `READY_FOR_VPA`, the response
also carries `intermediate` — the feed-agnostic document with all posits,
two-level provenance, and the raw unmapped tokens.

## Canonical workflow

For "import this workbook" tasks:

1. **Get the file as base64.** From Claude Excel this is provided directly;
   from Claude Web ask the user to attach the xlsx and base64-encode it
   (the runtime usually does this automatically once attached).
2. **Confirm the size** is under 25 MB before calling `upload_xlsx`.
3. **`upload_xlsx`** → capture `tracking_id`. Tell the user "uploaded; checking…".
4. **Poll `get_import_status`** every ~2 s until terminal. Most BMO workbooks
   finish in <5 s.
5. **`get_import_report`** with `include_intermediate=true` if state is
   `READY_FOR_VPA`, or default `false` if `PREPROCESS_FAILED`.
6. **Summarize for the user**:
   - On `READY_FOR_VPA`: number of posits, any warnings (cell-level), and
     where to find the intermediate JSON. Don't dump the full intermediate
     unless asked.
   - On `PREPROCESS_FAILED`: list errors by cell address (`Sheet!B7: …`) so
     the user can correct the source workbook directly.
7. **Suggest the Excel-with-comments report** if they want to fix errors in
   place: it round-trips their original workbook with a first-sheet "Issues"
   index and per-cell comments. The web UI hosted alongside the MCP server
   makes it downloadable.

## Supported feeds

| Feed | Detection | Status |
|---|---|---|
| **BMO Trust Appraisal** | sheets `Property Data` + `Unit Mix Detail`, `VPA360 PID` column | Fully implemented |
| CRE Appraisal Extraction | sheets `Properties` + `Sales` | Recognised, returns `feed-not-implemented` error |
| Lease Abstract | any sheet name containing "lease" | Recognised, returns `feed-not-implemented` error |
| Anything else | — | Returns `unknown-feed` error; no Excel report generated |

If the user asks for CRE or lease-abstract imports, set expectations: the
preprocessor isn't in MVP yet, so the upload will fail with a clear error
but no transformation will occur.

## Error handling

| Symptom | What it means | What to do |
|---|---|---|
| 401 on connect or tool call | Bad / missing token | Ask the user for the correct token; do not retry blindly |
| 413 on `upload_xlsx` | File over 25 MB | Tell the user; do not retry |
| `PREPROCESS_FAILED` with `unsafe-upload` | Zip-bomb / oversized member | Tell the user the file looks malformed |
| `PREPROCESS_FAILED` with `unknown-feed` | Workbook shape isn't recognised | Tell the user which feeds are supported |
| `error_count > 0` but `feed != "unknown"` | Real data errors (e.g. missing PID) | Surface cell addresses; suggest the Excel report for editing |

## Output style

- Lead with the bottom line: *succeeded / failed* and the posit / error /
  warning counts.
- For errors, list cell addresses (`Sheet!Cell — message`) — never paraphrase
  away the location.
- For warnings, group by column when there are many of the same kind
  ("12 unmapped MVS Class values like 'Class D - Wood Frame'").
- Never paste the full intermediate JSON into chat. Offer a download link
  or summary instead.
- Never echo the user's bearer token, even partially.

## Privacy & safety

- The token is the user's authorization — treat it like a password.
- Workbook contents may include owner names, addresses, and financial data.
  Don't quote large blocks of cell values back to the user; summarize.
