@nestpilot/mcp-app 1.0.0 → 1.0.1

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/dist/cli/init.js CHANGED
@@ -74,11 +74,15 @@ export async function initCommand() {
74
74
  }
75
75
  else {
76
76
  console.log(" ⚠ Could not provision API key (cloud API unavailable).");
77
+ console.log(" Local plan creation & storage still works without an API key.");
78
+ console.log(" Cloud compute (forecasts, Roth optimization) requires a key.");
77
79
  console.log(" Set NESTPILOT_API_KEY env var manually, or run init again later.");
78
80
  }
79
81
  }
80
82
  catch {
81
83
  console.log(" ⚠ Could not reach cloud API for key provisioning.");
84
+ console.log(" Local plan creation & storage still works without an API key.");
85
+ console.log(" Cloud compute (forecasts, Roth optimization) requires a key.");
82
86
  console.log(" Set NESTPILOT_API_KEY env var manually, or run init again later.");
83
87
  }
84
88
  }
@@ -132,19 +136,25 @@ extensions:
132
136
  type: mcp
133
137
  transport: stdio
134
138
  command: npx
135
- args: ["nestpilot-mcp-server"]
139
+ args:
140
+ - "--yes"
141
+ - "--package=@nestpilot/mcp-app"
142
+ - "nestpilot-mcp-server"
136
143
  env:
137
144
  NESTPILOT_MODE: local
145
+ NESTPILOT_DATA_DIR: "~/.nestpilot"
138
146
  `;
139
147
  await fs.writeFile(path.join(dir, "goose.yaml"), gooseConfig);
140
148
  // Cowork
141
149
  const coworkConfig = {
150
+ _comment: "NestPilot MCP Server — Claude Cowork (Claude Desktop) Configuration. Add this to your claude_desktop_config.json.",
142
151
  mcpServers: {
143
152
  nestpilot: {
144
153
  command: "npx",
145
- args: ["nestpilot-mcp-server"],
154
+ args: ["--yes", "--package=@nestpilot/mcp-app", "nestpilot-mcp-server"],
146
155
  env: {
147
156
  NESTPILOT_MODE: "local",
157
+ NESTPILOT_DATA_DIR: "~/.nestpilot",
148
158
  },
149
159
  },
150
160
  },
@@ -153,19 +163,25 @@ extensions:
153
163
  // OpenClaw
154
164
  const openclawManifest = {
155
165
  name: "nestpilot",
166
+ package: "@nestpilot/mcp-app",
156
167
  version: "1.0.0",
157
168
  description: "Local-first retirement planning — your data stays on your machine",
169
+ homepage: "https://www.npmjs.com/package/@nestpilot/mcp-app",
158
170
  transport: "stdio",
159
171
  command: "npx",
160
- args: ["nestpilot-mcp-server"],
172
+ args: ["--yes", "--package=@nestpilot/mcp-app", "nestpilot-mcp-server"],
161
173
  env: {
162
174
  NESTPILOT_MODE: "local",
175
+ NESTPILOT_DATA_DIR: "~/.nestpilot",
163
176
  },
164
177
  capabilities: {
165
178
  tools: true,
166
179
  resources: true,
167
180
  apps: true,
168
181
  },
182
+ setup: {
183
+ instructions: "Run `npx --yes --package=@nestpilot/mcp-app nestpilot init` before first use.",
184
+ },
169
185
  };
170
186
  await fs.writeFile(path.join(dir, "openclaw-manifest.json"), JSON.stringify(openclawManifest, null, 2));
171
187
  }
@@ -1,10 +1,16 @@
1
1
  {
2
+ "_comment": "NestPilot MCP Server — Claude Cowork (Claude Desktop) Configuration. Run `npx --yes --package=@nestpilot/mcp-app nestpilot init` first, then add this to your claude_desktop_config.json.",
2
3
  "mcpServers": {
3
4
  "nestpilot": {
4
5
  "command": "npx",
5
- "args": ["nestpilot-mcp-server"],
6
+ "args": [
7
+ "--yes",
8
+ "--package=@nestpilot/mcp-app",
9
+ "nestpilot-mcp-server"
10
+ ],
6
11
  "env": {
7
- "NESTPILOT_MODE": "local"
12
+ "NESTPILOT_MODE": "local",
13
+ "NESTPILOT_DATA_DIR": "~/.nestpilot"
8
14
  }
9
15
  }
10
16
  }
@@ -1,10 +1,15 @@
1
1
  # NestPilot MCP Server — Goose Host Configuration
2
2
  #
3
- # Add this to your Goose profile configuration:
4
- # ~/.config/goose/profiles.yaml
3
+ # Installation
4
+ # ------------
5
+ # 1. Run the initializer first (one-time setup):
6
+ # npx --yes --package=@nestpilot/mcp-app nestpilot init
5
7
  #
6
- # Under the `extensions:` section of your active profile,
7
- # paste the following block.
8
+ # 2. Add the block below to your Goose profile:
9
+ # ~/.config/goose/profiles.yaml
10
+ # under the `extensions:` key of your active profile.
11
+ #
12
+ # 3. Restart Goose.
8
13
  #
9
14
  # @feature FEAT-0088
10
15
 
@@ -13,10 +18,13 @@ extensions:
13
18
  type: mcp
14
19
  transport: stdio
15
20
  command: npx
16
- args: ["nestpilot-mcp-server"]
21
+ args:
22
+ - "--yes"
23
+ - "--package=@nestpilot/mcp-app"
24
+ - "nestpilot-mcp-server"
17
25
  env:
18
26
  NESTPILOT_MODE: local
27
+ NESTPILOT_DATA_DIR: "~/.nestpilot"
19
28
  # Optional overrides:
20
- # NESTPILOT_DATA_DIR: "~/.nestpilot"
21
- # NESTPILOT_CLOUD_API_URL: "https://api.nestpilot.com"
29
+ # NESTPILOT_CLOUD_API_URL: "https://api.nestpilot.net"
22
30
  # NESTPILOT_API_KEY: "your-api-key"
@@ -1,16 +1,26 @@
1
1
  {
2
2
  "name": "nestpilot",
3
+ "package": "@nestpilot/mcp-app",
3
4
  "version": "1.0.0",
4
5
  "description": "Local-first retirement planning — your data stays on your machine",
6
+ "homepage": "https://www.npmjs.com/package/@nestpilot/mcp-app",
5
7
  "transport": "stdio",
6
8
  "command": "npx",
7
- "args": ["nestpilot-mcp-server"],
9
+ "args": [
10
+ "--yes",
11
+ "--package=@nestpilot/mcp-app",
12
+ "nestpilot-mcp-server"
13
+ ],
8
14
  "env": {
9
- "NESTPILOT_MODE": "local"
15
+ "NESTPILOT_MODE": "local",
16
+ "NESTPILOT_DATA_DIR": "~/.nestpilot"
10
17
  },
11
18
  "capabilities": {
12
19
  "tools": true,
13
20
  "resources": true,
14
21
  "apps": true
22
+ },
23
+ "setup": {
24
+ "instructions": "Run `npx --yes --package=@nestpilot/mcp-app nestpilot init` before first use."
15
25
  }
16
26
  }
package/dist/main.js CHANGED
@@ -104,10 +104,16 @@ async function startStreamableHTTPServer(factory) {
104
104
  process.on("SIGTERM", shutdown);
105
105
  }
106
106
  /**
107
- * Starts an MCP server with stdio transport (for Claude Desktop).
107
+ * Starts an MCP server with stdio transport (for Claude Desktop / VS Code).
108
+ *
109
+ * In stdio mode there are no HTTP headers, so we synthesize an authCtx from
110
+ * the config's defaultUserId. This ensures the policy engine can resolve a
111
+ * real role ("authenticated") when MCP_DEFAULT_USER_ID is set to anything
112
+ * other than the literal string "anonymous".
108
113
  */
109
114
  async function startStdioServer(factory) {
110
- await factory().connect(new StdioServerTransport());
115
+ const stdioAuthCtx = { userId: config.defaultUserId };
116
+ await factory(stdioAuthCtx).connect(new StdioServerTransport());
111
117
  }
112
118
  async function main() {
113
119
  if (process.argv.includes("--stdio")) {
package/dist/server.js CHANGED
@@ -72,49 +72,49 @@ export function createServer(authCtx) {
72
72
  });
73
73
  if (localConfig.mode === "local") {
74
74
  // ── Local mode (FEAT-0087) ────────────────────────────────────────
75
- console.log("[server] Starting in LOCAL mode — data stays on this machine");
75
+ console.error("[server] Starting in LOCAL mode — data stays on this machine");
76
76
  const keychain = createKeychainProvider(localConfig.dataDir);
77
77
  const encryption = new EncryptionService(keychain);
78
78
  const store = new LocalPlanStore(localConfig.dataDir, encryption);
79
79
  const computeClient = new CloudComputeClient(localConfig.cloudApiUrl, localConfig.apiKey ?? "");
80
- console.log("Registering Local Plan tools...");
80
+ console.error("Registering Local Plan tools...");
81
81
  registerLocalPlanTools(server, store, computeClient);
82
82
  // Cloud-backed tools that use PII-free payloads
83
- console.log("Registering Medicare tools (cloud)...");
83
+ console.error("Registering Medicare tools (cloud)...");
84
84
  registerMedicareTools(server, authCtx);
85
- console.log("Registering Roth tools (cloud)...");
85
+ console.error("Registering Roth tools (cloud)...");
86
86
  registerRothTools(server, authCtx);
87
- console.log("Registering Report tools (cloud)...");
87
+ console.error("Registering Report tools (cloud)...");
88
88
  registerReportTools(server, authCtx);
89
89
  }
90
90
  else {
91
91
  // ── Cloud mode (default — existing behavior) ──────────────────────
92
- console.log("[server] Starting in CLOUD mode — proxy to Spring Boot API");
93
- console.log("Registering Medicare tools...");
92
+ console.error("[server] Starting in CLOUD mode — proxy to Spring Boot API");
93
+ console.error("Registering Medicare tools...");
94
94
  registerMedicareTools(server, authCtx);
95
- console.log("Registering Planning tools...");
95
+ console.error("Registering Planning tools...");
96
96
  registerPlanningTools(server, authCtx);
97
- console.log("Registering Roth tools...");
97
+ console.error("Registering Roth tools...");
98
98
  registerRothTools(server, authCtx);
99
- console.log("Registering Agent tools...");
99
+ console.error("Registering Agent tools...");
100
100
  registerAgentTools(server, authCtx);
101
- console.log("Registering Plan Management tools...");
101
+ console.error("Registering Plan Management tools...");
102
102
  registerPlanManagementTools(server, authCtx);
103
- console.log("Registering Forecast Management tools...");
103
+ console.error("Registering Forecast Management tools...");
104
104
  registerForecastManagementTools(server, authCtx);
105
- console.log("Registering Scenario Management tools...");
105
+ console.error("Registering Scenario Management tools...");
106
106
  registerScenarioManagementTools(server, authCtx);
107
- console.log("Registering Proposal tools...");
107
+ console.error("Registering Proposal tools...");
108
108
  registerProposalTools(server, authCtx);
109
- console.log("Registering Report tools...");
109
+ console.error("Registering Report tools...");
110
110
  registerReportTools(server, authCtx);
111
111
  }
112
112
  // ── Shared registrations (both modes) ─────────────────────────────────
113
- console.log("Registering views...");
113
+ console.error("Registering views...");
114
114
  registerPlannerView(server);
115
115
  registerVerificationPacketView(server);
116
116
  registerSkillResources(server);
117
- console.log("Server registration complete");
117
+ console.error("Server registration complete");
118
118
  return server;
119
119
  }
120
120
  // ── Retirement Planner view ─────────────────────────────────────────────
@@ -83,28 +83,46 @@ class WindowsKeychain {
83
83
  async get(service, account) {
84
84
  try {
85
85
  const target = this.target(service, account);
86
+ // Use Win32 CredRead API via P/Invoke — works on all Windows versions
87
+ // without requiring the external CredentialManager PowerShell module.
88
+ // The script is Base64-encoded (UTF-16LE) to avoid quoting/escaping issues.
89
+ const script = [
90
+ "Add-Type -TypeDefinition @'",
91
+ "using System; using System.Runtime.InteropServices;",
92
+ "public class NpCred {",
93
+ ' [DllImport("advapi32.dll", SetLastError=true, CharSet=CharSet.Unicode)]',
94
+ " public static extern bool CredRead(string t, int ty, int f, out IntPtr c);",
95
+ ' [DllImport("advapi32.dll")]',
96
+ " public static extern void CredFree(IntPtr c);",
97
+ " [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]",
98
+ " public struct CREDENTIAL {",
99
+ " public int Flags; public int Type; public string TargetName;",
100
+ " public string Comment; public long LastWritten;",
101
+ " public int CredentialBlobSize; public IntPtr CredentialBlob;",
102
+ " public int Persist; public int AttributeCount; public IntPtr Attributes;",
103
+ " public string TargetAlias; public string UserName;",
104
+ " }",
105
+ "}",
106
+ "'@",
107
+ "$p=[IntPtr]::Zero",
108
+ `if([NpCred]::CredRead('${target}',1,0,[ref]$p)){`,
109
+ " $c=[Runtime.InteropServices.Marshal]::PtrToStructure($p,[Type][NpCred+CREDENTIAL])",
110
+ " $s=[Runtime.InteropServices.Marshal]::PtrToStringUni($c.CredentialBlob,$c.CredentialBlobSize/2)",
111
+ " [NpCred]::CredFree($p)",
112
+ " Write-Output $s",
113
+ "}",
114
+ ].join("\n");
115
+ const encoded = Buffer.from(script, "utf16le").toString("base64");
86
116
  const { stdout } = await execFileAsync("powershell.exe", [
87
117
  "-NoProfile",
88
- "-Command",
89
- `$cred = Get-StoredCredential -Target '${target}' -ErrorAction SilentlyContinue; if ($cred) { $cred.GetNetworkCredential().Password } else { $null }`,
118
+ "-EncodedCommand",
119
+ encoded,
90
120
  ]);
91
121
  const result = stdout.trim();
92
122
  return result && result !== "" ? result : null;
93
123
  }
94
124
  catch {
95
- // Fallback: try cmdkey
96
- try {
97
- const target = this.target(service, account);
98
- const { stdout } = await execFileAsync("powershell.exe", [
99
- "-NoProfile",
100
- "-Command",
101
- `[System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR((New-Object System.Management.Automation.PSCredential('_', (cmdkey /generic:${target} /list 2>$null | Out-Null; [System.Security.SecureString]::new()))).Password))`,
102
- ]);
103
- return stdout.trim() || null;
104
- }
105
- catch {
106
- return null;
107
- }
125
+ return null;
108
126
  }
109
127
  }
110
128
  async set(service, account, value) {
@@ -31,7 +31,7 @@ export function configFilePath(dataDir) {
31
31
  return path.join(dataDir, "config.json");
32
32
  }
33
33
  // ── Configuration loader ─────────────────────────────────────────────────
34
- const DEFAULT_CLOUD_API_URL = "https://api.nestpilot.com";
34
+ const DEFAULT_CLOUD_API_URL = "https://api.nestpilot.net";
35
35
  /**
36
36
  * Loads the local configuration from environment variables.
37
37
  *
@@ -207,7 +207,7 @@ Requires internet connectivity for cloud compute. Cached results are returned if
207
207
  }, true);
208
208
  }
209
209
  if (removedFields.length > 0) {
210
- console.log(`[local] PII scrubber removed ${removedFields.length} field(s): ${removedFields.join(", ")}`);
210
+ console.error(`[local] PII scrubber removed ${removedFields.length} field(s): ${removedFields.join(", ")}`);
211
211
  }
212
212
  // Call cloud compute
213
213
  const result = await computeClient.forecast(scrubbed);
@@ -80,9 +80,16 @@ function runPipeline(opts) {
80
80
  }
81
81
  }
82
82
  if (contract) {
83
+ const resolvedUserId = opts.actorId ?? opts.authCtx?.userId ?? "anonymous";
84
+ const resolvedRole = opts.role ??
85
+ (opts.authCtx?.bearerToken
86
+ ? "authenticated"
87
+ : resolvedUserId !== "anonymous"
88
+ ? "authenticated"
89
+ : "anonymous");
83
90
  const policyCtx = {
84
- actorId: opts.actorId ?? opts.authCtx?.userId ?? "anonymous",
85
- role: opts.role ?? (opts.authCtx?.bearerToken ? "authenticated" : "anonymous"),
91
+ actorId: resolvedUserId,
92
+ role: resolvedRole,
86
93
  toolName: opts.toolName,
87
94
  };
88
95
  const decision = evaluatePolicy(policyCtx);
@@ -32,7 +32,7 @@ const POLICY_TARGET_BRACKETS = {
32
32
  };
33
33
  // ── Tool registration ───────────────────────────────────────────────────
34
34
  export function registerRothTools(server, authCtx) {
35
- console.log("registerRothTools: Starting registration...");
35
+ console.error("registerRothTools: Starting registration...");
36
36
  try {
37
37
  registerAppTool(server, "optimize_roth_conversion", {
38
38
  title: "Optimize Roth Conversion",
@@ -377,7 +377,7 @@ DO NOT USE for basic forecasting — use run_forecast instead.`,
377
377
  }, true);
378
378
  }
379
379
  });
380
- console.log("registerRothTools: Registration successful!");
380
+ console.error("registerRothTools: Registration successful!");
381
381
  }
382
382
  catch (error) {
383
383
  console.error("registerRothTools: FAILED to register optimize_roth_conversion:", error);
@@ -1,10 +1,16 @@
1
1
  {
2
+ "_comment": "NestPilot MCP Server — Claude Cowork (Claude Desktop) Configuration. Run `npx --yes --package=@nestpilot/mcp-app nestpilot init` first, then add this to your claude_desktop_config.json.",
2
3
  "mcpServers": {
3
4
  "nestpilot": {
4
5
  "command": "npx",
5
- "args": ["nestpilot-mcp-server"],
6
+ "args": [
7
+ "--yes",
8
+ "--package=@nestpilot/mcp-app",
9
+ "nestpilot-mcp-server"
10
+ ],
6
11
  "env": {
7
- "NESTPILOT_MODE": "local"
12
+ "NESTPILOT_MODE": "local",
13
+ "NESTPILOT_DATA_DIR": "~/.nestpilot"
8
14
  }
9
15
  }
10
16
  }
@@ -1,10 +1,15 @@
1
1
  # NestPilot MCP Server — Goose Host Configuration
2
2
  #
3
- # Add this to your Goose profile configuration:
4
- # ~/.config/goose/profiles.yaml
3
+ # Installation
4
+ # ------------
5
+ # 1. Run the initializer first (one-time setup):
6
+ # npx --yes --package=@nestpilot/mcp-app nestpilot init
5
7
  #
6
- # Under the `extensions:` section of your active profile,
7
- # paste the following block.
8
+ # 2. Add the block below to your Goose profile:
9
+ # ~/.config/goose/profiles.yaml
10
+ # under the `extensions:` key of your active profile.
11
+ #
12
+ # 3. Restart Goose.
8
13
  #
9
14
  # @feature FEAT-0088
10
15
 
@@ -13,10 +18,13 @@ extensions:
13
18
  type: mcp
14
19
  transport: stdio
15
20
  command: npx
16
- args: ["nestpilot-mcp-server"]
21
+ args:
22
+ - "--yes"
23
+ - "--package=@nestpilot/mcp-app"
24
+ - "nestpilot-mcp-server"
17
25
  env:
18
26
  NESTPILOT_MODE: local
27
+ NESTPILOT_DATA_DIR: "~/.nestpilot"
19
28
  # Optional overrides:
20
- # NESTPILOT_DATA_DIR: "~/.nestpilot"
21
- # NESTPILOT_CLOUD_API_URL: "https://api.nestpilot.com"
29
+ # NESTPILOT_CLOUD_API_URL: "https://api.nestpilot.net"
22
30
  # NESTPILOT_API_KEY: "your-api-key"
@@ -1,16 +1,26 @@
1
1
  {
2
2
  "name": "nestpilot",
3
+ "package": "@nestpilot/mcp-app",
3
4
  "version": "1.0.0",
4
5
  "description": "Local-first retirement planning — your data stays on your machine",
6
+ "homepage": "https://www.npmjs.com/package/@nestpilot/mcp-app",
5
7
  "transport": "stdio",
6
8
  "command": "npx",
7
- "args": ["nestpilot-mcp-server"],
9
+ "args": [
10
+ "--yes",
11
+ "--package=@nestpilot/mcp-app",
12
+ "nestpilot-mcp-server"
13
+ ],
8
14
  "env": {
9
- "NESTPILOT_MODE": "local"
15
+ "NESTPILOT_MODE": "local",
16
+ "NESTPILOT_DATA_DIR": "~/.nestpilot"
10
17
  },
11
18
  "capabilities": {
12
19
  "tools": true,
13
20
  "resources": true,
14
21
  "apps": true
22
+ },
23
+ "setup": {
24
+ "instructions": "Run `npx --yes --package=@nestpilot/mcp-app nestpilot init` before first use."
15
25
  }
16
26
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nestpilot/mcp-app",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "type": "module",
5
5
  "description": "NestPilot MCP App — Retirement planning tools and interactive views",
6
6
  "bin": {