@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.
- package/README.md +350 -0
- package/dist/cli/doctor.d.ts +1 -0
- package/dist/cli/doctor.js +214 -0
- package/dist/cli/export-import.d.ts +6 -0
- package/dist/cli/export-import.js +132 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +168 -0
- package/dist/cli/init.d.ts +1 -0
- package/dist/cli/init.js +171 -0
- package/dist/host-configs/cowork.json +11 -0
- package/dist/host-configs/goose.yaml +22 -0
- package/dist/host-configs/openclaw-manifest.json +16 -0
- package/dist/main.d.ts +2 -0
- package/dist/main.js +128 -0
- package/dist/mcp-app.html +155 -0
- package/dist/nestpilot-client.d.ts +44 -0
- package/dist/nestpilot-client.js +160 -0
- package/dist/planner.html +222 -0
- package/dist/server.d.ts +19 -0
- package/dist/server.js +245 -0
- package/dist/skills/SKILL.md +162 -0
- package/dist/skills/manifest.json +51 -0
- package/dist/skills/tools/activate_plan.md +36 -0
- package/dist/skills/tools/coach.md +59 -0
- package/dist/skills/tools/comprehensive_plan.md +65 -0
- package/dist/skills/tools/create_plan.md +59 -0
- package/dist/skills/tools/create_saved_plan.md +49 -0
- package/dist/skills/tools/delete_plan.md +42 -0
- package/dist/skills/tools/delete_scenario.md +38 -0
- package/dist/skills/tools/generate_proposal.md +63 -0
- package/dist/skills/tools/generate_retirement_report.md +50 -0
- package/dist/skills/tools/get_active_plan.md +44 -0
- package/dist/skills/tools/get_baseline_forecast.md +47 -0
- package/dist/skills/tools/get_plan.md +44 -0
- package/dist/skills/tools/get_plan_components.md +50 -0
- package/dist/skills/tools/get_scenario.md +46 -0
- package/dist/skills/tools/list_plans.md +44 -0
- package/dist/skills/tools/list_scenarios.md +42 -0
- package/dist/skills/tools/medicare-guardian.md +59 -0
- package/dist/skills/tools/nestpilot_run_plan.md +61 -0
- package/dist/skills/tools/optimize_roth_conversion.md +107 -0
- package/dist/skills/tools/optimize_ss_claiming.md +30 -0
- package/dist/skills/tools/rename_plan.md +34 -0
- package/dist/skills/tools/retirement-planner.md +55 -0
- package/dist/skills/tools/run_forecast.md +65 -0
- package/dist/skills/tools/run_saved_forecast.md +52 -0
- package/dist/skills/tools/run_scenario.md +66 -0
- package/dist/skills/tools/save_plan.md +48 -0
- package/dist/skills/tools/save_scenario.md +50 -0
- package/dist/skills/tools/verify_forecast.md +43 -0
- package/dist/src/config.d.ts +20 -0
- package/dist/src/config.js +44 -0
- package/dist/src/contracts/provenance.d.ts +37 -0
- package/dist/src/contracts/provenance.js +71 -0
- package/dist/src/contracts/tool-contract-registry.d.ts +43 -0
- package/dist/src/contracts/tool-contract-registry.js +282 -0
- package/dist/src/local/cloud-compute-client.d.ts +55 -0
- package/dist/src/local/cloud-compute-client.js +135 -0
- package/dist/src/local/encryption.d.ts +24 -0
- package/dist/src/local/encryption.js +105 -0
- package/dist/src/local/keychain.d.ts +41 -0
- package/dist/src/local/keychain.js +236 -0
- package/dist/src/local/local-config.d.ts +34 -0
- package/dist/src/local/local-config.js +61 -0
- package/dist/src/local/local-data-layer.d.ts +20 -0
- package/dist/src/local/local-data-layer.js +15 -0
- package/dist/src/local/local-plan-store.d.ts +66 -0
- package/dist/src/local/local-plan-store.js +195 -0
- package/dist/src/local/pii-scrubber.d.ts +26 -0
- package/dist/src/local/pii-scrubber.js +219 -0
- package/dist/src/policy/policy-engine.d.ts +44 -0
- package/dist/src/policy/policy-engine.js +119 -0
- package/dist/src/rate-limit.d.ts +17 -0
- package/dist/src/rate-limit.js +41 -0
- package/dist/src/security.d.ts +19 -0
- package/dist/src/security.js +118 -0
- package/dist/src/skills/index.d.ts +12 -0
- package/dist/src/skills/index.js +16 -0
- package/dist/src/skills/retirement-pack-v1.d.ts +28 -0
- package/dist/src/skills/retirement-pack-v1.js +295 -0
- package/dist/src/skills/skill-executor.d.ts +65 -0
- package/dist/src/skills/skill-executor.js +174 -0
- package/dist/src/skills/skill-manifest-schema.d.ts +337 -0
- package/dist/src/skills/skill-manifest-schema.js +94 -0
- package/dist/src/skills/skill-registry.d.ts +71 -0
- package/dist/src/skills/skill-registry.js +116 -0
- package/dist/src/telemetry.d.ts +12 -0
- package/dist/src/telemetry.js +59 -0
- package/dist/src/types.d.ts +46 -0
- package/dist/src/types.js +4 -0
- package/dist/tools/agent-tools.d.ts +12 -0
- package/dist/tools/agent-tools.js +141 -0
- package/dist/tools/forecast-management-tools.d.ts +9 -0
- package/dist/tools/forecast-management-tools.js +133 -0
- package/dist/tools/local-plan-tools.d.ts +8 -0
- package/dist/tools/local-plan-tools.js +357 -0
- package/dist/tools/mcp-helpers.d.ts +52 -0
- package/dist/tools/mcp-helpers.js +177 -0
- package/dist/tools/medicare-tools.d.ts +3 -0
- package/dist/tools/medicare-tools.js +162 -0
- package/dist/tools/optimize-roth-tools-test.d.ts +2 -0
- package/dist/tools/optimize-roth-tools-test.js +36 -0
- package/dist/tools/optimize-roth-tools.d.ts +3 -0
- package/dist/tools/optimize-roth-tools.js +818 -0
- package/dist/tools/plan-management-tools.d.ts +3 -0
- package/dist/tools/plan-management-tools.js +196 -0
- package/dist/tools/planning-tools.d.ts +3 -0
- package/dist/tools/planning-tools.js +290 -0
- package/dist/tools/proposal-tools.d.ts +3 -0
- package/dist/tools/proposal-tools.js +428 -0
- package/dist/tools/report-tools.d.ts +3 -0
- package/dist/tools/report-tools.js +245 -0
- package/dist/tools/scenario-management-tools.d.ts +3 -0
- package/dist/tools/scenario-management-tools.js +136 -0
- package/dist/views/verification-packet.html +211 -0
- package/host-configs/cowork.json +11 -0
- package/host-configs/goose.yaml +22 -0
- package/host-configs/openclaw-manifest.json +16 -0
- package/package.json +66 -0
- package/skills/SKILL.md +162 -0
- package/skills/manifest.json +51 -0
- package/skills/tools/activate_plan.md +36 -0
- package/skills/tools/coach.md +59 -0
- package/skills/tools/comprehensive_plan.md +65 -0
- package/skills/tools/create_plan.md +59 -0
- package/skills/tools/create_saved_plan.md +49 -0
- package/skills/tools/delete_plan.md +42 -0
- package/skills/tools/delete_scenario.md +38 -0
- package/skills/tools/generate_proposal.md +63 -0
- package/skills/tools/generate_retirement_report.md +50 -0
- package/skills/tools/get_active_plan.md +44 -0
- package/skills/tools/get_baseline_forecast.md +47 -0
- package/skills/tools/get_plan.md +44 -0
- package/skills/tools/get_plan_components.md +50 -0
- package/skills/tools/get_scenario.md +46 -0
- package/skills/tools/list_plans.md +44 -0
- package/skills/tools/list_scenarios.md +42 -0
- package/skills/tools/medicare-guardian.md +59 -0
- package/skills/tools/nestpilot_run_plan.md +61 -0
- package/skills/tools/optimize_roth_conversion.md +107 -0
- package/skills/tools/optimize_ss_claiming.md +30 -0
- package/skills/tools/rename_plan.md +34 -0
- package/skills/tools/retirement-planner.md +55 -0
- package/skills/tools/run_forecast.md +65 -0
- package/skills/tools/run_saved_forecast.md +52 -0
- package/skills/tools/run_scenario.md +66 -0
- package/skills/tools/save_plan.md +48 -0
- package/skills/tools/save_scenario.md +50 -0
- 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
|
+
}
|