@nestpilot/mcp-app 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. package/README.md +350 -0
  2. package/dist/cli/doctor.d.ts +1 -0
  3. package/dist/cli/doctor.js +214 -0
  4. package/dist/cli/export-import.d.ts +6 -0
  5. package/dist/cli/export-import.js +132 -0
  6. package/dist/cli/index.d.ts +2 -0
  7. package/dist/cli/index.js +168 -0
  8. package/dist/cli/init.d.ts +1 -0
  9. package/dist/cli/init.js +171 -0
  10. package/dist/host-configs/cowork.json +11 -0
  11. package/dist/host-configs/goose.yaml +22 -0
  12. package/dist/host-configs/openclaw-manifest.json +16 -0
  13. package/dist/main.d.ts +2 -0
  14. package/dist/main.js +128 -0
  15. package/dist/mcp-app.html +155 -0
  16. package/dist/nestpilot-client.d.ts +44 -0
  17. package/dist/nestpilot-client.js +160 -0
  18. package/dist/planner.html +222 -0
  19. package/dist/server.d.ts +19 -0
  20. package/dist/server.js +245 -0
  21. package/dist/skills/SKILL.md +162 -0
  22. package/dist/skills/manifest.json +51 -0
  23. package/dist/skills/tools/activate_plan.md +36 -0
  24. package/dist/skills/tools/coach.md +59 -0
  25. package/dist/skills/tools/comprehensive_plan.md +65 -0
  26. package/dist/skills/tools/create_plan.md +59 -0
  27. package/dist/skills/tools/create_saved_plan.md +49 -0
  28. package/dist/skills/tools/delete_plan.md +42 -0
  29. package/dist/skills/tools/delete_scenario.md +38 -0
  30. package/dist/skills/tools/generate_proposal.md +63 -0
  31. package/dist/skills/tools/generate_retirement_report.md +50 -0
  32. package/dist/skills/tools/get_active_plan.md +44 -0
  33. package/dist/skills/tools/get_baseline_forecast.md +47 -0
  34. package/dist/skills/tools/get_plan.md +44 -0
  35. package/dist/skills/tools/get_plan_components.md +50 -0
  36. package/dist/skills/tools/get_scenario.md +46 -0
  37. package/dist/skills/tools/list_plans.md +44 -0
  38. package/dist/skills/tools/list_scenarios.md +42 -0
  39. package/dist/skills/tools/medicare-guardian.md +59 -0
  40. package/dist/skills/tools/nestpilot_run_plan.md +61 -0
  41. package/dist/skills/tools/optimize_roth_conversion.md +107 -0
  42. package/dist/skills/tools/optimize_ss_claiming.md +30 -0
  43. package/dist/skills/tools/rename_plan.md +34 -0
  44. package/dist/skills/tools/retirement-planner.md +55 -0
  45. package/dist/skills/tools/run_forecast.md +65 -0
  46. package/dist/skills/tools/run_saved_forecast.md +52 -0
  47. package/dist/skills/tools/run_scenario.md +66 -0
  48. package/dist/skills/tools/save_plan.md +48 -0
  49. package/dist/skills/tools/save_scenario.md +50 -0
  50. package/dist/skills/tools/verify_forecast.md +43 -0
  51. package/dist/src/config.d.ts +20 -0
  52. package/dist/src/config.js +44 -0
  53. package/dist/src/contracts/provenance.d.ts +37 -0
  54. package/dist/src/contracts/provenance.js +71 -0
  55. package/dist/src/contracts/tool-contract-registry.d.ts +43 -0
  56. package/dist/src/contracts/tool-contract-registry.js +282 -0
  57. package/dist/src/local/cloud-compute-client.d.ts +55 -0
  58. package/dist/src/local/cloud-compute-client.js +135 -0
  59. package/dist/src/local/encryption.d.ts +24 -0
  60. package/dist/src/local/encryption.js +105 -0
  61. package/dist/src/local/keychain.d.ts +41 -0
  62. package/dist/src/local/keychain.js +236 -0
  63. package/dist/src/local/local-config.d.ts +34 -0
  64. package/dist/src/local/local-config.js +61 -0
  65. package/dist/src/local/local-data-layer.d.ts +20 -0
  66. package/dist/src/local/local-data-layer.js +15 -0
  67. package/dist/src/local/local-plan-store.d.ts +66 -0
  68. package/dist/src/local/local-plan-store.js +195 -0
  69. package/dist/src/local/pii-scrubber.d.ts +26 -0
  70. package/dist/src/local/pii-scrubber.js +219 -0
  71. package/dist/src/policy/policy-engine.d.ts +44 -0
  72. package/dist/src/policy/policy-engine.js +119 -0
  73. package/dist/src/rate-limit.d.ts +17 -0
  74. package/dist/src/rate-limit.js +41 -0
  75. package/dist/src/security.d.ts +19 -0
  76. package/dist/src/security.js +118 -0
  77. package/dist/src/skills/index.d.ts +12 -0
  78. package/dist/src/skills/index.js +16 -0
  79. package/dist/src/skills/retirement-pack-v1.d.ts +28 -0
  80. package/dist/src/skills/retirement-pack-v1.js +295 -0
  81. package/dist/src/skills/skill-executor.d.ts +65 -0
  82. package/dist/src/skills/skill-executor.js +174 -0
  83. package/dist/src/skills/skill-manifest-schema.d.ts +337 -0
  84. package/dist/src/skills/skill-manifest-schema.js +94 -0
  85. package/dist/src/skills/skill-registry.d.ts +71 -0
  86. package/dist/src/skills/skill-registry.js +116 -0
  87. package/dist/src/telemetry.d.ts +12 -0
  88. package/dist/src/telemetry.js +59 -0
  89. package/dist/src/types.d.ts +46 -0
  90. package/dist/src/types.js +4 -0
  91. package/dist/tools/agent-tools.d.ts +12 -0
  92. package/dist/tools/agent-tools.js +141 -0
  93. package/dist/tools/forecast-management-tools.d.ts +9 -0
  94. package/dist/tools/forecast-management-tools.js +133 -0
  95. package/dist/tools/local-plan-tools.d.ts +8 -0
  96. package/dist/tools/local-plan-tools.js +357 -0
  97. package/dist/tools/mcp-helpers.d.ts +52 -0
  98. package/dist/tools/mcp-helpers.js +177 -0
  99. package/dist/tools/medicare-tools.d.ts +3 -0
  100. package/dist/tools/medicare-tools.js +162 -0
  101. package/dist/tools/optimize-roth-tools-test.d.ts +2 -0
  102. package/dist/tools/optimize-roth-tools-test.js +36 -0
  103. package/dist/tools/optimize-roth-tools.d.ts +3 -0
  104. package/dist/tools/optimize-roth-tools.js +818 -0
  105. package/dist/tools/plan-management-tools.d.ts +3 -0
  106. package/dist/tools/plan-management-tools.js +196 -0
  107. package/dist/tools/planning-tools.d.ts +3 -0
  108. package/dist/tools/planning-tools.js +290 -0
  109. package/dist/tools/proposal-tools.d.ts +3 -0
  110. package/dist/tools/proposal-tools.js +428 -0
  111. package/dist/tools/report-tools.d.ts +3 -0
  112. package/dist/tools/report-tools.js +245 -0
  113. package/dist/tools/scenario-management-tools.d.ts +3 -0
  114. package/dist/tools/scenario-management-tools.js +136 -0
  115. package/dist/views/verification-packet.html +211 -0
  116. package/host-configs/cowork.json +11 -0
  117. package/host-configs/goose.yaml +22 -0
  118. package/host-configs/openclaw-manifest.json +16 -0
  119. package/package.json +66 -0
  120. package/skills/SKILL.md +162 -0
  121. package/skills/manifest.json +51 -0
  122. package/skills/tools/activate_plan.md +36 -0
  123. package/skills/tools/coach.md +59 -0
  124. package/skills/tools/comprehensive_plan.md +65 -0
  125. package/skills/tools/create_plan.md +59 -0
  126. package/skills/tools/create_saved_plan.md +49 -0
  127. package/skills/tools/delete_plan.md +42 -0
  128. package/skills/tools/delete_scenario.md +38 -0
  129. package/skills/tools/generate_proposal.md +63 -0
  130. package/skills/tools/generate_retirement_report.md +50 -0
  131. package/skills/tools/get_active_plan.md +44 -0
  132. package/skills/tools/get_baseline_forecast.md +47 -0
  133. package/skills/tools/get_plan.md +44 -0
  134. package/skills/tools/get_plan_components.md +50 -0
  135. package/skills/tools/get_scenario.md +46 -0
  136. package/skills/tools/list_plans.md +44 -0
  137. package/skills/tools/list_scenarios.md +42 -0
  138. package/skills/tools/medicare-guardian.md +59 -0
  139. package/skills/tools/nestpilot_run_plan.md +61 -0
  140. package/skills/tools/optimize_roth_conversion.md +107 -0
  141. package/skills/tools/optimize_ss_claiming.md +30 -0
  142. package/skills/tools/rename_plan.md +34 -0
  143. package/skills/tools/retirement-planner.md +55 -0
  144. package/skills/tools/run_forecast.md +65 -0
  145. package/skills/tools/run_saved_forecast.md +52 -0
  146. package/skills/tools/run_scenario.md +66 -0
  147. package/skills/tools/save_plan.md +48 -0
  148. package/skills/tools/save_scenario.md +50 -0
  149. package/skills/tools/verify_forecast.md +43 -0
package/README.md ADDED
@@ -0,0 +1,350 @@
1
+ # NestPilot MCP App
2
+
3
+ NestPilot retirement planning tools and interactive apps, distributed as an MCP server via the `@modelcontextprotocol/ext-apps` SDK. Runs in any MCP-compatible host (Claude Desktop, ChatGPT, GitHub Copilot, VS Code, MCPJam, basic-host).
4
+
5
+ Migrated from `apps/chatgpt-app/` (Python FastApps / OpenAI Apps SDK).
6
+
7
+ **Features**: FEAT-0052 (Medicare Guardian), FEAT-0001 (Plan Creation), FEAT-0002 (Forecast), FEAT-0003 (Scenarios), FEAT-0066 (Roth Conversion)
8
+
9
+ ## Tools
10
+
11
+ ### Planning Tools
12
+
13
+ | Tool | Visibility | Backend Endpoint | Purpose |
14
+ |------|-----------|------------------|---------|
15
+ | `create_plan` | model | `POST /api/plan/calc` | Quick retirement projection (balance, withdrawal, readiness score) |
16
+ | `run_forecast` | model | `POST /api/plan/forecast` | Comprehensive multi-account, multi-income forecast with yearly projections |
17
+ | `run_scenario` | model | `POST /api/plan/scenario/run` | What-if scenario analysis with plan deltas, component overrides, stress tests |
18
+ | `verify_forecast` | model | `POST /api/plan/forecast/verify` | Forecast verification with auditable calculation trace |
19
+ | `coach` | model | `POST /api/spring_ai/coach` | AI-powered retirement coaching and concept explanations |
20
+
21
+ ### Roth Conversion Tools
22
+
23
+ | Tool | Visibility | Backend Endpoint | Purpose |
24
+ |------|-----------|------------------|---------|
25
+ | `optimize_roth_conversion` | model | `POST /api/tax/roth/max-conversion` | Multi-year Roth conversion strategy with tax analysis, IRMAA warnings, RMD impact |
26
+
27
+ **Note**: This tool is currently under development (FEAT-0066).
28
+
29
+ ### Medicare Tools
30
+
31
+ | Tool | Visibility | Backend Endpoint | Purpose |
32
+ |------|-----------|------------------|---------|
33
+ | `medicare-guardian` | model + app | — (renders UI) | Launches the interactive Medicare Guardian UI |
34
+ | `medicare-analyze` | app (internal) | `POST /api/medicare/guardian/analyze` | Proxies analysis to NestPilot backend API |
35
+ | `email-subscribe` | app (internal) | `POST /api/medicare/subscribe` | Proxies email subscription to backend API |
36
+
37
+ ### Agent Runtime Tools
38
+
39
+ | Tool | Visibility | Backend Endpoint | Purpose |
40
+ |------|-----------|------------------|---------|
41
+ | `nestpilot_list_skills` | model | `GET /skills` (agent-runtime) | Lists all available agent skills |
42
+ | `nestpilot_run_skill` | model | `POST /sessions/:id/messages` | Runs a specific agent skill within a session |
43
+ | `nestpilot_run_plan` | model | `POST /sessions/:id/messages` | Full retirement readiness assessment via retirement-readiness skill |
44
+
45
+ ### Interactive Views
46
+
47
+ | View | Resource URI | Purpose |
48
+ |------|-------------|---------|
49
+ | `retirement-planner` | `ui://retirement-planner/planner-v2.html` | Interactive retirement planner with charts and scenario comparison |
50
+ | `verification-packet` | `ui://verification-packet/verification-packet.html` | Verification packet display for forecast audit trails |
51
+
52
+ ## Quick Start
53
+
54
+ ```bash
55
+ npm install
56
+ npm run build
57
+ cp .env.example .env
58
+ npm run serve:auth # HTTP transport on port 3001 (auth enforced)
59
+ # or
60
+ npm run serve:auth:stdio # stdio transport for Claude Desktop
61
+ ```
62
+
63
+ ## Testing the MCP Server
64
+
65
+ ### 1. Test with Custom Script
66
+
67
+ ```bash
68
+ # Build the MCP app first
69
+ npm run build
70
+
71
+ # Start the MCP server (HTTP transport with auth)
72
+ npm run serve:auth
73
+
74
+ # In another terminal, run the test script
75
+ node test-mcp-tools.mjs
76
+ ```
77
+
78
+ The test script `test-mcp-tools.mjs` tests all major tools:
79
+ - `optimize_roth_conversion` (Roth optimization)
80
+ - `create_plan` (Quick projection)
81
+ - `run_forecast` (Multi-account forecast)
82
+ - `coach` (AI coaching)
83
+ - `nestpilot_list_skills` (Agent skills)
84
+ - `verify_forecast` (Verification)
85
+
86
+ ### 2.Test with GitHub Copilot (VS Code)
87
+
88
+ Add to `.github/claude_desktop_config.json` or VS Code's MCP settings:
89
+
90
+ ```json
91
+ {
92
+ "mcpServers": {
93
+ "nestpilot": {
94
+ "command": "npx",
95
+ "args": ["tsx", "C:\\git\\javapan\\nestpilot\\apps\\mcp-app\\main.ts", "--stdio"]
96
+ }
97
+ }
98
+ }
99
+ ```
100
+
101
+ Then in GitHub Copilot Chat:
102
+ - "List available NestPilot tools"
103
+ - "Create a retirement plan for someone who is 45, wants to retire at 65, has $300k saved"
104
+ - "What's the 4% rule?" (tests coach tool)
105
+
106
+ ### 3. Test with basic-host
107
+
108
+ ```bash
109
+ # Terminal 1: Run this server
110
+ npm start
111
+
112
+ # Terminal 2: Run basic-host from the ext-apps repo
113
+ cd c:\git\ref\ext-apps\examples\basic-host
114
+ npm install
115
+ SERVERS='["http://localhost:3001/mcp"]' npm run start
116
+ # Open http://localhost:8080
117
+ ```
118
+
119
+ ### 4. Test with Claude Desktop
120
+
121
+ Add to `%APPDATA%\Claude\claude_desktop_config.json`:
122
+
123
+ ```json
124
+ {
125
+ "mcpServers": {
126
+ "nestpilot": {
127
+ "command": "npx",
128
+ "args": ["tsx", "C:\\git\\javapan\\nestpilot\\apps\\mcp-app\\main.ts", "--stdio"]
129
+ }
130
+ }
131
+ }
132
+ ```
133
+
134
+ Restart Claude Desktop, then test:
135
+ - "What tools do you have from NestPilot?"
136
+ - "Help me plan my retirement. I'm 40, have $250k saved, contributing $2k/month"
137
+
138
+ ### 5. Manual Testing with curl/PowerShell
139
+
140
+ ```powershell
141
+ # List all tools
142
+ $payload = @{
143
+ jsonrpc = "2.0"
144
+ id = "test1"
145
+ method = "tools/list"
146
+ params = @{}
147
+ } | ConvertTo-Json
148
+
149
+ Invoke-RestMethod -Uri "http://localhost:3001/mcp" `
150
+ -Method POST `
151
+ -ContentType "application/json" `
152
+ -Headers @{ Accept = "application/json, text/event-stream" } `
153
+ -Body $payload
154
+
155
+ # Call create_plan tool
156
+ $payload = @{
157
+ jsonrpc = "2.0"
158
+ id = "test2"
159
+ method = "tools/call"
160
+ params = @{
161
+ name = "create_plan"
162
+ arguments = @{
163
+ currentAge = 40
164
+ retireAge = 65
165
+ lifeExpectancy = 95
166
+ currentBalance = 250000
167
+ monthlyContribution = 2000
168
+ realReturnRate = 0.05
169
+ withdrawalRate = 0.04
170
+ targetAnnualSpending = 60000
171
+ }
172
+ }
173
+ } | ConvertTo-Json -Depth 10
174
+
175
+ Invoke-RestMethod -Uri "http://localhost:3001/mcp" `
176
+ -Method POST `
177
+ -ContentType "application/json" `
178
+ -Headers @{ Accept = "application/json, text/event-stream" } `
179
+ -Body $payload
180
+ ```
181
+
182
+ ## Testing in MCP Hosts
183
+
184
+ ### GitHub Copilot (Recommended for Development)
185
+
186
+ 1. **Setup**: Register the MCP server in VS Code settings or GitHub Copilot configuration
187
+ 2. **Verification**: Type `@nestpilot` in Copilot Chat to see available tools
188
+ 3. **Testing**: Use natural language queries that match tool descriptions
189
+ 4. **Debugging**: Check VS Code Output panel → "MCP" channel for errors
190
+
191
+ ### Claude Desktop
192
+
193
+ 1. **Setup**: Edit `claude_desktop_config.json` with server configuration
194
+ 2. **Verification**: Ask "What NestPilot tools are available?"
195
+ 3. **Testing**: Provide detailed retirement scenarios in conversation
196
+ 4. **Debugging**: Check Claude Desktop logs: `%APPDATA%\Claude\logs\`
197
+
198
+ ### MCPJam Inspector
199
+
200
+ ```bash
201
+ npx @mcpjam/inspector@latest
202
+ ```
203
+
204
+ 1. Connect to `http://localhost:3001/mcp`
205
+ 2. Browse available tools in the inspector UI
206
+ 3. Test tools with custom JSON payloads
207
+ 4. View real-time request/response
208
+
209
+ ## Project Structure
210
+
211
+ ```
212
+ apps/mcp-app/
213
+ ├── server.ts # MCP server orchestrator
214
+ ├── main.ts # Entry point: HTTP/stdio transport
215
+ ├── nestpilot-client.ts # HTTP client for NestPilot backend API
216
+ ├── tools/
217
+ │ ├── mcp-helpers.ts # Shared: toCallToolResult(), proxyPostTool()
218
+ │ ├── medicare-tools.ts # Medicare tools (guardian, analyze, subscribe)
219
+ │ ├── planning-tools.ts # Planning tools (create_plan, run_forecast, run_scenario, verify_forecast, coach)
220
+ │ ├── optimize-roth-tools.ts # Roth conversion optimization (FEAT-0066) [IN DEVELOPMENT]
221
+ │ └── agent-tools.ts # Agent runtime tools (list_skills, run_skill, run_plan)
222
+ ├── mcp-app.html # Vite entry point for the Medicare Guardian view
223
+ ├── src/
224
+ │ ├── planner/
225
+ │ │ └── planner-app.tsx # Retirement planner interactive view
226
+ │ ├── mcp-app.tsx # Medicare Guardian React app (useApp hook)
227
+ │ ├── types.ts # Shared TypeScript types
228
+ │ ├── styles.ts # CSS-in-JS styles with host variable fallbacks
229
+ │ ├── global.css # Global CSS with host variable fallbacks
230
+ │ └── components/
231
+ │ ├── TimelineChecker.tsx # Input form for user data
232
+ │ ├── ResultsPanel.tsx # Results display (score, deadlines, penalties)
233
+ │ ├── EmailCapture.tsx # Email subscription form
234
+ │ ├── CitationsDrawer.tsx # Expandable citations section
235
+ │ ├── SEPInterview.tsx # Special Enrollment Period checker
236
+ │ └── FunnelCTA.tsx # Cross-sell to full NestPilot platform
237
+ ├── tests/
238
+ │ ├── server.test.ts # Server tool and client tests (vitest)
239
+ │ └── contract-policy.test.ts # Contract validation tests
240
+ ├── dist/ # Build output (generated)
241
+ │ ├── mcp-app.html # Bundled Medicare Guardian single-file HTML
242
+ │ ├── planner.html # Bundled retirement planner single-file HTML
243
+ │ └── views/
244
+ │ └── verification-packet.html # Bundled verification packet view
245
+ ├── test-mcp-tools.mjs # Comprehensive MCP tools test script
246
+ ├── package.json
247
+ ├── vite.config.ts
248
+ ├── vite.planner.config.ts
249
+ ├── vite.verification.config.ts
250
+ └──tsconfig.json
251
+ ```
252
+
253
+ ## Architecture
254
+
255
+ ```
256
+ User in MCP Host (Claude / ChatGPT / GitHub Copilot / VS Code / MCPJam)
257
+ |
258
+ v
259
+ MCP Protocol (stdio or HTTP)
260
+ |
261
+ v
262
+ MCP Server (server.ts — orchestrator)
263
+ |
264
+ ├── tools/planning-tools.ts
265
+ │ ├── create_plan --> POST /api/plan/calc
266
+ │ ├── run_forecast --> POST /api/plan/forecast
267
+ │ ├── run_scenario --> POST /api/plan/scenario/run
268
+ │ ├── verify_forecast --> POST /api/plan/forecast/verify
269
+ │ └── coach --> POST /api/spring_ai/coach
270
+ |
271
+ ├── tools/roth-tools.ts
272
+ │ └── optimize_roth_conversion --> POST /api/tax/roth/max-conversion
273
+ |
274
+ ├── tools/medicare-tools.ts
275
+ │ ├── medicare-guardian --> renders UI resource (mcp-app.html)
276
+ │ ├── medicare-analyze --> POST /api/medicare/guardian/analyze
277
+ │ └── email-subscribe --> POST /api/medicare/subscribe
278
+ |
279
+ ├── tools/agent-tools.ts
280
+ │ ├── nestpilot_list_skills --> GET /skills (agent-runtime:3002)
281
+ │ ├── nestpilot_run_skill --> POST /sessions/:id/messages
282
+ │ └── nestpilot_run_plan --> POST /sessions/:id/messages
283
+ |
284
+ └── tools/mcp-helpers.ts
285
+ ├── toCallToolResult() -- wraps payload into MCP CallToolResult
286
+ └── proxyPostTool() -- POST to backend + error handling
287
+ |
288
+ v
289
+ NestPilot Spring Boot API (port 8080)
290
+ |
291
+ v
292
+ PostgreSQL
293
+ ```
294
+
295
+ Planning tools are thin facades — they validate input via Zod schemas and proxy to the Spring Boot API via `nestpilot-client.ts`. The Medicare Guardian view (React app in iframe) communicates with the server via `app.callServerTool()`.
296
+
297
+ ## Environment Variables
298
+
299
+ | Variable | Default | Description |
300
+ |----------|---------|-------------|
301
+ | `NESTPILOT_BACKEND_URL` | `http://localhost:8080` | NestPilot Spring Boot API URL |
302
+ | `AGENT_RUNTIME_URL` | `http://localhost:3002` | Agent Runtime service URL |
303
+ | `PORT` | `3001` | HTTP transport port |
304
+ | `MCP_REQUIRE_AUTH` | `true` | Require bearer authentication on HTTP `/mcp` |
305
+ | `MCP_AUTH_MODE` | `token` | Auth mode: `token` or `jwt-passthrough` |
306
+ | `MCP_AUTH_TOKENS` | (none) | Comma-separated bearer tokens (required in `token` mode when auth is enabled) |
307
+ | `MCP_DEFAULT_USER_ID` | `anonymous` | Fallback user id when auth is disabled |
308
+ | `MCP_RATE_LIMIT_WINDOW_MS` | `60000` | Rate limit window in milliseconds |
309
+ | `MCP_RATE_LIMIT_MAX_REQUESTS` | `60` | Max requests per window |
310
+
311
+ ## Commands
312
+
313
+ ```bash
314
+ npm run build # Vite build -> dist/*.html
315
+ npm run serve # Start MCP server (HTTP transport)
316
+ npm run serve:auth # Start MCP server with auth validation + .env loading
317
+ npm run serve:auth:stdio # Start stdio mode with auth env validation
318
+ npm run start # Build + serve
319
+ npm test # Run vitest tests
320
+ node test-mcp-tools.mjs # Run comprehensive tool tests
321
+ ```
322
+
323
+ ## Known Issues
324
+
325
+ - **optimize_roth_conversion** tool (FEAT-0066) is currently under development and may not be fully functional
326
+ - Console.log output in tool registration appears suppressed in HTTP transport mode
327
+ - Tool registration happens per-request for HTTP transport, not at server startup
328
+
329
+ ## Migration from chatgpt-app
330
+
331
+ | chatgpt-app (Python) | mcp-app (TypeScript) |
332
+ |----------------------|---------------------|
333
+ | `FastApps` / `WidgetMCPServer` | `McpServer` + `registerAppTool` |
334
+ | `BaseWidget` subclasses | `server.tool()` / `registerAppTool()` |
335
+ | `window.openai.callTool()` | `app.callServerTool()` |
336
+ | `useWidgetProps()` (FastApps) | `useApp()` (MCP Apps SDK) |
337
+ | `structuredContent` / `_meta` | `CallToolResult.content` |
338
+ | React JSX (JavaScript) | React TSX (TypeScript) |
339
+ | Python httpx | Node.js fetch |
340
+ | `fastapps.json` CSP | Single-file bundle (no CSP needed) |
341
+ |----------------------|---------------------|
342
+ | `FastApps` / `WidgetMCPServer` | `McpServer` + `registerAppTool` |
343
+ | `BaseWidget` subclasses | `server.tool()` / `registerAppTool()` |
344
+ | `window.openai.callTool()` | `app.callServerTool()` |
345
+ | `useWidgetProps()` (FastApps) | `useApp()` (MCP Apps SDK) |
346
+ | `structuredContent` / `_meta` | `CallToolResult.content` |
347
+ | React JSX (JavaScript) | React TSX (TypeScript) |
348
+ | Python httpx | Node.js fetch |
349
+ | `fastapps.json` CSP | Single-file bundle (no CSP needed) |
350
+
@@ -0,0 +1 @@
1
+ export declare function doctorCommand(): Promise<void>;
@@ -0,0 +1,214 @@
1
+ /**
2
+ * `nestpilot doctor` — diagnoses common issues with the local installation.
3
+ *
4
+ * Checks:
5
+ * 1. Data directory exists
6
+ * 2. Config file is valid JSON
7
+ * 3. API key is present (keychain or env)
8
+ * 4. Encryption key is in keychain
9
+ * 5. Cloud API is reachable
10
+ * 6. Node.js version ≥ 18
11
+ * 7. Plans directory is readable
12
+ * 8. Disk space is sufficient
13
+ *
14
+ * @feature FEAT-0088
15
+ */
16
+ import fs from "node:fs/promises";
17
+ import path from "node:path";
18
+ import { loadLocalConfig, configFilePath, plansDir, } from "../src/local/local-config.js";
19
+ import { createKeychainProvider } from "../src/local/keychain.js";
20
+ // ── Doctor command ───────────────────────────────────────────────────────
21
+ export async function doctorCommand() {
22
+ console.log("\n🩺 NestPilot Doctor");
23
+ console.log("═══════════════════\n");
24
+ const config = loadLocalConfig();
25
+ const keychain = createKeychainProvider(config.dataDir);
26
+ const checks = [
27
+ {
28
+ name: "Data directory exists",
29
+ check: async () => {
30
+ try {
31
+ await fs.access(config.dataDir);
32
+ return { ok: true, message: config.dataDir };
33
+ }
34
+ catch {
35
+ return {
36
+ ok: false,
37
+ message: `${config.dataDir} not found. Run: nestpilot init`,
38
+ };
39
+ }
40
+ },
41
+ },
42
+ {
43
+ name: "Config file valid",
44
+ check: async () => {
45
+ const cfgPath = configFilePath(config.dataDir);
46
+ try {
47
+ const raw = await fs.readFile(cfgPath, "utf-8");
48
+ JSON.parse(raw);
49
+ return { ok: true, message: cfgPath };
50
+ }
51
+ catch (e) {
52
+ return {
53
+ ok: false,
54
+ message: `${cfgPath} — ${e instanceof Error ? e.message : String(e)}`,
55
+ };
56
+ }
57
+ },
58
+ },
59
+ {
60
+ name: "API key configured",
61
+ check: async () => {
62
+ if (config.apiKey) {
63
+ return { ok: true, message: "Set via NESTPILOT_API_KEY env var" };
64
+ }
65
+ const key = await keychain.get("nestpilot", "api-key");
66
+ if (key) {
67
+ return { ok: true, message: "Found in OS keychain" };
68
+ }
69
+ return {
70
+ ok: false,
71
+ message: "No API key found. Run: nestpilot init, or set NESTPILOT_API_KEY env var",
72
+ };
73
+ },
74
+ },
75
+ {
76
+ name: "Encryption key in keychain",
77
+ check: async () => {
78
+ const key = await keychain.get("nestpilot", "encryption-key");
79
+ if (key) {
80
+ return { ok: true, message: "Found in OS keychain" };
81
+ }
82
+ return {
83
+ ok: false,
84
+ message: "No encryption key. Run: nestpilot init",
85
+ };
86
+ },
87
+ },
88
+ {
89
+ name: "Cloud API reachable",
90
+ check: async () => {
91
+ try {
92
+ const response = await fetch(`${config.cloudApiUrl}/actuator/health`, {
93
+ method: "GET",
94
+ signal: AbortSignal.timeout(5_000),
95
+ });
96
+ if (response.ok) {
97
+ return { ok: true, message: config.cloudApiUrl };
98
+ }
99
+ return {
100
+ ok: false,
101
+ message: `${config.cloudApiUrl} returned ${response.status}`,
102
+ };
103
+ }
104
+ catch {
105
+ return {
106
+ ok: false,
107
+ message: `Cannot reach ${config.cloudApiUrl} — check internet`,
108
+ };
109
+ }
110
+ },
111
+ },
112
+ {
113
+ name: "Node.js version ≥ 18",
114
+ check: async () => {
115
+ const major = Number.parseInt(process.version.slice(1), 10);
116
+ if (major >= 18) {
117
+ return { ok: true, message: process.version };
118
+ }
119
+ return {
120
+ ok: false,
121
+ message: `${process.version} — Node.js 18+ required`,
122
+ };
123
+ },
124
+ },
125
+ {
126
+ name: "Plans directory readable",
127
+ check: async () => {
128
+ const dir = plansDir(config.dataDir);
129
+ try {
130
+ const files = await fs.readdir(dir);
131
+ const planFiles = files.filter((f) => f.endsWith(".json"));
132
+ return {
133
+ ok: true,
134
+ message: `${planFiles.length} plan(s) found in ${dir}`,
135
+ };
136
+ }
137
+ catch {
138
+ return {
139
+ ok: false,
140
+ message: `${dir} not accessible. Run: nestpilot init`,
141
+ };
142
+ }
143
+ },
144
+ },
145
+ {
146
+ name: "Disk space sufficient",
147
+ check: async () => {
148
+ try {
149
+ // Check total data dir size
150
+ let totalSize = 0;
151
+ const walkDir = async (dirPath) => {
152
+ try {
153
+ const entries = await fs.readdir(dirPath, {
154
+ withFileTypes: true,
155
+ });
156
+ for (const entry of entries) {
157
+ const entryPath = path.join(dirPath, entry.name);
158
+ if (entry.isDirectory()) {
159
+ await walkDir(entryPath);
160
+ }
161
+ else {
162
+ const stat = await fs.stat(entryPath);
163
+ totalSize += stat.size;
164
+ }
165
+ }
166
+ }
167
+ catch {
168
+ // Skip inaccessible directories
169
+ }
170
+ };
171
+ await walkDir(config.dataDir);
172
+ const sizeMb = (totalSize / (1024 * 1024)).toFixed(2);
173
+ const ok = totalSize < 500 * 1024 * 1024; // 500 MB threshold
174
+ return {
175
+ ok,
176
+ message: ok
177
+ ? `${sizeMb} MB used (< 500 MB limit)`
178
+ : `${sizeMb} MB used — consider pruning old plans`,
179
+ };
180
+ }
181
+ catch {
182
+ return { ok: true, message: "Could not check disk usage" };
183
+ }
184
+ },
185
+ },
186
+ ];
187
+ let passCount = 0;
188
+ let failCount = 0;
189
+ for (const { name, check } of checks) {
190
+ try {
191
+ const { ok, message } = await check();
192
+ if (ok) {
193
+ console.log(` ✅ ${name}: ${message}`);
194
+ passCount++;
195
+ }
196
+ else {
197
+ console.log(` ❌ ${name}: ${message}`);
198
+ failCount++;
199
+ }
200
+ }
201
+ catch (e) {
202
+ console.log(` ❌ ${name}: Unexpected error — ${e instanceof Error ? e.message : String(e)}`);
203
+ failCount++;
204
+ }
205
+ }
206
+ console.log(`\n${passCount} passed, ${failCount} failed out of ${checks.length} checks.`);
207
+ if (failCount > 0) {
208
+ console.log("\nTip: Run `nestpilot init` to fix most issues.\n");
209
+ process.exit(1);
210
+ }
211
+ else {
212
+ console.log("\nAll checks passed! NestPilot is healthy. ✨\n");
213
+ }
214
+ }
@@ -0,0 +1,6 @@
1
+ export declare function exportCommand(opts: {
2
+ plan?: string;
3
+ all?: boolean;
4
+ output?: string;
5
+ }): Promise<void>;
6
+ export declare function importCommand(file: string): Promise<void>;