Skip to content

Commit 8437574

Browse files
vosesoftclaude
andcommitted
Fix all five field-report findings (0.3.11)
From the 2026-07-20 field bug report (P. Vanneck, ModelRisk 9.1.1.1): 1. Claude Code install was a silent no-op: the entry went to ~/.claude/settings.json, which Claude Code does not read for MCP servers. Now prefers `claude mcp add --scope user` (CLI owns the schema) with a ~/.claude.json mcpServers merge fallback; detection keys on ~/.claude.json / the claude binary. Live-verified: `claude mcp list` shows the server Connected. 2. --read-only implemented and ENFORCED (was declared, never wired): new CLI flag + MODELRISK_MCP_READ_ONLY env; every mutating path - cell/array/range writes, clears, named ranges, simulations, saves, save-on-close - raises ReadOnlyModeError; reads/analysis unaffected. 3. Multi-instance Excel attach: probe each instance (VosePoisson(5)) and prefer one where ModelRisk answers instead of trusting apps.active; the add-in-dead error now enumerates instances (PID/workbooks/live) so process mismatch is visible. 4. MRService.dll export check at load: 7.1.x-era DLLs (Tamara) fail with a version diagnosis (needs >= 7.3.2.1; bundled key covers 7.3.2.1-9.2.2.1) instead of a mid-call 'function not found'; messages stop recommending the Tamara path and state plainly that ModelRisk installers through 9.1.x do not ship the DLL. 5. Claude for Excel docs corrected and WITHDRAWN: no local Connectors panel exists; claude.ai custom connectors are remote-fetched by Anthropic infra so loopback is unreachable by construction; docs direct users to Claude Desktop/Code, note that local stdio servers never appear in the Connectors panel, and warn explicitly against public-tunnel workarounds. 13 new regression tests; 648 green; ruff+mypy clean. 0.3.10 -> 0.3.11. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent d68d4e8 commit 8437574

15 files changed

Lines changed: 675 additions & 147 deletions

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,16 @@ All notable changes to ModelRisk MCP. Follows [Keep a Changelog](https://keepach
44

55
## [Unreleased]
66

7+
## [0.3.11] — 2026-07-20
8+
9+
### Field bug report fixes (all five findings addressed — thank you, Peter Vanneck)
10+
11+
- **Claude Code registration actually works now.** `modelrisk-mcp install` wrote the server entry into `~/.claude/settings.json` — a file Claude Code does not read for MCP servers — while printing a green "added" line: a silent no-op. It now prefers `claude mcp add --scope user` (the CLI owns the schema), falling back to a direct `mcpServers` merge into `~/.claude.json` when the CLI is absent; detection keys on `~/.claude.json` / the `claude` binary. Verified live end-to-end (`claude mcp list` shows the server "✓ Connected").
12+
- **`--read-only` exists and is enforced.** `Settings.read_only` was declared but nothing set or checked it. New `--read-only` flag / `MODELRISK_MCP_READ_ONLY=1` env: every mutating operation — cell/range/array writes, named ranges, clears, simulations, workbook saves (including save-on-close) — raises a clear `ReadOnlyModeError` while reading and analysis keep working. The natural first-session posture against a live client model.
13+
- **Multi-instance Excel attach is no longer a coin toss.** With several Excel processes running (ModelRisk's shortcut can spawn a second one), `xlwings.apps.active` could attach to an instance *without* the add-in, making diagnostics report ModelRisk dead while Vose functions worked fine in the user's real session. The bridge now probes each instance with the separator-safe `VosePoisson(5)` evaluation and prefers one where ModelRisk answers; the add-in-dead error now enumerates running instances (PID, workbooks, live/dead) so a mismatch is visible instead of reading like a locale or licence fault.
14+
- **MRService.dll too old is diagnosed up front.** Required exports are probed at load; a 7.1.x-era DLL (e.g. the copy shipped with Tamara) now fails with "too old — needs ≥ 7.3.2.1 (bundled key covers 7.3.2.1–9.2.2.1)" instead of a cryptic mid-call "function 'MRLIB_SetOfflineActivationKeyEx2' not found". Error messages no longer recommend the Tamara path; they state plainly that ModelRisk installers through 9.1.x do not ship MRService.dll, so `.vmrs` reading is unavailable out of the box on ModelRisk-only machines (building and simulating are unaffected).
15+
- **Claude for Excel instructions corrected — withdrawn, not hedged.** Current Claude for Excel builds have no local Connectors panel; they resolve connectors from the claude.ai account, and claude.ai custom connectors are fetched by Anthropic's infrastructure — a loopback URL is unreachable *by construction*. README + docs/claude-for-excel.md now say this plainly, direct users to Claude Desktop / Claude Code (stdio), document that local stdio servers never appear in the claude.ai "Connectors" panel (absence there is not a failed install), and explicitly warn against public-tunnel workarounds for a server that can write and save workbooks.
16+
717
## [0.3.10] — 2026-07-05
818

919
### Demo gallery wave 2 — full use-case coverage

README.md

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -187,18 +187,42 @@ After any of the three, **restart Claude Desktop** so it spawns the MCP server s
187187

188188
---
189189

190-
## Wire into Claude for Excel (HTTP transport)
191-
192-
Claude for Excel runs inside an Office.js sandbox and can't spawn subprocesses, so it talks to MCP servers over HTTP. Start the server in HTTP mode:
190+
## Claude for Excel — not currently reachable (read this before trying)
191+
192+
**Current Claude for Excel builds cannot connect to this server, and no local
193+
workaround exists.** Earlier versions of this README described a
194+
Settings → Connectors flow; that was wrong for shipping builds and has been
195+
withdrawn (verified against a 2026-07 build; independently confirmed by a
196+
field report).
197+
198+
What we verified:
199+
200+
- The Claude for Excel add-in has **no local Connectors panel**. It resolves
201+
connectors from your **claude.ai account**, and claude.ai custom connectors
202+
are **remote MCP servers fetched by Anthropic's infrastructure** — not by
203+
your device. A loopback URL (`http://127.0.0.1:…`) is therefore unreachable
204+
**by construction**, and the dialog's OAuth fields don't match this server's
205+
bearer-token auth.
206+
- Local **stdio** servers (the Claude Desktop kind) never appear in the
207+
claude.ai / Claude Desktop "Connectors" panel. Their absence there is
208+
normal, not a failed install — Claude Desktop lists them separately under
209+
local MCP servers.
210+
- **Do not expose this server through a public tunnel to work around this.**
211+
It can write formulas into, simulate, and save your workbooks; putting that
212+
behind a single bearer token on a public URL is a materially different risk
213+
posture than the loopback bind these docs describe, and we don't support it.
214+
215+
**Use Claude Desktop or Claude Code instead** (local stdio — the supported,
216+
tested path; `modelrisk-mcp install` wires both). The HTTP transport below
217+
remains useful for LAN/self-hosted setups with MCP clients that genuinely
218+
run on your machine:
193219

194220
```powershell
195221
$env:MODELRISK_MCP_TOKEN = [Guid]::NewGuid().ToString("N") * 2
196222
modelrisk-mcp --transport=streamable-http --port=8000 --token=$env:MODELRISK_MCP_TOKEN
197223
```
198224

199-
Then in Claude for Excel: Settings → Connectors → Add MCP server, URL `http://127.0.0.1:8000/mcp`, paste the token. Full guide: [docs/claude-for-excel.md](docs/claude-for-excel.md).
200-
201-
**Why this is interesting:** Claude for Excel's sandbox can't reach Excel's COM surface or the ModelRisk ribbon on its own. ModelRisk MCP runs outside the sandbox and bridges that gap — Claude for Excel can do things via this server it structurally can't do otherwise (run simulations, dispatch ModelRisk COM, write distributions through the safety pipeline).
225+
Details and the full analysis: [docs/claude-for-excel.md](docs/claude-for-excel.md).
202226

203227
---
204228

docs/claude-for-excel.md

Lines changed: 75 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1,103 +1,81 @@
1-
# Wiring ModelRisk MCP into Claude for Excel
2-
3-
Claude for Excel runs inside an Office.js iframe sandboxed *inside* Excel itself. It can't spawn local subprocesses (the way Claude Desktop launches a stdio MCP server), so it talks to MCP servers over **HTTP** instead. ModelRisk MCP serves both stdio and HTTP transports.
4-
5-
This is also where the architectural payoff is: Office.js can't reach Excel's COM surface or ModelRisk's ribbon. ModelRisk MCP, running outside the sandbox, can — which means Claude for Excel can do things via this server that it structurally can't do on its own.
6-
7-
## Prerequisites
8-
9-
- Excel 2019+ with ModelRisk loaded
10-
- Claude for Excel installed (Microsoft AppSource → "Claude for Excel" by Anthropic)
11-
- ModelRisk MCP installed: `pip install modelrisk-mcp` or the standalone `.exe`
12-
13-
## 1. Start the server in HTTP mode
14-
15-
Open a PowerShell window. Generate a token first — anything random and >=24 characters. PowerShell built-in:
16-
17-
```powershell
18-
$env:MODELRISK_MCP_TOKEN = [Guid]::NewGuid().ToString("N") + [Guid]::NewGuid().ToString("N")
19-
$env:MODELRISK_MCP_TOKEN
20-
```
21-
22-
Copy the printed token — you'll paste it into Claude for Excel in step 2. Then start the server:
1+
# Claude for Excel and ModelRisk MCP — the honest status
2+
3+
> **TL;DR: Claude for Excel cannot currently reach this server. Use Claude
4+
> Desktop or Claude Code (local stdio) instead — `modelrisk-mcp install`
5+
> wires both.** This page replaces earlier instructions that described a
6+
> Settings → Connectors flow inside the add-in; those instructions were wrong
7+
> for shipping builds and are withdrawn (verified 2026-07 against a current
8+
> build, and independently confirmed by a field report).
9+
10+
## Why it can't work today
11+
12+
Claude for Excel is an Office.js add-in running in a browser WebView. Its
13+
connector model has three properties that together rule out a local,
14+
Excel-driving MCP server:
15+
16+
1. **No local Connectors panel.** The add-in's settings contain no "Add MCP
17+
server" entry. It resolves connectors from your **claude.ai account**
18+
connector list.
19+
2. **claude.ai custom connectors are remote.** The "Add custom connector"
20+
dialog takes a **remote MCP server URL** plus OAuth credentials, and those
21+
connectors are **fetched by Anthropic's infrastructure, not by your
22+
device**. A loopback URL (`http://127.0.0.1:8000/mcp`) is unreachable *by
23+
construction* — no configuration makes it work. The OAuth fields also
24+
don't match this server's bearer-token auth.
25+
3. **Local stdio servers never appear in the "Connectors" panel** on
26+
claude.ai or Claude Desktop. That's normal: the Connectors panel lists
27+
account-level remote connectors; local stdio servers are a separate,
28+
Desktop-only mechanism. Not seeing `modelrisk` there does **not** mean the
29+
install failed.
30+
31+
The server side is fine — the HTTP transport handshakes correctly and
32+
enforces its bearer token (`401` without it). The limitation is entirely in
33+
how the client resolves connectors.
34+
35+
## Do not tunnel around this
36+
37+
The only technically-possible route to Claude for Excel today would be
38+
exposing the server through a public tunnel with OAuth in front. **We do not
39+
recommend or support this.** This server can write formulas into your
40+
workbooks, run simulations, and save files to disk. A publicly reachable
41+
instance guarded by a single bearer token is a materially different security
42+
posture from the loopback bind these docs are written around. If your
43+
organisation genuinely needs an in-Excel path, contact Vose — that is a
44+
product decision (a hosted connector with real auth), not a configuration
45+
setting.
46+
47+
## What to use instead
48+
49+
| Client | Transport | Status |
50+
|---|---|---|
51+
| **Claude Desktop** | local stdio | ✅ Supported, tested — `modelrisk-mcp install` |
52+
| **Claude Code** | local stdio | ✅ Supported, tested — `modelrisk-mcp install` (0.3.11+; earlier versions wrote the wrong config file) |
53+
| Cursor / Zed / other local MCP clients | local stdio | ✅ Works; configure manually |
54+
| An MCP client running on your own machine/LAN | `--transport=streamable-http` + bearer token | ✅ Works — keep the default loopback bind |
55+
| **Claude for Excel** || ❌ Not reachable by current builds |
56+
57+
Everything this server does — building models, running simulations, reading
58+
results, charts, reports — works identically from Claude Desktop and Claude
59+
Code against the same Excel session you have open. Excel and ModelRisk are
60+
driven either way; only the chat window lives elsewhere.
61+
62+
## HTTP transport (for clients that do run on your machine)
2363

2464
```powershell
25-
modelrisk-mcp --transport=streamable-http --port=8000 --token=$env:MODELRISK_MCP_TOKEN
65+
$env:MODELRISK_MCP_TOKEN = [Guid]::NewGuid().ToString("N") * 2
66+
modelrisk-mcp --transport=streamable-http --host=127.0.0.1 --port=8000 --token=$env:MODELRISK_MCP_TOKEN
2667
```
2768

28-
You should see:
29-
30-
```
31-
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
32-
```
33-
34-
Leave the window open while you work. Closing it stops the server.
35-
36-
> **Why a token?** The HTTP endpoint binds to `127.0.0.1` (loopback) by default, so only processes on your machine can reach it. But *any* process on your machine could otherwise hit it and drive Excel — including malicious ones. A bearer token shuts that down. If you bind to a non-loopback host, the token isn't optional.
37-
38-
## 2. Add the connector in Claude for Excel
39-
40-
Open Excel, open Claude for Excel (the side-panel icon), then:
41-
42-
1. Go to **Settings → Connectors** (or the equivalent in your current Claude for Excel build).
43-
2. Click **Add MCP server**.
44-
3. Fill in:
45-
- **Name:** `modelrisk` (or anything you'll recognise)
46-
- **URL:** `http://127.0.0.1:8000/mcp`
47-
- **Authentication:** Bearer token
48-
- **Token:** paste the token from step 1
49-
4. Save.
50-
51-
Claude for Excel should report the connection as live and show the 40 ModelRisk tools.
52-
53-
## 3. First conversation
54-
55-
In Claude for Excel:
56-
57-
> Summarise the active workbook's risk model — inputs, outputs, distributions.
58-
59-
Or jump straight in:
60-
61-
> /build-risk-model
62-
63-
The same tool surface and same prompts as Claude Desktop. See [docs/demo-script.md](demo-script.md) for the headline workflow.
64-
65-
## Lifecycle tips
66-
67-
- **Token in another shell.** If you need to share the token with a second Claude for Excel session or paste it into a different config, save it to a file once and read it back. `Get-Clipboard` after the env-var line above also works.
68-
- **Stopping the server.** `Ctrl+C` in the PowerShell window. The Claude for Excel connector will go red until you start it again.
69-
- **Auto-start on boot.** Wrap the launch command in a Windows scheduled task running at logon, or a Start Menu shortcut. Keep the token out of source control.
70-
- **Two-machine setup (advanced).** Bind to `0.0.0.0`, expose the chosen port through your firewall, and use a strong token. Watch the security model carefully — this server can write to your Excel.
71-
72-
## Troubleshooting
73-
74-
### "Connection refused" or "ERR_CONNECTION_RESET"
75-
76-
The server isn't running, or it's bound to a port Claude for Excel can't reach. Check the PowerShell window — uvicorn prints `Uvicorn running on http://127.0.0.1:8000` when it's healthy.
77-
78-
### 401 Unauthorised
79-
80-
The token Claude for Excel is sending doesn't match the one the server expects. Re-paste it. Tokens are case-sensitive.
81-
82-
### Tools listed but every call hangs
83-
84-
The server is running but can't reach Excel. Confirm Excel is open and ModelRisk is loaded. Try the standalone `modelrisk-mcp --transport=stdio` with Claude Desktop first — that's a simpler topology to debug.
85-
86-
### Concurrent-writer errors
87-
88-
If Claude Desktop is running the same server over stdio at the same time, the writer mutex will reject one of them. Pick one client per session, or run the HTTP server with a different mutex name (advanced — see `src/modelrisk_mcp/safety.py`).
89-
90-
### "version": "1.27.1" in serverInfo
91-
92-
That's the FastMCP library version FastMCP currently reports, not our package version. Cosmetic, doesn't affect behaviour. The actual server is identifiable as `"name": "modelrisk-mcp"`.
93-
94-
## Security model — important
95-
96-
ModelRisk MCP over HTTP is a **local-only, single-user** integration in its default configuration. The defaults:
69+
- `POST /mcp` with `Authorization: Bearer <token>` → MCP initialize handshake.
70+
- No token → `401`.
71+
- Keep `--host=127.0.0.1` unless you fully understand the exposure of
72+
`0.0.0.0` on your network.
73+
- Add `--read-only` for a first session against a model you care about
74+
(0.3.11+): reading and analysis work; writes, simulations and saves are
75+
refused with a clear error.
9776

98-
- Loopback bind (`127.0.0.1`) — only your own machine
99-
- Bearer token required if you change the bind to a non-loopback host
100-
- No outbound network calls — the server doesn't phone home
101-
- All writes still default to `dry_run=True`; the writer mutex still serialises commits; the audit log still records every change
77+
## If Anthropic's connector model changes
10278

103-
Do **not** expose this server to the public internet. The MCP tool surface includes `replace_constant_with_distribution`, `run_simulation`, and `set_named_range` — anyone who can reach the endpoint with a valid token can drive your Excel. Treat the token like an API key.
79+
If a future Claude for Excel build adds local MCP support or a
80+
device-fetched connector mode, this page will be updated and the client
81+
re-tested before instructions are published. Watch the CHANGELOG.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "modelrisk-mcp"
3-
version = "0.3.10"
3+
version = "0.3.11"
44
description = "Open MCP server bridging Anthropic Claude (and any MCP-compatible client) with the ModelRisk Excel add-in."
55
readme = "README.md"
66
requires-python = ">=3.11"

server.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
"name": "io.github.vosesoftware/modelrisk-mcp",
44
"title": "ModelRisk",
55
"description": "Read, build, fit, and run Monte Carlo risk models in Excel through Vose Software's ModelRisk.",
6-
"version": "0.3.10",
6+
"version": "0.3.11",
77
"packages": [
88
{
99
"registryType": "pypi",
1010
"identifier": "modelrisk-mcp",
11-
"version": "0.3.10",
11+
"version": "0.3.11",
1212
"transport": {
1313
"type": "stdio"
1414
}

src/modelrisk_mcp/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "0.3.10"
1+
__version__ = "0.3.11"

src/modelrisk_mcp/__main__.py

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
33
Supports three MCP transports per the spec — stdio (default) for
44
Claude Desktop / Code / Cursor / Zed, and streamable-http or sse for
5-
Claude for Excel and other remote MCP clients that can't spawn local
6-
subprocesses.
5+
MCP clients on your own machine that speak HTTP rather than spawning a
6+
subprocess. (Claude for Excel cannot reach a local server at all — see
7+
docs/claude-for-excel.md.)
78
89
CLI:
910
@@ -36,8 +37,8 @@ def _build_parser() -> argparse.ArgumentParser:
3637
prog="modelrisk-mcp",
3738
description=(
3839
"ModelRisk MCP server — exposes ModelRisk's read/build/run "
39-
"surface to Claude Desktop, Claude Code, Claude for Excel, "
40-
"Cursor, Zed, and any MCP-compliant client."
40+
"surface to Claude Desktop, Claude Code, Cursor, Zed, and any "
41+
"local MCP-compliant client."
4142
),
4243
)
4344
p.add_argument(
@@ -47,7 +48,7 @@ def _build_parser() -> argparse.ArgumentParser:
4748
help=(
4849
"Transport protocol. 'stdio' (default) is for local clients "
4950
"like Claude Desktop; 'streamable-http' is the modern remote "
50-
"MCP transport (recommended for Claude for Excel); 'sse' is "
51+
"MCP transport for clients running on your own machine; 'sse' is "
5152
"the legacy SSE transport."
5253
),
5354
)
@@ -84,6 +85,18 @@ def _build_parser() -> argparse.ArgumentParser:
8485
"recommended for any non-loopback HTTP deployment."
8586
),
8687
)
88+
p.add_argument(
89+
"--read-only",
90+
action="store_true",
91+
help=(
92+
"Disable every mutating operation: cell/range writes, named "
93+
"ranges, simulations, and workbook saves all raise a clear "
94+
"error while the reading and analysis tools keep working. "
95+
"The recommended first-session posture when pointing the "
96+
"server at a live client model. Equivalent to setting "
97+
"MODELRISK_MCP_READ_ONLY=1."
98+
),
99+
)
87100
return p
88101

89102

@@ -144,6 +157,13 @@ def main(argv: list[str] | None = None) -> None:
144157
argv = argv[1:] # `modelrisk-mcp serve --transport=stdio` works too
145158

146159
args = _build_parser().parse_args(argv)
160+
if args.read_only:
161+
# The enforcement layer (ExcelBridge._ensure_writable) checks
162+
# this env var at call time, so setting it here covers every
163+
# transport and entry path.
164+
import os
165+
166+
os.environ["MODELRISK_MCP_READ_ONLY"] = "1"
147167
if args.transport == "stdio":
148168
_run_stdio()
149169
else:

0 commit comments

Comments
 (0)