@chrischall/mcp-utils 0.23.1 → 0.23.2
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 +7 -1
- package/dist/response/media.d.ts +17 -1
- package/dist/response/media.d.ts.map +1 -1
- package/dist/response/media.js +31 -7
- package/dist/response/media.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -163,9 +163,15 @@ mutates its input. Do **not** apply it to a tool whose product IS the image —
|
|
|
163
163
|
records are literally a `photoUrls` bundle) — where it empties the response
|
|
164
164
|
rather than shrinking it.
|
|
165
165
|
|
|
166
|
+
**Arrays of bare image URLs under a non-media key are kept** — `floorplan_urls:
|
|
167
|
+
['a.jpg', 'b.jpg']` comes back whole. That is deliberate: removing a *key* is
|
|
168
|
+
visible, removing *elements* is not, and a caller reading `.length` to report
|
|
169
|
+
"4 floor plans" would be quietly wrong. Use `drop` for those. An array under a
|
|
170
|
+
media-named key (`photos: [...]`) is already removed by the key rule.
|
|
171
|
+
|
|
166
172
|
Two escape hatches, and they are symmetric: `keep` preserves a key that looks
|
|
167
173
|
like media but is the thing the caller asked for; `drop` adds keys this pattern
|
|
168
|
-
does not know
|
|
174
|
+
does not know. Both take `string | RegExp`, so a service with an unguessed naming convention can fix itself
|
|
169
175
|
**without a library release** — which is what Google Workspace's
|
|
170
176
|
`thumbnailLink`/`iconUri`/`photoUrl` cost the first time round. `keep` wins over
|
|
171
177
|
`drop`.
|
package/dist/response/media.d.ts
CHANGED
|
@@ -25,6 +25,10 @@
|
|
|
25
25
|
* answered. Three points is not worth a payload that can no longer distinguish
|
|
26
26
|
* "no value" from "not reported".
|
|
27
27
|
*
|
|
28
|
+
* **Arrays of bare image URLs under a non-media key are KEPT** — see the note
|
|
29
|
+
* in `walk`. Use `drop` for those; the key rule already covers an array under
|
|
30
|
+
* a media-named key (`photos: [...]`).
|
|
31
|
+
*
|
|
28
32
|
* **Never apply this to a tool whose PRODUCT is the image.**
|
|
29
33
|
* `alltrails_get_trail_photos`, `zillow_get_property_photos`,
|
|
30
34
|
* `sw_get_receipt`, `musescore_fetch_svgs` exist to return exactly these URLs,
|
|
@@ -36,8 +40,20 @@ export interface StripMediaOptions {
|
|
|
36
40
|
/**
|
|
37
41
|
* Keys to keep even when they look like media — for a payload that mixes a
|
|
38
42
|
* decorative avatar with an image the caller actually asked for.
|
|
43
|
+
*
|
|
44
|
+
* Takes the same `string | RegExp` shapes as `drop`. It did not until this
|
|
45
|
+
* was noticed: `drop` gained RegExp support in #194 and `keep` was left on
|
|
46
|
+
* strings, so a repo preserving several CONSTRUCTED media fields that share a
|
|
47
|
+
* prefix — redfin-mcp's `image_url` + `thumbnail_url`, compass-mcp's
|
|
48
|
+
* `primary_photo_url` + `primary_thumbnail_url` — had to enumerate each one
|
|
49
|
+
* while the opposite intent could be a pattern.
|
|
50
|
+
*
|
|
51
|
+
* That was an oversight in the API rather than a judgement, and if either
|
|
52
|
+
* side deserved the expressiveness first it was this one: a missed `drop`
|
|
53
|
+
* entry leaves bytes in the response, while a missed `keep` entry silently
|
|
54
|
+
* deletes a field the caller asked for.
|
|
39
55
|
*/
|
|
40
|
-
keep?: readonly string[];
|
|
56
|
+
keep?: readonly (string | RegExp)[];
|
|
41
57
|
/**
|
|
42
58
|
* Extra keys to drop, for a service whose naming this pattern does not know.
|
|
43
59
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"media.d.ts","sourceRoot":"","sources":["../../src/response/media.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"media.d.ts","sourceRoot":"","sources":["../../src/response/media.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAiFH,MAAM,WAAW,iBAAiB;IAChC;;;;;;;;;;;;;;;OAeG;IACH,IAAI,CAAC,EAAE,SAAS,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;IACpC;;;;;;;;;;;;;;OAcG;IACH,IAAI,CAAC,EAAE,SAAS,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;CACrC;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,GAAE,iBAAsB,GAAG,CAAC,CAoB3E"}
|
package/dist/response/media.js
CHANGED
|
@@ -25,6 +25,10 @@
|
|
|
25
25
|
* answered. Three points is not worth a payload that can no longer distinguish
|
|
26
26
|
* "no value" from "not reported".
|
|
27
27
|
*
|
|
28
|
+
* **Arrays of bare image URLs under a non-media key are KEPT** — see the note
|
|
29
|
+
* in `walk`. Use `drop` for those; the key rule already covers an array under
|
|
30
|
+
* a media-named key (`photos: [...]`).
|
|
31
|
+
*
|
|
28
32
|
* **Never apply this to a tool whose PRODUCT is the image.**
|
|
29
33
|
* `alltrails_get_trail_photos`, `zillow_get_property_photos`,
|
|
30
34
|
* `sw_get_receipt`, `musescore_fetch_svgs` exist to return exactly these URLs,
|
|
@@ -113,7 +117,9 @@ const MEDIA_URL = /^https?:\/\/[^\s]+?\.(png|jpe?g|gif|webp|svg|avif|bmp|ico)([?
|
|
|
113
117
|
* The input is never mutated: several repos hand these helpers live cache rows.
|
|
114
118
|
*/
|
|
115
119
|
export function stripMediaUrls(value, opts = {}) {
|
|
116
|
-
|
|
120
|
+
// `keep` is normalised exactly like `drop` below — same copy-the-regex
|
|
121
|
+
// reasoning, same once-per-call lowercasing.
|
|
122
|
+
const keep = normalizeRules(opts.keep ?? []);
|
|
117
123
|
// Every RegExp rule is COPIED, once per call, for two reasons.
|
|
118
124
|
//
|
|
119
125
|
// `test()` on a `g`- or `y`-flagged regex advances `lastIndex` on a match, so
|
|
@@ -128,13 +134,17 @@ export function stripMediaUrls(value, opts = {}) {
|
|
|
128
134
|
// caller's RegExp is input too. The docs invite hoisting `drop` as a shared
|
|
129
135
|
// constant, which is exactly when someone else's `lastIndex` would be ours to
|
|
130
136
|
// corrupt. Strings are lowercased here for the same once-per-call reason.
|
|
131
|
-
const drop = (opts.drop ?? [])
|
|
137
|
+
const drop = normalizeRules(opts.drop ?? []);
|
|
132
138
|
return walk(value, keep, drop);
|
|
133
139
|
}
|
|
134
|
-
/**
|
|
135
|
-
function
|
|
140
|
+
/** Lowercase the strings and copy the regexes, once per call. See above. */
|
|
141
|
+
function normalizeRules(rules) {
|
|
142
|
+
return rules.map((rule) => (typeof rule === 'string' ? rule.toLowerCase() : new RegExp(rule.source, rule.flags)));
|
|
143
|
+
}
|
|
144
|
+
/** Does `key` match one of the caller's rules? Used for both `keep` and `drop`. */
|
|
145
|
+
function matchesRule(key, rules) {
|
|
136
146
|
const lower = key.toLowerCase();
|
|
137
|
-
for (const rule of
|
|
147
|
+
for (const rule of rules) {
|
|
138
148
|
if (typeof rule === 'string') {
|
|
139
149
|
if (rule === lower)
|
|
140
150
|
return true;
|
|
@@ -148,6 +158,20 @@ function alsoDrop(key, drop) {
|
|
|
148
158
|
return false;
|
|
149
159
|
}
|
|
150
160
|
function walk(value, keep, drop) {
|
|
161
|
+
// Array ELEMENTS are walked but never value-tested, so an array of bare image
|
|
162
|
+
// URLs under a non-media key — homes-mcp's `floorplan_urls` — comes back
|
|
163
|
+
// whole. That is deliberate, and it is the one place this helper knowingly
|
|
164
|
+
// leaves bytes on the table.
|
|
165
|
+
//
|
|
166
|
+
// Removing a KEY is visible: the field is gone and a reader can see that it
|
|
167
|
+
// is. Removing ELEMENTS is invisible — the array is merely shorter, and a
|
|
168
|
+
// caller reading `floorplan_urls.length` to say "this listing has 4 floor
|
|
169
|
+
// plans" would be quietly wrong. That is the same class of harm as the
|
|
170
|
+
// dropped-nulls rule this helper also refuses (see the docblock above):
|
|
171
|
+
// a silently altered count reads as fact.
|
|
172
|
+
//
|
|
173
|
+
// The fix for such a field is `drop: ['floorplan_urls']` at the call site,
|
|
174
|
+
// which removes the key outright and stays legible in the response.
|
|
151
175
|
if (Array.isArray(value))
|
|
152
176
|
return value.map((v) => walk(v, keep, drop));
|
|
153
177
|
// `null` is data here, not an empty object — see the docblock.
|
|
@@ -160,11 +184,11 @@ function walk(value, keep, drop) {
|
|
|
160
184
|
return value;
|
|
161
185
|
const out = {};
|
|
162
186
|
for (const [key, v] of Object.entries(value)) {
|
|
163
|
-
if (
|
|
187
|
+
if (matchesRule(key, keep)) {
|
|
164
188
|
out[key] = v;
|
|
165
189
|
continue;
|
|
166
190
|
}
|
|
167
|
-
if (MEDIA_KEY.test(key) ||
|
|
191
|
+
if (MEDIA_KEY.test(key) || matchesRule(key, drop))
|
|
168
192
|
continue;
|
|
169
193
|
if (typeof v === 'string' && MEDIA_URL.test(v))
|
|
170
194
|
continue;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"media.js","sourceRoot":"","sources":["../../src/response/media.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"media.js","sourceRoot":"","sources":["../../src/response/media.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chrischall/mcp-utils",
|
|
3
|
-
"version": "0.23.
|
|
3
|
+
"version": "0.23.2",
|
|
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",
|