@j0hanz/fetch-url-mcp 1.10.19 → 1.10.21

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.
@@ -1 +1 @@
1
- {"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../../src/transform/metadata.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAoCnE,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,MAAM,GACf,MAAM,CAkBR;AAwID,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,QAAQ,EAClB,OAAO,CAAC,EAAE,MAAM,GACf,iBAAiB,CAkBnB;AAED,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,MAAM,GACf,iBAAiB,GAAG,IAAI,CAY1B;AAED,wBAAgB,aAAa,CAC3B,KAAK,EAAE,iBAAiB,GAAG,IAAI,EAC/B,IAAI,EAAE,iBAAiB,GACtB,iBAAiB,CAmBnB;AA2GD,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,MAAM,GACd,MAAM,GAAG,SAAS,CAOpB;AACD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAuCxE;AAmBD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAczD;AAaD,wBAAgB,mBAAmB,CACjC,QAAQ,CAAC,EAAE,aAAa,EACxB,WAAW,CAAC,EAAE,MAAM,GACnB,MAAM,CAmBR"}
1
+ {"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../../src/transform/metadata.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAoCnE,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,MAAM,GACf,MAAM,CAkBR;AAuJD,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,QAAQ,EAClB,OAAO,CAAC,EAAE,MAAM,GACf,iBAAiB,CAYnB;AAED,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,MAAM,GACf,iBAAiB,GAAG,IAAI,CAY1B;AAED,wBAAgB,aAAa,CAC3B,KAAK,EAAE,iBAAiB,GAAG,IAAI,EAC/B,IAAI,EAAE,iBAAiB,GACtB,iBAAiB,CAmBnB;AA2GD,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,MAAM,GACd,MAAM,GAAG,SAAS,CAOpB;AACD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAuCxE;AAmBD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAczD;AAaD,wBAAgB,mBAAmB,CACjC,QAAQ,CAAC,EAAE,aAAa,EACxB,WAAW,CAAC,EAAE,MAAM,GACnB,MAAM,CAmBR"}
@@ -136,21 +136,36 @@ function resolveMetadataFromContext(ctx) {
136
136
  // ---------------------------------------------------------------------------
137
137
  // Favicon resolution
138
138
  // ---------------------------------------------------------------------------
139
+ /** Ordered by preference: exact 32×32, SVG, any generic icon, legacy shortcut. */
140
+ const FAVICON_SELECTORS = [
141
+ 'link[rel="icon"][sizes="32x32"]',
142
+ 'link[rel="icon"][type="image/svg+xml"]',
143
+ 'link[rel="icon"]',
144
+ 'link[rel="shortcut icon"]',
145
+ ];
139
146
  function resolveFaviconUrl(href, baseUrl) {
140
147
  const trimmed = href.trim();
141
- if (!trimmed)
142
- return undefined;
143
- if (trimmed.toLowerCase().startsWith('data:'))
148
+ if (!trimmed || trimmed.toLowerCase().startsWith('data:'))
144
149
  return undefined;
145
150
  const resolved = parseUrlOrNull(trimmed, baseUrl);
146
- if (!resolved) {
147
- return undefined;
148
- }
149
- if (resolved.protocol !== 'http:' && resolved.protocol !== 'https:') {
151
+ if (resolved?.protocol !== 'http:' && resolved?.protocol !== 'https:') {
150
152
  return undefined;
151
153
  }
152
154
  return resolved.toString();
153
155
  }
156
+ function extractFavicon(document, baseUrl) {
157
+ for (const selector of FAVICON_SELECTORS) {
158
+ for (const el of document.querySelectorAll(selector)) {
159
+ const href = el.getAttribute('href');
160
+ if (href) {
161
+ const resolved = resolveFaviconUrl(href, baseUrl);
162
+ if (resolved)
163
+ return resolved;
164
+ }
165
+ }
166
+ }
167
+ return undefined;
168
+ }
154
169
  // ---------------------------------------------------------------------------
155
170
  // Public interface
156
171
  // ---------------------------------------------------------------------------
@@ -161,13 +176,9 @@ export function extractMetadata(document, baseUrl) {
161
176
  metadata.title = normalizeDocumentTitle(metadata.title, baseUrl);
162
177
  }
163
178
  if (baseUrl) {
164
- const icon32 = document.querySelector('link[rel="icon"][sizes="32x32"]');
165
- const href = icon32?.getAttribute('href');
166
- if (href) {
167
- const resolved = resolveFaviconUrl(href, baseUrl);
168
- if (resolved)
169
- metadata.favicon = resolved;
170
- }
179
+ const favicon = extractFavicon(document, baseUrl);
180
+ if (favicon)
181
+ metadata.favicon = favicon;
171
182
  }
172
183
  return metadata;
173
184
  }
@@ -1 +1 @@
1
- {"version":3,"file":"title-policy.d.ts","sourceRoot":"","sources":["../../src/transform/title-policy.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,QAAQ,CAAC,wBAAwB,CAAC,EAAE,OAAO,CAAC;CAC7C;AAMD,wBAAgB,4BAA4B,CAC1C,KAAK,EAAE,MAAM,GAAG,SAAS,GACxB,MAAM,CAER;AAED,wBAAgB,+BAA+B,CAC7C,cAAc,EAAE,MAAM,GAAG,SAAS,EAClC,KAAK,EAAE,MAAM,GAAG,SAAS,GACxB,OAAO,CAWT;AAED,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAU9D;AAsCD,wBAAgB,8BAA8B,CAC5C,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,MAAM,GAAG,SAAS,EAClC,GAAG,EAAE,MAAM,GACV,MAAM,CAMR;AAcD,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,qBAAqB,EAC9B,GAAG,EAAE,MAAM,GACV,MAAM,CAUR"}
1
+ {"version":3,"file":"title-policy.d.ts","sourceRoot":"","sources":["../../src/transform/title-policy.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,QAAQ,CAAC,wBAAwB,CAAC,EAAE,OAAO,CAAC;CAC7C;AAMD,wBAAgB,4BAA4B,CAC1C,KAAK,EAAE,MAAM,GAAG,SAAS,GACxB,MAAM,CAER;AAED,wBAAgB,+BAA+B,CAC7C,cAAc,EAAE,MAAM,GAAG,SAAS,EAClC,KAAK,EAAE,MAAM,GAAG,SAAS,GACxB,OAAO,CAWT;AAED,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAU9D;AAkCD,wBAAgB,8BAA8B,CAC5C,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,MAAM,GAAG,SAAS,EAClC,GAAG,EAAE,MAAM,GACV,MAAM,CAMR;AAED,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,qBAAqB,EAC9B,GAAG,EAAE,MAAM,GACV,MAAM,CAYR"}
@@ -28,14 +28,11 @@ export function isGithubRepositoryRootUrl(url) {
28
28
  }
29
29
  return parsed.pathname.split('/').filter(Boolean).length === 2;
30
30
  }
31
- function normalizeHeadingText(value) {
32
- return value.replace(/\s+/g, ' ').trim().toLowerCase();
33
- }
34
31
  function stripLeadingHeading(markdown, headingText) {
35
32
  if (!markdown)
36
33
  return markdown;
37
34
  const lines = markdown.split('\n');
38
- const target = normalizeHeadingText(headingText);
35
+ const target = normalizeSyntheticTitleToken(headingText);
39
36
  let nonEmptySeen = 0;
40
37
  for (let index = 0; index < lines.length && nonEmptySeen < HEADING_SCAN_LIMIT; index += 1) {
41
38
  const trimmed = lines[index]?.trim() ?? '';
@@ -45,7 +42,7 @@ function stripLeadingHeading(markdown, headingText) {
45
42
  const match = LEADING_HEADING_PATTERN.exec(trimmed);
46
43
  if (!match)
47
44
  continue;
48
- const current = normalizeHeadingText(match[2] ?? '');
45
+ const current = normalizeSyntheticTitleToken(match[2] ?? '');
49
46
  if (current !== target)
50
47
  return markdown;
51
48
  lines.splice(index, 1);
@@ -62,15 +59,14 @@ export function maybeStripGithubPrimaryHeading(markdown, primaryHeading, url) {
62
59
  }
63
60
  return stripLeadingHeading(markdown, primaryHeading);
64
61
  }
65
- function buildSyntheticTitlePrefix(url, favicon, suppressFavicon) {
66
- if (!favicon || suppressFavicon)
67
- return ' ';
68
- const alt = parseUrlOrNull(url)?.hostname ?? '';
69
- return ` ![${alt}](${favicon}) `;
70
- }
71
62
  export function maybePrependSyntheticTitle(markdown, context, url) {
72
63
  if (!context.title || /^(#{1,6})\s/.test(markdown.trimStart())) {
73
64
  return markdown;
74
65
  }
75
- return `#${buildSyntheticTitlePrefix(url, context.favicon, context.suppressSyntheticFavicon)}${context.title}\n\n${markdown}`;
66
+ let prefix = ' ';
67
+ if (context.favicon && !context.suppressSyntheticFavicon) {
68
+ const alt = parseUrlOrNull(url)?.hostname ?? '';
69
+ prefix = ` ![${alt}](${context.favicon}) `;
70
+ }
71
+ return `#${prefix}${context.title}\n\n${markdown}`;
76
72
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@j0hanz/fetch-url-mcp",
3
- "version": "1.10.19",
3
+ "version": "1.10.21",
4
4
  "mcpName": "io.github.j0hanz/fetch-url-mcp",
5
5
  "description": "A web content fetcher MCP server that converts HTML to clean, AI and human readable markdown.",
6
6
  "type": "module",