
relentlessoldman/chatbotaddondemo
Developer toolsReference demo: add an opt-in AI assistant and MCP access to a legacy Windows desktop tool, with the customer paying for tokens.
개요
The "legacy tool" is TestBench, a pretend test-bench application (WPF app + console tool). It turns spec sheets into XML test plans, runs them on a simulated bench, writes CSV and PNG results, and analyzes them for key findings. Companies ship desktop tools that run on customer machines. Some of those customers would love to "just ask it"; others already use Claude Code or Codex and want their agent to drive the tool. This repo shows one sensible way to serve both, and each decision was made: - , and nothing leaves the PC until the user turns AI on and asks something. - : their own API key (encrypted with Windows DPAPI, the built-in per-user encryption), or better, their company's AI gateway (a proxy run by their IT that holds the vendor key). No vendor key ships in the binary. - , through the same validated code.
README
ChatbotAddonDemo 🔌
A reference demo of how to add AI to a legacy Windows desktop tool: an opt-in chat assistant for everyday users, MCP access for people who use coding agents, an MCP hub that also lets the assistant use other MCP servers, and the customer paying for their own tokens. The “legacy tool” is TestBench, a pretend test-bench application (WPF app + console tool). It turns spec sheets into XML test plans, runs them on a simulated bench, writes CSV and PNG results, and analyzes them for key findings.
Why
Companies ship desktop tools that run on customer machines. Some of those customers would love to “just ask it”; others already use Claude Code or Codex and want their agent to drive the tool. This repo shows one sensible way to serve both, and writes down why each decision was made:
- Off by default, and nothing leaves the PC until the user turns AI on and asks something.
- The customer pays: their own API key (encrypted with Windows DPAPI, the built-in per-user encryption), or better, their company’s AI gateway (a proxy run by their IT that holds the vendor key). No vendor key ships in the binary.
- AI can only do what a person could do, through the same validated code. Anything that changes data or acts on the outside world (here: creating files, starting the bench) needs a person’s OK: in the desktop app, an approval card, the same one whether the chat assistant or a connected coding agent asks; for coding agents using the console tool, the flags chosen when connecting plus the agent’s own permission prompts.
- The app computes, the model explains. The model never writes the app’s files or computes results (here: the XML and the statistics).
Words used here
| Word | Meaning |
|---|---|
| MCP | Model Context Protocol: the standard way AI plugs in outside tools. Coding agents such as Claude Code use it, and so can a chat assistant. Many products offer an MCP server (Perforce, GitHub, Microsoft 365…). |
| MCP hub | The desktop app’s one door for every AI (project Demo.Mcp). The chat panel calls the app’s tools through it, coding agents connect to the open app through it, and it connects the chat to other MCP servers. Approvals happen here. |
| Token | The unit AI providers bill by, roughly ¾ of a word, counted on everything sent and everything received. |
| Tools for AI (capability catalog) | The list of operations the AI may ask the app to run: name, a description the model reads, inputs, and a safety class. Project Demo.Capabilities. |
| Safety class | Read: only looks. Write: creates or changes files. Execute: acts on the outside world (here, the bench hardware). |
| AI gateway | A proxy run by the customer’s IT that holds the vendor key and can enforce budgets and logging. |
| Core logic | The legacy tool’s own code (Demo.Domain). Every path ends here. |
How it works
Two separate programs, four ways in. A and B existed before AI; C and D are what this demo adds. The two programs share nothing: each has its own workspace, and they never talk to each other.
flowchart LR
eng(["Engineer"])
dev(["Engineer witha coding agent"])
subgraph desktop["demo-desktop.exe (WPF app)"]
ui["Screens"]
chat["Chat panel (opt-in)runs the agent loop"]
hub["MCP hubthe one door for every AI:approvals, permissions"]
dcap["Tools for AIwhat it may do,with permissions"]
dlogic["Core logic"]
end
subgraph cli["demo-cli.exe (console tool)"]
cmds["Command-lineprocessing"]
mcp["MCP doordemo-cli mcp"]
ccap["Tools for AIwhat it may do,with permissions"]
clogic["Core logic"]
end
llm["💲 Claude / OpenAI APIbilled to the customer's key"]
agent["Claude Code / Codexruns its own agent loop"]
agentllm["💲 The agent's own modelbilled to the agent user'ssubscription or API key"]
other["Other MCP servers (optional)e.g. Perforce · GitHub · Microsoft 365chosen by the user or IT"]
dws[("Desktop app's workspacespecs · plans · runs")]
cws[("Console tool's workspacespecs · plans · runs")]
eng -- A --> ui --> dlogic
eng -- B --> cmds --> clogic
eng -- C --> chat
chat == "💲 C1: question +list of tools" ==> llm
llm == "💲 C2: reply: 'pleaserun tool X'" ==> chat
chat -- "C3: run tool X over MCP(in-process)" --> hub
hub -- "our tools" --> dcap --> dlogic
hub -- "MCP: their tools" --> other
dev -- D --> agent
agent == "💲 tokens" ==> agentllm
agent -- "D1: MCP to the open app(local only, no tokens)" --> hub
agent -- "D2: starts it; MCP overstdin/stdout (no tokens)" --> mcp --> ccap --> clogic
dlogic dws
clogic cws
classDef paid fill:#fff1c2,stroke:#d97706,color:#000
classDef new fill:#dbeafe,stroke:#2563eb,color:#000
class llm,agentllm paid
class hub,other new
💲 marks every place tokens are spent. Thick arrows are model calls; everything else is ordinary local code and file access.
Who does what: the model decides which tool to use, the loop drives (it sends the question, runs each tool the model asks for, and sends the result back), and the hub controls access (which tools there are, and which wait for the user’s approval).
The model never touches the app. It only sends back text that asks for a tool (C2). The chat panel, which is our code, asks you first if the tool would create files, use the bench or reach another MCP server (unless IT trusts that server), then runs it through the hub (C3) and sends the result back in the next request (C1 again). This repeats until the model answers in words. Which provider and model to use (Claude or OpenAI, and the model name) comes from the app’s Settings.
The hub is the desktop app’s one door for every AI. The chat panel’s tool calls go through it (in-process, so nothing extra leaves the PC). A coding agent can connect to the open app through it (D1), so it works with what you see on screen, and its changes wait for your OK in the window. And the hub connects the chat to other MCP servers, if the user or IT adds any, so the assistant can also use, say, Perforce or GitHub.
- C1 + C2 together are one billed request: tokens go out with the question, tokens come back with the reply.
- Every request carries the whole conversation so far, including earlier tool results such as spec limits and run findings. That is what leaves the PC, and why later requests in a chat cost more.
- Each exe contains its own copy of the same libraries. The two “Core logic” boxes are the same code, compiled into both programs. But they are two different programs, each with its own workspace; this demo shows both ways to add AI, not one app in two forms.
| Path | In plain words | AI? | Who pays |
|---|---|---|---|
| A | An engineer clicks around the desktop app. Unchanged legacy behavior. | No | n/a |
| B | An engineer or a script runs demo-cli commands. Unchanged legacy behavior. |
No | n/a |
| C | An engineer types in the chat panel. 💲 Our app sends the question and a list of tools to Claude/OpenAI; the model replies “call tool X”; if X would create files, use the bench or use another MCP server (unless IT trusts it), our app asks the user first; it runs X through the hub and 💲 sends the result back, until the model answers in words. | Yes, our app calls the model | Customer’s key or gateway |
| D1 | An engineer asks Claude Code or Codex to work with the open desktop app. 💲 The agent asks its own model what to do; it reaches the app’s hub through demo-desktop.exe mcp (a small relay it starts, which passes messages to the open window). Changes wait for approval in the window. Off until the user allows it in Settings. |
Yes, but only in their agent | Their own subscription or API key |
| D2 | An engineer asks Claude Code or Codex to use the console tool. 💲 The agent asks its own model what to do; when it decides to use a tool, it starts demo-cli mcp as a child process and calls the tool over MCP. |
Yes, but only in their agent | Their own subscription or API key |
Where tokens are spent
| Activity | Calls a model? | Whose tokens |
|---|---|---|
Using the screens or demo-cli commands (paths A, B) |
No | none |
| Running a plan, writing CSVs/plots, analyzing results, writing the report | No. The core logic is ordinary code | none |
| 💲 Each message in the chat panel (path C) | Yes. Several requests per message: one per model↔tool round trip, each resending the conversation so far | The customer’s API key, or their company gateway |
💲 A coding agent using the open app or demo-cli mcp (paths D1, D2) |
Our apps never do. The agent calls its own model to decide which tool to use next | The agent user’s own subscription or API key |
| The hub calling another MCP server’s tool for the chat | Not by itself, but the tool’s result goes to the chat’s model in the next request | The customer’s key or gateway (for that next request) |
| 💲 “Test connection” in Settings | Yes, one tiny request | The customer’s key or gateway |
--demo mode, the screenshots, dotnet test |
No. A scripted fake model stands in | none |
💲 DEMO_LIVE_TESTS=1 |
Yes: a short chat per provider, and the whole walkthrough job with each provider and with Claude Code and Codex (through demo-cli mcp) |
Whoever’s key is in the environment, and the agent users’ accounts |
The important points:
- The model never touches files or the bench. It can only ask for a tool; our code decides and runs it.
- The tools for AI (capability catalog) are defined once: 13 tools, each with a name and description the model reads, its inputs, and a safety class (Read / Write / Execute). Both programs serve them over MCP. The desktop app adds one more read-only tool,
get_current_view, so the assistant or an agent can see which plan or run you’re looking at. Every path ends in the same core logic, so the same rules apply to everyone. - In the desktop app, every AI goes through the hub, so approvals and permissions live in one place: the chat and connected agents get the same approval cards. Agents see only the app’s own tools, never the other MCP servers’. Other servers’ tools always ask first, unless IT marks a server as safe; the read-only labels such servers put on their own tools aren’t trusted.
Full explanation, with step-by-step sequence diagrams and the rules for every chat message: docs/design.md. What leaves the PC on each path is drawn in docs/security.md.
Applying this to your own tool
TestBench is only the example. For your own desktop tool, the existing parts stay as they are and you add these new ones (blue):
flowchart TB
subgraph before["Before"]
direction TB
p1(["People"])
s1["Your screens"]
c1["Your command line"]
l1["Your core logic"]
p1 --> s1 --> l1
p1 --> c1 --> l1
end
subgraph after["After"]
direction TB
p2(["People"])
a2(["People witha coding agent"])
s2["Your screens"]
chat["Chat panelopt-in, customer's key"]
hub["MCP hubin your app"]
c2["Your command line"]
mcp["MCP modeof your command line"]
tools["Tools for AIname, description, inputs,safety class"]
other["Other MCP servers(optional)"]
l2["Your core logicunchanged"]
p2 --> s2 --> l2
p2 --> c2 --> l2
p2 --> chat --> hub
a2 --> hub
a2 --> mcp
hub --> tools
hub --> other
mcp --> tools
tools --> l2
end
before ~~~ after
classDef new fill:#dbeafe,stroke:#2563eb,color:#000
class chat,hub,mcp,tools,other new
- Tools for AI: a class that wraps the operations you already have, one method each, with a description for the model and a safety class (Read / Write / Execute). Here:
TestBenchCapabilities.cs. - Chat panel: connects to the provider with the customer’s key or gateway, sends the tools with each message, and shows an approval card before anything that isn’t Read. Here:
Demo.Aiplus the panel inDemo.Desktop. - MCP hub: serves the tools to the chat panel in-process, lets coding agents connect to the open app (with the same approval cards), and connects the chat to other MCP servers. Here:
Demo.Mcpplusdemo-desktop.exe mcp, the relay an agent starts. - MCP mode: one more command on your console tool that serves the same tools to coding agents over stdin/stdout. Here:
demo-cli mcpinDemo.Cli.
The core logic isn’t changed for AI, so its rules apply to every path.
Walkthrough
These screenshots are generated by scripts/capture-screenshots.ps1, which drives the real apps. They are never taken by hand.
1. The legacy tool, unchanged. Test plans (XML), runs with findings and plots, and the customer’s spec sheets. No AI anywhere until someone opts in.
2. Opting in. Settings explains what gets sent and who pays, then asks for a key (or uses the company gateway).
3. Asking in plain English. “Make a hot/cold plan for the LDO-3300 from its datasheet … then run it.” The assistant reads the spec sheets on its own, but creating the plan file needs approval:
4. Using the bench needs a second approval. The new plan TP-1002 is already in the grid, validated and saved by the app, not written by the model.
5. Results. The run executes on the (simulated) bench. The app computes the findings: one unit (DUT 3) fails quiescent current at 125 °C while the others pass, so it’s a bad part rather than a design problem. The assistant explains them, and you can ask follow-ups about specific conditions:
The run’s real output files: results.csv, summary.csv, a plot per test, and report.html (when requested: Write report button, generate_report or demo-cli report):
6. Other MCP servers and coding agents. In Settings, the user can give the assistant other MCP servers (IT can add its own, or allow only its own), and let coding agents work with the open window:
7. A coding agent working with the open app (path D1). The agent connects through demo-desktop.exe mcp. It looks around on its own (here: what you’re looking at, and the spec sheets), but its change waits for the same approval card the assistant gets. (In the screenshot, an MCP client stands in for Claude Code.)
8. The console tool, used exactly as before with no AI involved:
9. A coding agent using the console tool (path D2). The agent starts demo-cli mcp, sees the tools (each marked with its safety class: read / write / exec), and plans, runs and analyzes:
Usage
Requires Windows and the .NET 10 SDK.
dotnet build ChatbotAddonDemo.sln
# Desktop app in demo mode: fresh example workspace in %TEMP%, scripted assistant, no key needed.
.\src\Demo.Desktop\bin\Debug\net10.0-windows\demo-desktop.exe --demo
# Desktop app for real (workspace in %LOCALAPPDATA%\ChatbotAddonDemo\workspace); turn on AI in Settings
.\src\Demo.Desktop\bin\Debug\net10.0-windows\demo-desktop.exe
# Console tool
.\src\Demo.Cli\bin\Debug\net10.0\demo-cli.exe help
# Tests. Nothing billed: the AI paths talk to a fake AI gateway on 127.0.0.1. The UI tests open the app off-screen.
# GitHub runs the same tests, except the UI tests, on every push to main and every pull request (.github/workflows/build.yml).
dotnet test ChatbotAddonDemo.sln
# 💲 Paid live tests, only when you explicitly opt in: the same job with real Claude and OpenAI (path C)
# and real Claude Code and Codex (path D2). Roughly $1 in all; the Claude chat and Claude Code stop at $0.50 each,
# the OpenAI chat at 200,000 tokens, Codex at 5 minutes. Each test skips itself if its key or agent is missing.
# The switch is removed again afterwards, so a later plain `dotnet test` in this window costs nothing.
$env:DEMO_LIVE_TESTS = '1'; dotnet test --filter Category=Live; Remove-Item Env:DEMO_LIVE_TESTS
# Re-render every diagram to PNG (docs/images/diagrams) after editing one. Needs Node.js, Chrome or Edge, and
# internet access the first time (it downloads the Mermaid renderer from npm). Headless.
node scripts/render-diagrams.mjs
# Regenerate every screenshot in docs/images (the apps open off-screen)
powershell -ExecutionPolicy Bypass -File scripts\capture-screenshots.ps1
Environment variables (all optional):
| Variable | Effect |
|---|---|
DEMO_WORKSPACE |
Workspace folder for demo-cli. A --workspace argument wins over it. Default: %LOCALAPPDATA%\ChatbotAddonDemoCli\workspace. (The desktop app has a workspace of its own: --workspace, else %LOCALAPPDATA%\ChatbotAddonDemo\workspace, or with --demo a throwaway folder in %TEMP%.) |
DEMO_BENCH_DELAY_MS |
0 makes the simulated bench instant (the tests use this). Any other value, or unset, runs at demo speed (a few seconds per run). |
ANTHROPIC_API_KEY, OPENAI_API_KEY |
A personal key for the chat panel, used when no company gateway is set (see the IT admin guide). |
💲 DEMO_LIVE_TESTS |
1 enables the paid live-API tests. |
DEMO_CLAUDE_EXE, DEMO_CODEX_EXE |
Where the live tests find Claude Code or Codex, if not on PATH. |
Connect a coding agent, to the open desktop app or to the console tool: docs/guides/connecting-a-coding-agent.md.
Docs
| Document | For |
|---|---|
| Design | Everyone: who calls what, why each piece exists, diagrams |
| Decision records | Why each major choice was made, and what we rejected |
| Security notes | Threats and mitigations |
| Using the assistant | End users |
| Connecting a coding agent | Claude Code / Codex users |
| IT admin deployment | Rolling out keys or an AI gateway, MCP servers, agent access |
Layout
src/
Demo.Domain/ Core logic: plans (XML + XSD), spec import, simulated bench, result files, plots, analysis. No AI.
Demo.Capabilities/ The tools for AI (capability catalog): what AI may do, with a safety class each (Read / Write / Execute), defined once.
Demo.Ai/ Calling Claude/OpenAI (IChatClient), chat loop with approvals, credentials, cost meter.
Demo.Mcp/ MCP: the desktop app's hub (chat, coding agents, other MCP servers) and the tools-to-MCP adapter.
Demo.Desktop/ demo-desktop.exe, the WPF app: the legacy screens + the opt-in chat panel + the hub's agent panel.
Demo.Cli/ demo-cli.exe, the console tool: the legacy commands + `demo-cli mcp` for coding agents.
tests/ Domain, capabilities, AI (scripted model), hub, CLI/MCP end-to-end, every path with the same job, desktop UI tests.
tools/Demo.Screenshots Drives the apps and renders docs/images.
docs/ Design, decision records, security, guides, images.
scripts/ capture-screenshots.ps1, render-diagrams.mjs
Contributing
This is a personal project, published as-is — issues and pull requests aren’t accepted (PRs auto-close). Fork it and make it your own. 🔌
License
MIT — see LICENSE. © 2026 RelentlessOldMan.
설치
This server does not publish a one-line install command.
Open the repository installation guide