estatehelm 1.0.21 → 1.0.22

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 CHANGED
@@ -68,17 +68,21 @@ Remove credentials and revoke device access.
68
68
  Start the MCP server.
69
69
 
70
70
  Options:
71
- - `-m, --mode <mode>` - Privacy mode: `full` (default) or `safe`
71
+ - `-m, --mode <mode>` - Privacy mode: `safe` (default) or `full`
72
+ - `-t, --token <token>` - MCP access token (or set ESTATEHELM_MCP_TOKEN env var)
72
73
  - `--poll <interval>` - Background sync interval (e.g., `15m`)
73
74
 
74
75
  ### `estatehelm status`
75
76
 
76
77
  Show current login status and cache information.
77
78
 
78
- ### `estatehelm sync`
79
+ ### `estatehelm sync [options]`
79
80
 
80
81
  Force sync cache from server.
81
82
 
83
+ Options:
84
+ - `-t, --token <token>` - MCP access token (or set ESTATEHELM_MCP_TOKEN env var)
85
+
82
86
  ### `estatehelm cache clear`
83
87
 
84
88
  Clear local cache (keeps credentials).
@@ -100,20 +104,20 @@ Get configuration value(s).
100
104
 
101
105
  ## Privacy Modes
102
106
 
103
- ### Full Mode (default)
107
+ ### Safe Mode (default)
104
108
 
105
- All data is returned as-is. Use this when you're alone and want full access.
109
+ Sensitive fields (passwords, account numbers, etc.) are redacted. Use this when sharing your screen or when uncertain.
106
110
 
107
111
  ```bash
108
- estatehelm mcp --mode full
112
+ estatehelm mcp --mode safe
109
113
  ```
110
114
 
111
- ### Safe Mode
115
+ ### Full Mode
112
116
 
113
- Sensitive fields (passwords, account numbers, etc.) are redacted. Use this when sharing your screen or when uncertain.
117
+ All data is returned as-is. Use this when you're alone and want full access.
114
118
 
115
119
  ```bash
116
- estatehelm mcp --mode safe
120
+ estatehelm mcp --mode full
117
121
  ```
118
122
 
119
123
  Redacted fields by entity type:
@@ -202,7 +206,7 @@ The device was revoked from EstateHelm settings. Run `estatehelm login` to re-re
202
206
 
203
207
  ### Cache issues
204
208
 
205
- Try clearing the cache: `estatehelm cache clear`, then `estatehelm sync`.
209
+ Try clearing the cache: `estatehelm cache clear`, then `estatehelm sync --token YOUR_TOKEN`.
206
210
 
207
211
  ## License
208
212
 
package/dist/index.js CHANGED
@@ -744,195 +744,6 @@ async function decryptEntity(householdKey, encrypted, options = {}) {
744
744
  }
745
745
  }
746
746
 
747
- // ../encryption/src/entityKeyMapping.ts
748
- var ENTITY_KEY_TYPE_MAP = {
749
- // General household items
750
- "property": "general",
751
- "maintenance_task": "task",
752
- "pet": "general",
753
- "vehicle": "general",
754
- "device": "general",
755
- "valuable": "general",
756
- "valuables": "general",
757
- // Route alias
758
- "access_code": "access_code",
759
- "contact": "general",
760
- "service": "general",
761
- "document": "general",
762
- "travel": "general",
763
- "resident": "general",
764
- "home_improvement": "general",
765
- // Property improvements
766
- "vehicle_maintenance": "general",
767
- // Vehicle maintenance history
768
- "vehicle_service_visit": "general",
769
- // Vehicle service visits
770
- "pet_vet_visit": "general",
771
- // Pet vet visits (like vehicle_service_visit for vehicles)
772
- "pet_health": "general",
773
- // Pet health records (simple single records)
774
- "military_record": "general",
775
- // Military service records
776
- "education_record": "general",
777
- // Education records (diplomas, transcripts, etc.)
778
- "credential": "general",
779
- // Credentials (professional licenses, government IDs, etc.)
780
- "credentials": "general",
781
- // Route alias
782
- "membership_record": "general",
783
- // Membership records (airline, hotel, retail loyalty programs)
784
- // Health records (sensitive - requires health key)
785
- "health_record": "health",
786
- // Financial
787
- "bank_account": "financial",
788
- "investment": "financial",
789
- "tax_document": "financial",
790
- "tax_year": "financial",
791
- "taxes": "financial",
792
- // Route alias
793
- "financial_account": "financial",
794
- "financial": "financial",
795
- // Route alias
796
- // Legal (owner-only)
797
- "legal": "legal",
798
- // Insurance (both names for compatibility)
799
- "insurance": "general",
800
- "insurance_policy": "general",
801
- "subscription": "subscription",
802
- // Passwords (shared household credentials)
803
- "password": "password",
804
- // Identity (Personal Vault)
805
- "identity": "identity",
806
- // Calendar Connections (Personal Vault - user's OAuth tokens for calendar sync)
807
- "calendar_connection": "identity",
808
- // Continuity (Personal Vault - shared messages for beneficiaries)
809
- "continuity": "continuity",
810
- // Emergency (household-scoped emergency info, encrypted with general key so all members can decrypt)
811
- "emergency": "general"
812
- };
813
- function getKeyTypeForEntity(entityType) {
814
- const keyType = ENTITY_KEY_TYPE_MAP[entityType];
815
- if (!keyType) {
816
- throw new Error(
817
- `No key type mapping found for entity type: ${entityType}. Please add it to ENTITY_KEY_TYPE_MAP in entityKeyMapping.ts`
818
- );
819
- }
820
- return keyType;
821
- }
822
-
823
- // ../encryption/src/recoveryKey.ts
824
- var RECOVERY_KEY_SIZE = 16;
825
- var GROUP_SIZE = 4;
826
- var BASE32_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
827
- function encodeBase32(bytes) {
828
- let bits = "";
829
- for (const byte of bytes) {
830
- bits += byte.toString(2).padStart(8, "0");
831
- }
832
- while (bits.length % 5 !== 0) {
833
- bits += "0";
834
- }
835
- let result = "";
836
- for (let i = 0; i < bits.length; i += 5) {
837
- const chunk = bits.substring(i, i + 5);
838
- const index = parseInt(chunk, 2);
839
- result += BASE32_ALPHABET[index];
840
- }
841
- return result;
842
- }
843
- function decodeBase32(base32) {
844
- const cleaned = base32.toUpperCase().replace(/[^A-Z2-7]/g, "");
845
- let bits = "";
846
- for (const char of cleaned) {
847
- const index = BASE32_ALPHABET.indexOf(char);
848
- if (index === -1) {
849
- throw new Error(`Invalid base32 character: ${char}`);
850
- }
851
- bits += index.toString(2).padStart(5, "0");
852
- }
853
- const bytes = [];
854
- for (let i = 0; i < bits.length - bits.length % 8; i += 8) {
855
- const byte = parseInt(bits.substring(i, i + 8), 2);
856
- bytes.push(byte);
857
- }
858
- return new Uint8Array(bytes);
859
- }
860
- function formatRecoveryKey(base32) {
861
- const groups = [];
862
- for (let i = 0; i < base32.length; i += GROUP_SIZE) {
863
- groups.push(base32.substring(i, i + GROUP_SIZE));
864
- }
865
- return groups.join("-");
866
- }
867
- function unformatRecoveryKey(formatted) {
868
- return formatted.toUpperCase().replace(/[^A-Z2-7]/g, "");
869
- }
870
- function validateRecoveryKey(recoveryKey) {
871
- try {
872
- const unformatted = unformatRecoveryKey(recoveryKey);
873
- if (unformatted.length < 20 || unformatted.length > 30) {
874
- return false;
875
- }
876
- for (const char of unformatted) {
877
- if (!BASE32_ALPHABET.includes(char)) {
878
- return false;
879
- }
880
- }
881
- const bytes = decodeBase32(unformatted);
882
- return bytes.length >= RECOVERY_KEY_SIZE - 2 && bytes.length <= RECOVERY_KEY_SIZE + 2;
883
- } catch {
884
- return false;
885
- }
886
- }
887
- function parseRecoveryKey(recoveryKey) {
888
- if (!validateRecoveryKey(recoveryKey)) {
889
- throw new Error("Invalid recovery key format");
890
- }
891
- const unformatted = unformatRecoveryKey(recoveryKey);
892
- const bytes = decodeBase32(unformatted);
893
- const normalizedBytes = new Uint8Array(RECOVERY_KEY_SIZE);
894
- normalizedBytes.set(bytes.slice(0, RECOVERY_KEY_SIZE));
895
- const base32 = encodeBase32(normalizedBytes);
896
- const formatted = formatRecoveryKey(base32);
897
- const base64 = base64Encode(normalizedBytes);
898
- return {
899
- bytes: normalizedBytes,
900
- formatted,
901
- base64
902
- };
903
- }
904
- async function deriveWrapKey(recoveryKeyBytes, serverWrapSecret, info = "hearthcoo-wrap-key-v1") {
905
- if (recoveryKeyBytes.length !== RECOVERY_KEY_SIZE) {
906
- throw new Error(`Recovery key must be ${RECOVERY_KEY_SIZE} bytes`);
907
- }
908
- if (serverWrapSecret.length !== 32) {
909
- throw new Error("Server wrap secret must be 32 bytes");
910
- }
911
- const recoveryKeyMaterial = await crypto.subtle.importKey(
912
- "raw",
913
- recoveryKeyBytes,
914
- "HKDF",
915
- false,
916
- ["deriveKey"]
917
- );
918
- const wrapKey = await crypto.subtle.deriveKey(
919
- {
920
- name: "HKDF",
921
- hash: "SHA-256",
922
- salt: serverWrapSecret,
923
- info: new TextEncoder().encode(info)
924
- },
925
- recoveryKeyMaterial,
926
- {
927
- name: "AES-GCM",
928
- length: 256
929
- },
930
- false,
931
- ["encrypt", "decrypt"]
932
- );
933
- return wrapKey;
934
- }
935
-
936
747
  // ../types/src/contacts.ts
937
748
  function getContactDisplayName(contact, fallback = "") {
938
749
  const personName = [contact.first_name, contact.last_name].filter(Boolean).join(" ");
@@ -946,6 +757,76 @@ function getContactDisplayName(contact, fallback = "") {
946
757
  // ../types/src/keys.ts
947
758
  var DEFAULT_KEY_BUNDLE_ALG = "ECDH-P-521";
948
759
 
760
+ // ../types/src/entities.ts
761
+ var ENTITIES = {
762
+ // ===== General household items =====
763
+ property: { keyType: "general" },
764
+ pet: { keyType: "general" },
765
+ vehicle: { keyType: "general" },
766
+ contact: { keyType: "general" },
767
+ device: { keyType: "general" },
768
+ valuable: { keyType: "general" },
769
+ resident: { keyType: "general" },
770
+ service: { keyType: "general" },
771
+ home_improvement: { keyType: "general" },
772
+ vehicle_maintenance: { keyType: "general" },
773
+ vehicle_service_visit: { keyType: "general" },
774
+ pet_vet_visit: { keyType: "general" },
775
+ military_record: { keyType: "general" },
776
+ education_record: { keyType: "general" },
777
+ credential: { keyType: "general" },
778
+ membership_record: { keyType: "general" },
779
+ maintenance_task: { keyType: "task" },
780
+ // ===== Access codes =====
781
+ access_code: { keyType: "access_code" },
782
+ // ===== Insurance =====
783
+ insurance_policy: { keyType: "general" },
784
+ // ===== Subscriptions =====
785
+ subscription: { keyType: "subscription" },
786
+ // ===== Financial (sensitive) =====
787
+ financial_account: { keyType: "financial" },
788
+ tax_year: { keyType: "financial" },
789
+ // ===== Health (sensitive) =====
790
+ health_record: { keyType: "health" },
791
+ // ===== Legal (owner-only) =====
792
+ legal_document: { keyType: "legal" },
793
+ // ===== Personal vault =====
794
+ digital_identity: { keyType: "identity" },
795
+ calendar_connection: { keyType: "identity" },
796
+ // ===== Shared passwords =====
797
+ password: { keyType: "password", hasSchema: false },
798
+ // ===== Internal/legacy types (no schemas) =====
799
+ emergency: { keyType: "general", hasSchema: false },
800
+ continuity: { keyType: "continuity", hasSchema: false },
801
+ document: { keyType: "general", hasSchema: false },
802
+ travel: { keyType: "general", hasSchema: false },
803
+ pet_health: { keyType: "general", hasSchema: false },
804
+ bank_account: { keyType: "financial", hasSchema: false },
805
+ investment: { keyType: "financial", hasSchema: false },
806
+ tax_document: { keyType: "financial", hasSchema: false }
807
+ };
808
+ var ENTITY_ALIASES = {
809
+ valuables: "valuable",
810
+ credentials: "credential",
811
+ taxes: "tax_year",
812
+ financial: "financial_account",
813
+ insurance: "insurance_policy",
814
+ legal: "legal_document",
815
+ identity: "digital_identity"
816
+ };
817
+ var ENTITY_TYPES = Object.keys(ENTITIES);
818
+ var SCHEMA_ENTITY_TYPES = Object.entries(ENTITIES).filter(([_, config]) => config.hasSchema !== false).map(([type]) => type);
819
+ function getKeyTypeForEntity(entityType) {
820
+ const resolvedType = entityType in ENTITY_ALIASES ? ENTITY_ALIASES[entityType] : entityType;
821
+ const config = ENTITIES[resolvedType];
822
+ if (!config) {
823
+ throw new Error(
824
+ `Unknown entity type: ${entityType}. Please add it to ENTITIES in entities.ts`
825
+ );
826
+ }
827
+ return config.keyType;
828
+ }
829
+
949
830
  // ../types/src/options.ts
950
831
  var PERSONAL_LEGAL_DOCUMENT_TYPE_OPTIONS = [
951
832
  { label: "Birth Certificate", value: "birth_certificate" },
@@ -1209,6 +1090,131 @@ var COSTARICA_NAV_COLORS = stripeColors([
1209
1090
  // Blue stripe
1210
1091
  ]);
1211
1092
 
1093
+ // ../encryption/src/entityKeyMapping.ts
1094
+ var ENTITY_KEY_TYPE_MAP = {
1095
+ // Build from ENTITIES
1096
+ ...Object.fromEntries(
1097
+ Object.entries(ENTITIES).map(([type, config]) => [type, config.keyType])
1098
+ ),
1099
+ // Add aliases
1100
+ ...Object.fromEntries(
1101
+ Object.entries(ENTITY_ALIASES).map(([alias, target]) => [alias, ENTITIES[target].keyType])
1102
+ )
1103
+ };
1104
+
1105
+ // ../encryption/src/recoveryKey.ts
1106
+ var RECOVERY_KEY_SIZE = 16;
1107
+ var GROUP_SIZE = 4;
1108
+ var BASE32_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
1109
+ function encodeBase32(bytes) {
1110
+ let bits = "";
1111
+ for (const byte of bytes) {
1112
+ bits += byte.toString(2).padStart(8, "0");
1113
+ }
1114
+ while (bits.length % 5 !== 0) {
1115
+ bits += "0";
1116
+ }
1117
+ let result = "";
1118
+ for (let i = 0; i < bits.length; i += 5) {
1119
+ const chunk = bits.substring(i, i + 5);
1120
+ const index = parseInt(chunk, 2);
1121
+ result += BASE32_ALPHABET[index];
1122
+ }
1123
+ return result;
1124
+ }
1125
+ function decodeBase32(base32) {
1126
+ const cleaned = base32.toUpperCase().replace(/[^A-Z2-7]/g, "");
1127
+ let bits = "";
1128
+ for (const char of cleaned) {
1129
+ const index = BASE32_ALPHABET.indexOf(char);
1130
+ if (index === -1) {
1131
+ throw new Error(`Invalid base32 character: ${char}`);
1132
+ }
1133
+ bits += index.toString(2).padStart(5, "0");
1134
+ }
1135
+ const bytes = [];
1136
+ for (let i = 0; i < bits.length - bits.length % 8; i += 8) {
1137
+ const byte = parseInt(bits.substring(i, i + 8), 2);
1138
+ bytes.push(byte);
1139
+ }
1140
+ return new Uint8Array(bytes);
1141
+ }
1142
+ function formatRecoveryKey(base32) {
1143
+ const groups = [];
1144
+ for (let i = 0; i < base32.length; i += GROUP_SIZE) {
1145
+ groups.push(base32.substring(i, i + GROUP_SIZE));
1146
+ }
1147
+ return groups.join("-");
1148
+ }
1149
+ function unformatRecoveryKey(formatted) {
1150
+ return formatted.toUpperCase().replace(/[^A-Z2-7]/g, "");
1151
+ }
1152
+ function validateRecoveryKey(recoveryKey) {
1153
+ try {
1154
+ const unformatted = unformatRecoveryKey(recoveryKey);
1155
+ if (unformatted.length < 20 || unformatted.length > 30) {
1156
+ return false;
1157
+ }
1158
+ for (const char of unformatted) {
1159
+ if (!BASE32_ALPHABET.includes(char)) {
1160
+ return false;
1161
+ }
1162
+ }
1163
+ const bytes = decodeBase32(unformatted);
1164
+ return bytes.length >= RECOVERY_KEY_SIZE - 2 && bytes.length <= RECOVERY_KEY_SIZE + 2;
1165
+ } catch {
1166
+ return false;
1167
+ }
1168
+ }
1169
+ function parseRecoveryKey(recoveryKey) {
1170
+ if (!validateRecoveryKey(recoveryKey)) {
1171
+ throw new Error("Invalid recovery key format");
1172
+ }
1173
+ const unformatted = unformatRecoveryKey(recoveryKey);
1174
+ const bytes = decodeBase32(unformatted);
1175
+ const normalizedBytes = new Uint8Array(RECOVERY_KEY_SIZE);
1176
+ normalizedBytes.set(bytes.slice(0, RECOVERY_KEY_SIZE));
1177
+ const base32 = encodeBase32(normalizedBytes);
1178
+ const formatted = formatRecoveryKey(base32);
1179
+ const base64 = base64Encode(normalizedBytes);
1180
+ return {
1181
+ bytes: normalizedBytes,
1182
+ formatted,
1183
+ base64
1184
+ };
1185
+ }
1186
+ async function deriveWrapKey(recoveryKeyBytes, serverWrapSecret, info = "hearthcoo-wrap-key-v1") {
1187
+ if (recoveryKeyBytes.length !== RECOVERY_KEY_SIZE) {
1188
+ throw new Error(`Recovery key must be ${RECOVERY_KEY_SIZE} bytes`);
1189
+ }
1190
+ if (serverWrapSecret.length !== 32) {
1191
+ throw new Error("Server wrap secret must be 32 bytes");
1192
+ }
1193
+ const recoveryKeyMaterial = await crypto.subtle.importKey(
1194
+ "raw",
1195
+ recoveryKeyBytes,
1196
+ "HKDF",
1197
+ false,
1198
+ ["deriveKey"]
1199
+ );
1200
+ const wrapKey = await crypto.subtle.deriveKey(
1201
+ {
1202
+ name: "HKDF",
1203
+ hash: "SHA-256",
1204
+ salt: serverWrapSecret,
1205
+ info: new TextEncoder().encode(info)
1206
+ },
1207
+ recoveryKeyMaterial,
1208
+ {
1209
+ name: "AES-GCM",
1210
+ length: 256
1211
+ },
1212
+ false,
1213
+ ["encrypt", "decrypt"]
1214
+ );
1215
+ return wrapKey;
1216
+ }
1217
+
1212
1218
  // ../encryption/src/keyBundle.ts
1213
1219
  async function decryptPrivateKeyWithWrapKey(encryptedPrivateKey, wrapKey) {
1214
1220
  const packed = base64Decode(encryptedPrivateKey);
@@ -1889,7 +1895,7 @@ async function getPrivateKey(mcpToken) {
1889
1895
  // src/server.ts
1890
1896
  var import_server = require("@modelcontextprotocol/sdk/server/index.js");
1891
1897
  var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
1892
- var import_types42 = require("@modelcontextprotocol/sdk/types.js");
1898
+ var import_types44 = require("@modelcontextprotocol/sdk/types.js");
1893
1899
 
1894
1900
  // ../cache-sqlite/src/sqliteStore.ts
1895
1901
  var import_better_sqlite3 = __toESM(require("better-sqlite3"));
@@ -4844,7 +4850,7 @@ function enrichEntities(entities, entityType) {
4844
4850
  }
4845
4851
 
4846
4852
  // src/resources/entities.ts
4847
- var ENTITY_TYPES = Object.keys(ENTITY_KEY_TYPE_MAP).filter(
4853
+ var ENTITY_TYPES2 = Object.keys(ENTITY_KEY_TYPE_MAP).filter(
4848
4854
  (t) => !ROUTE_ALIASES.has(t)
4849
4855
  );
4850
4856
  async function getEntities(householdId, entityType, privateKey, privacyMode) {
@@ -5096,7 +5102,7 @@ async function startServer(mode, mcpToken) {
5096
5102
  }
5097
5103
  }
5098
5104
  );
5099
- server.setRequestHandler(import_types42.ListResourcesRequestSchema, async () => {
5105
+ server.setRequestHandler(import_types44.ListResourcesRequestSchema, async () => {
5100
5106
  const households = await listHouseholds();
5101
5107
  const resources = [
5102
5108
  {
@@ -5113,7 +5119,7 @@ async function startServer(mode, mcpToken) {
5113
5119
  description: `Household: ${household.name}`,
5114
5120
  mimeType: "application/json"
5115
5121
  });
5116
- for (const type of ENTITY_TYPES) {
5122
+ for (const type of ENTITY_TYPES2) {
5117
5123
  resources.push({
5118
5124
  uri: `estatehelm://households/${household.id}/${type}`,
5119
5125
  name: `${household.name} - ${formatEntityType(type)}`,
@@ -5124,7 +5130,7 @@ async function startServer(mode, mcpToken) {
5124
5130
  }
5125
5131
  return { resources };
5126
5132
  });
5127
- server.setRequestHandler(import_types42.ReadResourceRequestSchema, async (request) => {
5133
+ server.setRequestHandler(import_types44.ReadResourceRequestSchema, async (request) => {
5128
5134
  const uri = request.params.uri;
5129
5135
  const parsed = parseResourceUri(uri);
5130
5136
  if (!parsed) {
@@ -5153,7 +5159,7 @@ async function startServer(mode, mcpToken) {
5153
5159
  ]
5154
5160
  };
5155
5161
  });
5156
- server.setRequestHandler(import_types42.ListToolsRequestSchema, async () => {
5162
+ server.setRequestHandler(import_types44.ListToolsRequestSchema, async () => {
5157
5163
  return {
5158
5164
  tools: [
5159
5165
  {
@@ -5246,7 +5252,7 @@ async function startServer(mode, mcpToken) {
5246
5252
  ]
5247
5253
  };
5248
5254
  });
5249
- server.setRequestHandler(import_types42.CallToolRequestSchema, async (request) => {
5255
+ server.setRequestHandler(import_types44.CallToolRequestSchema, async (request) => {
5250
5256
  const { name, arguments: args } = request.params;
5251
5257
  trackToolUse(name);
5252
5258
  switch (name) {
@@ -5296,7 +5302,7 @@ async function startServer(mode, mcpToken) {
5296
5302
  throw new Error(`Unknown tool: ${name}`);
5297
5303
  }
5298
5304
  });
5299
- server.setRequestHandler(import_types42.ListPromptsRequestSchema, async () => {
5305
+ server.setRequestHandler(import_types44.ListPromptsRequestSchema, async () => {
5300
5306
  return {
5301
5307
  prompts: [
5302
5308
  {
@@ -5329,7 +5335,7 @@ async function startServer(mode, mcpToken) {
5329
5335
  ]
5330
5336
  };
5331
5337
  });
5332
- server.setRequestHandler(import_types42.GetPromptRequestSchema, async (request) => {
5338
+ server.setRequestHandler(import_types44.GetPromptRequestSchema, async (request) => {
5333
5339
  const { name, arguments: args } = request.params;
5334
5340
  switch (name) {
5335
5341
  case "household_summary": {
@@ -5485,17 +5491,23 @@ program.command("status").description("Show current login status and cache info"
5485
5491
  process.exit(1);
5486
5492
  }
5487
5493
  });
5488
- program.command("sync").description("Force sync cache from server").action(async () => {
5494
+ program.command("sync").description("Force sync cache from server").option("-t, --token <token>", "MCP access token (or set ESTATEHELM_MCP_TOKEN env var)").action(async (options) => {
5489
5495
  try {
5496
+ const token = options.token || process.env.ESTATEHELM_MCP_TOKEN;
5497
+ if (!token) {
5498
+ console.error("Access token required.");
5499
+ console.error("Use --token flag or set ESTATEHELM_MCP_TOKEN environment variable.");
5500
+ process.exit(1);
5501
+ }
5490
5502
  console.log("Syncing...");
5491
5503
  const client = await getAuthenticatedClient();
5492
5504
  if (!client) {
5493
5505
  console.error("Not logged in. Run: estatehelm login");
5494
5506
  process.exit(1);
5495
5507
  }
5496
- const privateKey = await getPrivateKey();
5508
+ const privateKey = await getPrivateKey(token);
5497
5509
  if (!privateKey) {
5498
- console.error("Failed to load encryption keys. Run: estatehelm login");
5510
+ console.error("Failed to load encryption keys. Check your MCP token or run: estatehelm login");
5499
5511
  process.exit(1);
5500
5512
  }
5501
5513
  await initCache();