ofw-mcp 2.4.0 → 2.4.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.
@@ -6,7 +6,7 @@
6
6
  },
7
7
  "metadata": {
8
8
  "description": "OurFamilyWizard tools for Claude Code",
9
- "version": "2.4.0"
9
+ "version": "2.4.1"
10
10
  },
11
11
  "plugins": [
12
12
  {
@@ -14,7 +14,7 @@
14
14
  "displayName": "OurFamilyWizard",
15
15
  "source": "./",
16
16
  "description": "OurFamilyWizard co-parenting tools for Claude — messages, calendar, expenses, and journal via MCP",
17
- "version": "2.4.0",
17
+ "version": "2.4.1",
18
18
  "author": {
19
19
  "name": "Chris Chall"
20
20
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ofw",
3
3
  "displayName": "OurFamilyWizard",
4
- "version": "2.4.0",
4
+ "version": "2.4.1",
5
5
  "description": "OurFamilyWizard co-parenting tools for Claude — messages, calendar, expenses, and journal via MCP",
6
6
  "author": {
7
7
  "name": "Chris Chall"
@@ -17,5 +17,40 @@
17
17
  "family"
18
18
  ],
19
19
  "skills": "./skills/",
20
- "mcp": "./.mcp.json"
20
+ "mcp": "./.mcp.json",
21
+ "userConfig": {
22
+ "ofw_username": {
23
+ "type": "string",
24
+ "title": "OFW Email",
25
+ "description": "Your OurFamilyWizard login email address. Optional — if omitted, the server falls back to the fetchproxy browser extension (requires being signed in to ourfamilywizard.com in your browser).",
26
+ "required": false
27
+ },
28
+ "ofw_password": {
29
+ "type": "string",
30
+ "title": "OFW Password",
31
+ "description": "Your OurFamilyWizard password. Optional — see ofw_username.",
32
+ "required": false,
33
+ "sensitive": true
34
+ },
35
+ "ofw_inline_attachments": {
36
+ "type": "boolean",
37
+ "title": "Return attachments inline",
38
+ "description": "When on, ofw_download_attachment returns bytes inline as MCP content (images render directly; other files come back as embedded resources) instead of writing to disk. Recommended for sandboxed hosts like Claude Desktop where the model cannot read files written to ~/Downloads. Callers can still override per-call via the tool's `inline` argument.",
39
+ "required": false,
40
+ "default": false
41
+ },
42
+ "ofw_attachments_dir": {
43
+ "type": "directory",
44
+ "title": "Attachments download directory",
45
+ "description": "Directory where ofw_download_attachment writes files when not returning inline. Defaults to ~/Downloads/ofw-mcp/. Pick a directory that is readable by your MCP host.",
46
+ "required": false
47
+ },
48
+ "ofw_write_mode": {
49
+ "type": "string",
50
+ "title": "Write mode",
51
+ "description": "Structural write gate. \"none\" = no write tools registered (read/sync/search only); \"drafts\" = draft-level writes only (save/delete drafts, upload attachments — never send or calendar/expense/journal writes); \"all\" = everything (default). Unrecognized values fail closed to \"none\".",
52
+ "required": false,
53
+ "default": "all"
54
+ }
55
+ }
21
56
  }
package/.mcp.json CHANGED
@@ -4,8 +4,11 @@
4
4
  "command": "npx",
5
5
  "args": ["-y", "ofw-mcp"],
6
6
  "env": {
7
- "OFW_USERNAME": "${OFW_USERNAME}",
8
- "OFW_PASSWORD": "${OFW_PASSWORD}"
7
+ "OFW_USERNAME": "${user_config.ofw_username}",
8
+ "OFW_PASSWORD": "${user_config.ofw_password}",
9
+ "OFW_INLINE_ATTACHMENTS": "${user_config.ofw_inline_attachments}",
10
+ "OFW_ATTACHMENTS_DIR": "${user_config.ofw_attachments_dir}",
11
+ "OFW_WRITE_MODE": "${user_config.ofw_write_mode}"
9
12
  }
10
13
  }
11
14
  }
package/dist/bundle.js CHANGED
@@ -13,7 +13,11 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
13
13
  throw Error('Dynamic require of "' + x + '" is not supported');
14
14
  });
15
15
  var __commonJS = (cb, mod) => function __require2() {
16
- return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
16
+ try {
17
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
18
+ } catch (e) {
19
+ throw mod = 0, e;
20
+ }
17
21
  };
18
22
  var __export = (target, all) => {
19
23
  for (var name in all)
@@ -38146,7 +38150,7 @@ function getDefaultInlineAttachments() {
38146
38150
  // package.json
38147
38151
  var package_default = {
38148
38152
  name: "ofw-mcp",
38149
- version: "2.4.0",
38153
+ version: "2.4.1",
38150
38154
  license: "MIT",
38151
38155
  mcpName: "io.github.chrischall/ofw-mcp",
38152
38156
  description: "OurFamilyWizard MCP server for Claude \u2014 developed and maintained by AI (Claude Code)",
@@ -38257,7 +38261,26 @@ async function resolveAuth() {
38257
38261
  );
38258
38262
  }
38259
38263
 
38264
+ // src/env-bootstrap.ts
38265
+ var USER_CONFIG_KEYS = [
38266
+ "OFW_USERNAME",
38267
+ "OFW_PASSWORD",
38268
+ "OFW_INLINE_ATTACHMENTS",
38269
+ "OFW_ATTACHMENTS_DIR",
38270
+ "OFW_WRITE_MODE"
38271
+ ];
38272
+ function clearBlankInjectedEnv(env = process.env, keys = USER_CONFIG_KEYS) {
38273
+ for (const key of keys) {
38274
+ const value = env[key];
38275
+ if (value === void 0) continue;
38276
+ if (value.trim() === "" || value.includes("${")) {
38277
+ delete env[key];
38278
+ }
38279
+ }
38280
+ }
38281
+
38260
38282
  // src/client.ts
38283
+ clearBlankInjectedEnv();
38261
38284
  var __dirname = dirname(fileURLToPath(import.meta.url));
38262
38285
  await loadDotenvSafely({ path: join4(__dirname, "..", ".env") });
38263
38286
  function parseContentDispositionFilename(cd) {
@@ -39783,7 +39806,7 @@ process.emit = function(event, ...args) {
39783
39806
  };
39784
39807
  await runMcp({
39785
39808
  name: "ofw",
39786
- version: "2.4.0",
39809
+ version: "2.4.1",
39787
39810
  // x-release-please-version
39788
39811
  deps: client,
39789
39812
  tools: [
package/dist/client.js CHANGED
@@ -4,7 +4,13 @@ import { dirname, join } from 'path';
4
4
  import { fileURLToPath } from 'url';
5
5
  import { resolveAuth } from './auth.js';
6
6
  import { parseBoolEnv } from './config.js';
7
+ import { clearBlankInjectedEnv } from './env-bootstrap.js';
7
8
  import { BASE_URL, OFW_PROTOCOL_HEADERS, OFW_TOKEN_TTL_MS, OFW_TOKEN_EXPIRY_SKEW_MS } from './protocol.js';
9
+ // When the plugin runs via a host that maps creds from optional
10
+ // `${user_config.*}` fields, an unset field can be injected as a blank /
11
+ // placeholder env value. Clear those first so the next step's .env (and the
12
+ // shell env) can still populate them — a filled field keeps its real value.
13
+ clearBlankInjectedEnv();
8
14
  // Load .env for local dev; silently skip if dotenv is unavailable (e.g. mcpb
9
15
  // bundle). loadDotenvSafely applies override:false + quiet:true and swallows a
10
16
  // missing dotenv module, matching the prior inline try/catch exactly.
@@ -0,0 +1,28 @@
1
+ // Credentials + config the plugin's .mcp.json maps from `${user_config.*}`.
2
+ // These are the keys whose host-injected values we sanity-check before
3
+ // loading .env.
4
+ export const USER_CONFIG_KEYS = [
5
+ 'OFW_USERNAME',
6
+ 'OFW_PASSWORD',
7
+ 'OFW_INLINE_ATTACHMENTS',
8
+ 'OFW_ATTACHMENTS_DIR',
9
+ 'OFW_WRITE_MODE',
10
+ ];
11
+ // A host that maps an UNSET optional `${user_config.x}` into the server env
12
+ // may inject the key as an empty string or the literal, unexpanded
13
+ // "${user_config.x}". Either one SHADOWS the user's `.env`/shell value,
14
+ // because the server loads .env with dotenv `override:false` (it won't
15
+ // replace a key that's already present). Clearing those blanks first lets the
16
+ // `.env`/shell credential path keep working when the Connectors field is left
17
+ // empty — while a real, user-provided value is left untouched (so the desktop
18
+ // userConfig path still wins when set).
19
+ export function clearBlankInjectedEnv(env = process.env, keys = USER_CONFIG_KEYS) {
20
+ for (const key of keys) {
21
+ const value = env[key];
22
+ if (value === undefined)
23
+ continue;
24
+ if (value.trim() === '' || value.includes('${')) {
25
+ delete env[key];
26
+ }
27
+ }
28
+ }
package/dist/index.js CHANGED
@@ -24,7 +24,7 @@ import { registerJournalTools } from './tools/journal.js';
24
24
  // always succeeds before any credential check runs.
25
25
  await runMcp({
26
26
  name: 'ofw',
27
- version: '2.4.0', // x-release-please-version
27
+ version: '2.4.1', // x-release-please-version
28
28
  deps: client,
29
29
  tools: [
30
30
  registerUserTools,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ofw-mcp",
3
- "version": "2.4.0",
3
+ "version": "2.4.1",
4
4
  "license": "MIT",
5
5
  "mcpName": "io.github.chrischall/ofw-mcp",
6
6
  "description": "OurFamilyWizard MCP server for Claude — developed and maintained by AI (Claude Code)",
package/server.json CHANGED
@@ -6,12 +6,12 @@
6
6
  "url": "https://github.com/chrischall/ofw-mcp",
7
7
  "source": "github"
8
8
  },
9
- "version": "2.4.0",
9
+ "version": "2.4.1",
10
10
  "packages": [
11
11
  {
12
12
  "registryType": "npm",
13
13
  "identifier": "ofw-mcp",
14
- "version": "2.4.0",
14
+ "version": "2.4.1",
15
15
  "transport": {
16
16
  "type": "stdio"
17
17
  },