@chrischall/mcp-utils 0.21.0 → 0.22.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 +12 -1
- package/dist/response/index.d.ts +1 -0
- package/dist/response/index.d.ts.map +1 -1
- package/dist/response/index.js +1 -0
- package/dist/response/index.js.map +1 -1
- package/dist/response/media.d.ts +52 -0
- package/dist/response/media.d.ts.map +1 -0
- package/dist/response/media.js +93 -0
- package/dist/response/media.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -108,7 +108,7 @@ deepMapStringField(payload, 'eventDate', dmyToIso);
|
|
|
108
108
|
#### The `view` vocabulary — read tools answer in the cheap shape by default
|
|
109
109
|
|
|
110
110
|
`VIEWS`, `DEFAULT_VIEW`, `View`, `viewParam`, `resolveView`, `viewResult`,
|
|
111
|
-
`minifiedResult`, `projectOrRaw`. See `docs/fleet-conventions.md`
|
|
111
|
+
`minifiedResult`, `projectOrRaw`, `stripMediaUrls`. See `docs/fleet-conventions.md`
|
|
112
112
|
("Response shape") for the convention these implement.
|
|
113
113
|
|
|
114
114
|
`view: 'compact' | 'full' | 'raw'`, defaulting to **`compact`** — a projection
|
|
@@ -142,6 +142,17 @@ where a record is *assembled* from several endpoints rather than passed through
|
|
|
142
142
|
from one, and a value that silently aliases to another is a lie in the schema.
|
|
143
143
|
`raw` means "no projection" — never "no normalisation".
|
|
144
144
|
|
|
145
|
+
`stripMediaUrls(payload)` is the highest-value projection that needs no
|
|
146
|
+
knowledge of the API: it drops `avatar` / `picture` / `cover_photo` /
|
|
147
|
+
`thumbnail` keys and bare image URLs. Measured on a real 187.6 KB
|
|
148
|
+
`splitwise-mcp` groups response — which does not fit in a tool result at all —
|
|
149
|
+
minifying alone is −25%, minifying **and** stripping media is **−73%**. It
|
|
150
|
+
deliberately keeps `null` (an absent key and a null one are different facts;
|
|
151
|
+
`ofw-mcp`'s `viewedAt: null` means "never opened"), keeps page URLs, and never
|
|
152
|
+
mutates its input. Do **not** apply it to a tool whose product IS the image —
|
|
153
|
+
`alltrails_get_trail_photos`, `sw_get_receipt` — where it empties the response
|
|
154
|
+
rather than shrinking it; `keep` is the escape hatch for a mixed payload.
|
|
155
|
+
|
|
145
156
|
`viewResult` minifies `compact` and `full` and leaves `raw` indented (that rung
|
|
146
157
|
exists to be read by a person); `minifiedResult` is the same rule with no view
|
|
147
158
|
to hand. Formatting whitespace only — whitespace *inside* a value is content
|
package/dist/response/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/response/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAIzE;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,OAAO,GAAG,cAAc,CAIxD;AAED,sEAAsE;AACtE,eAAO,MAAM,UAAU,mBAAa,CAAC;AAErC,qEAAqE;AACrE,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,CAE1D;AAED,yDAAyD;AACzD,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,cAAc,CAI5E;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,CAK3D;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAoBxD;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,GAAG,CAAC,CAY5F;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAMpF;AAED;;;;;;;GAOG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,EAAE,GAAG,IAAI,GAAG,SAAS,GAAG,CAAC,EAAE,CAGjE;AACD,cAAc,WAAW,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/response/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAIzE;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,OAAO,GAAG,cAAc,CAIxD;AAED,sEAAsE;AACtE,eAAO,MAAM,UAAU,mBAAa,CAAC;AAErC,qEAAqE;AACrE,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,CAE1D;AAED,yDAAyD;AACzD,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,cAAc,CAI5E;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,CAK3D;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAoBxD;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,GAAG,CAAC,CAY5F;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAMpF;AAED;;;;;;;GAOG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,EAAE,GAAG,IAAI,GAAG,SAAS,GAAG,CAAC,EAAE,CAGjE;AACD,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC"}
|
package/dist/response/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/response/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/response/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Drop image and avatar references from a payload.
|
|
3
|
+
*
|
|
4
|
+
* This is the single highest-value, lowest-risk projection in the fleet, and it
|
|
5
|
+
* needs no knowledge of the API at all. Measured on a real
|
|
6
|
+
* `splitwise-mcp sw_list_groups` response — 51 groups, 187.6 KB, which does not
|
|
7
|
+
* fit in a tool result at all and fails outright:
|
|
8
|
+
*
|
|
9
|
+
* | | size | |
|
|
10
|
+
* |---|---|---|
|
|
11
|
+
* | as shipped (pretty) | 187.6 KB | — |
|
|
12
|
+
* | minified | 140.5 KB | −25% |
|
|
13
|
+
* | minified + media stripped | **51.4 KB** | **−73%** |
|
|
14
|
+
*
|
|
15
|
+
* 60% of that payload was `avatar`, `tall_avatar`, `cover_photo` and a
|
|
16
|
+
* `picture` per member — URLs a model cannot see, cannot fetch, and would not
|
|
17
|
+
* benefit from if it could.
|
|
18
|
+
*
|
|
19
|
+
* **Why not also drop nulls.** It was the obvious companion rule and it is
|
|
20
|
+
* rejected on purpose: measured on the same payload it buys three further
|
|
21
|
+
* points (−73% → −75%), and it costs meaning. `ofw-mcp` emits
|
|
22
|
+
* `recipients[].viewedAt: null` to say "this person has never opened it",
|
|
23
|
+
* which is evidence in a custody record; an absent key and a null one are the
|
|
24
|
+
* same to `JSON.parse` but not to a reader deciding whether the question was
|
|
25
|
+
* answered. Three points is not worth a payload that can no longer distinguish
|
|
26
|
+
* "no value" from "not reported".
|
|
27
|
+
*
|
|
28
|
+
* **Never apply this to a tool whose PRODUCT is the image.**
|
|
29
|
+
* `alltrails_get_trail_photos`, `zillow_get_property_photos`,
|
|
30
|
+
* `sw_get_receipt`, `musescore_fetch_svgs` exist to return exactly these URLs,
|
|
31
|
+
* and stripping them there does not shrink the response, it empties it. The
|
|
32
|
+
* tool's own name is the test. `keep` is the escape hatch for a payload that
|
|
33
|
+
* mixes both.
|
|
34
|
+
*/
|
|
35
|
+
export interface StripMediaOptions {
|
|
36
|
+
/**
|
|
37
|
+
* Keys to keep even when they look like media — for a payload that mixes a
|
|
38
|
+
* decorative avatar with an image the caller actually asked for.
|
|
39
|
+
*/
|
|
40
|
+
keep?: readonly string[];
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Return a copy of `value` with media keys and bare image URLs removed.
|
|
44
|
+
*
|
|
45
|
+
* Recurses through arrays and plain objects. Everything else — strings,
|
|
46
|
+
* numbers, booleans, **null**, Dates — is passed through untouched, so a
|
|
47
|
+
* `null` that means "never viewed" survives.
|
|
48
|
+
*
|
|
49
|
+
* The input is never mutated: several repos hand these helpers live cache rows.
|
|
50
|
+
*/
|
|
51
|
+
export declare function stripMediaUrls<T>(value: T, opts?: StripMediaOptions): T;
|
|
52
|
+
//# sourceMappingURL=media.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media.d.ts","sourceRoot":"","sources":["../../src/response/media.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAyBH,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1B;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,GAAE,iBAAsB,GAAG,CAAC,CAG3E"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Drop image and avatar references from a payload.
|
|
3
|
+
*
|
|
4
|
+
* This is the single highest-value, lowest-risk projection in the fleet, and it
|
|
5
|
+
* needs no knowledge of the API at all. Measured on a real
|
|
6
|
+
* `splitwise-mcp sw_list_groups` response — 51 groups, 187.6 KB, which does not
|
|
7
|
+
* fit in a tool result at all and fails outright:
|
|
8
|
+
*
|
|
9
|
+
* | | size | |
|
|
10
|
+
* |---|---|---|
|
|
11
|
+
* | as shipped (pretty) | 187.6 KB | — |
|
|
12
|
+
* | minified | 140.5 KB | −25% |
|
|
13
|
+
* | minified + media stripped | **51.4 KB** | **−73%** |
|
|
14
|
+
*
|
|
15
|
+
* 60% of that payload was `avatar`, `tall_avatar`, `cover_photo` and a
|
|
16
|
+
* `picture` per member — URLs a model cannot see, cannot fetch, and would not
|
|
17
|
+
* benefit from if it could.
|
|
18
|
+
*
|
|
19
|
+
* **Why not also drop nulls.** It was the obvious companion rule and it is
|
|
20
|
+
* rejected on purpose: measured on the same payload it buys three further
|
|
21
|
+
* points (−73% → −75%), and it costs meaning. `ofw-mcp` emits
|
|
22
|
+
* `recipients[].viewedAt: null` to say "this person has never opened it",
|
|
23
|
+
* which is evidence in a custody record; an absent key and a null one are the
|
|
24
|
+
* same to `JSON.parse` but not to a reader deciding whether the question was
|
|
25
|
+
* answered. Three points is not worth a payload that can no longer distinguish
|
|
26
|
+
* "no value" from "not reported".
|
|
27
|
+
*
|
|
28
|
+
* **Never apply this to a tool whose PRODUCT is the image.**
|
|
29
|
+
* `alltrails_get_trail_photos`, `zillow_get_property_photos`,
|
|
30
|
+
* `sw_get_receipt`, `musescore_fetch_svgs` exist to return exactly these URLs,
|
|
31
|
+
* and stripping them there does not shrink the response, it empties it. The
|
|
32
|
+
* tool's own name is the test. `keep` is the escape hatch for a payload that
|
|
33
|
+
* mixes both.
|
|
34
|
+
*/
|
|
35
|
+
/** Keys whose value is a picture, whatever it holds. */
|
|
36
|
+
const MEDIA_KEY = /^(avatar|tall_avatar|cover_photo|cover_image|picture|photo|thumbnail|thumb|image|icon|banner|profile_pic(ture)?|logo)s?$/i;
|
|
37
|
+
/**
|
|
38
|
+
* A URL that points at an image rather than at a page: a known image extension
|
|
39
|
+
* ending the PATH.
|
|
40
|
+
*
|
|
41
|
+
* Nothing looser. Earlier drafts also matched an `avatar` path segment and a
|
|
42
|
+
* bare `avatar` word, and both were false positives waiting to happen —
|
|
43
|
+
* `…/users/avatar-collection` and `…/v1/avatar/settings` are pages, and this
|
|
44
|
+
* helper's whole promise is that it keeps pages. Measured on the payload that
|
|
45
|
+
* motivated the helper, the extra clauses removed exactly ZERO additional
|
|
46
|
+
* bytes: every avatar in a real Splitwise response is either under a media KEY
|
|
47
|
+
* (caught by `MEDIA_KEY`, whatever the URL looks like) or ends in `.png`/`.jpg`.
|
|
48
|
+
* A rule that adds risk and removes nothing is not a rule.
|
|
49
|
+
*
|
|
50
|
+
* The extension must END the path, so a signed URL whose query happens to
|
|
51
|
+
* contain `.jpeg` — Splitwise's `…/receipt?cachebust=29f.jpeg&size=large` — is
|
|
52
|
+
* KEPT. That one is content a caller asked for, not decoration.
|
|
53
|
+
*/
|
|
54
|
+
const MEDIA_URL = /^https?:\/\/[^\s]+?\.(png|jpe?g|gif|webp|svg|avif|bmp|ico)([?#]|$)/i;
|
|
55
|
+
/**
|
|
56
|
+
* Return a copy of `value` with media keys and bare image URLs removed.
|
|
57
|
+
*
|
|
58
|
+
* Recurses through arrays and plain objects. Everything else — strings,
|
|
59
|
+
* numbers, booleans, **null**, Dates — is passed through untouched, so a
|
|
60
|
+
* `null` that means "never viewed" survives.
|
|
61
|
+
*
|
|
62
|
+
* The input is never mutated: several repos hand these helpers live cache rows.
|
|
63
|
+
*/
|
|
64
|
+
export function stripMediaUrls(value, opts = {}) {
|
|
65
|
+
const keep = new Set((opts.keep ?? []).map((k) => k.toLowerCase()));
|
|
66
|
+
return walk(value, keep);
|
|
67
|
+
}
|
|
68
|
+
function walk(value, keep) {
|
|
69
|
+
if (Array.isArray(value))
|
|
70
|
+
return value.map((v) => walk(v, keep));
|
|
71
|
+
// `null` is data here, not an empty object — see the docblock.
|
|
72
|
+
if (value === null || typeof value !== 'object')
|
|
73
|
+
return value;
|
|
74
|
+
// Anything with a prototype of its own (Date, Map, a class instance) is left
|
|
75
|
+
// alone: rebuilding it from its enumerable keys would quietly change what it
|
|
76
|
+
// is, and none of them carry avatars.
|
|
77
|
+
if (Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null)
|
|
78
|
+
return value;
|
|
79
|
+
const out = {};
|
|
80
|
+
for (const [key, v] of Object.entries(value)) {
|
|
81
|
+
if (keep.has(key.toLowerCase())) {
|
|
82
|
+
out[key] = v;
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
if (MEDIA_KEY.test(key))
|
|
86
|
+
continue;
|
|
87
|
+
if (typeof v === 'string' && MEDIA_URL.test(v))
|
|
88
|
+
continue;
|
|
89
|
+
out[key] = walk(v, keep);
|
|
90
|
+
}
|
|
91
|
+
return out;
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=media.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media.js","sourceRoot":"","sources":["../../src/response/media.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,wDAAwD;AACxD,MAAM,SAAS,GACb,2HAA2H,CAAC;AAE9H;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,SAAS,GAAG,qEAAqE,CAAC;AAUxF;;;;;;;;GAQG;AACH,MAAM,UAAU,cAAc,CAAI,KAAQ,EAAE,OAA0B,EAAE;IACtE,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;IACpE,OAAO,IAAI,CAAC,KAAK,EAAE,IAAI,CAAM,CAAC;AAChC,CAAC;AAED,SAAS,IAAI,CAAC,KAAc,EAAE,IAAyB;IACrD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;IACjE,+DAA+D;IAC/D,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC9D,6EAA6E;IAC7E,6EAA6E;IAC7E,sCAAsC;IACtC,IAAI,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,KAAK,MAAM,CAAC,SAAS,IAAI,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC7G,MAAM,GAAG,GAA4B,EAAE,CAAC;IACxC,KAAK,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAgC,CAAC,EAAE,CAAC;QACxE,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC;YAChC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACb,SAAS;QACX,CAAC;QACD,IAAI,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC;YAAE,SAAS;QAClC,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;YAAE,SAAS;QACzD,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;IAC3B,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chrischall/mcp-utils",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.0",
|
|
4
4
|
"description": "Shared scaffolding for the chrischall MCP fleet — server bootstrap, tool-result formatting, helpful errors, hardened env/config, a bearer API-client kit, zod atoms, session registries, a fetchproxy transport adapter, auth resolver skeletons, an in-memory test harness, and opt-in HTML helpers. The generic MCP glue hoisted out of ~19 sibling servers.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|