@cyanheads/openalex-mcp-server 0.7.15 → 0.7.16

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.
Files changed (43) hide show
  1. package/AGENTS.md +2 -2
  2. package/CLAUDE.md +2 -2
  3. package/README.md +8 -6
  4. package/changelog/0.7.x/0.7.16.md +32 -0
  5. package/dist/mcp-server/tools/definitions/analyze-trends.tool.d.ts +3 -2
  6. package/dist/mcp-server/tools/definitions/analyze-trends.tool.d.ts.map +1 -1
  7. package/dist/mcp-server/tools/definitions/analyze-trends.tool.js +40 -14
  8. package/dist/mcp-server/tools/definitions/analyze-trends.tool.js.map +1 -1
  9. package/dist/mcp-server/tools/definitions/citation-graph.tool.d.ts.map +1 -1
  10. package/dist/mcp-server/tools/definitions/citation-graph.tool.js +1 -1
  11. package/dist/mcp-server/tools/definitions/citation-graph.tool.js.map +1 -1
  12. package/dist/mcp-server/tools/definitions/describe-fields.tool.d.ts.map +1 -1
  13. package/dist/mcp-server/tools/definitions/describe-fields.tool.js +82 -15
  14. package/dist/mcp-server/tools/definitions/describe-fields.tool.js.map +1 -1
  15. package/dist/mcp-server/tools/definitions/resolve-name.tool.d.ts +7 -0
  16. package/dist/mcp-server/tools/definitions/resolve-name.tool.d.ts.map +1 -1
  17. package/dist/mcp-server/tools/definitions/resolve-name.tool.js +17 -6
  18. package/dist/mcp-server/tools/definitions/resolve-name.tool.js.map +1 -1
  19. package/dist/mcp-server/tools/definitions/search-entities.tool.d.ts +10 -0
  20. package/dist/mcp-server/tools/definitions/search-entities.tool.d.ts.map +1 -1
  21. package/dist/mcp-server/tools/definitions/search-entities.tool.js +38 -10
  22. package/dist/mcp-server/tools/definitions/search-entities.tool.js.map +1 -1
  23. package/dist/mcp-server/tools/escape-markdown.d.ts +32 -0
  24. package/dist/mcp-server/tools/escape-markdown.d.ts.map +1 -0
  25. package/dist/mcp-server/tools/escape-markdown.js +389 -0
  26. package/dist/mcp-server/tools/escape-markdown.js.map +1 -0
  27. package/dist/mcp-server/tools/render-entity-record.d.ts +2 -1
  28. package/dist/mcp-server/tools/render-entity-record.d.ts.map +1 -1
  29. package/dist/mcp-server/tools/render-entity-record.js +24 -8
  30. package/dist/mcp-server/tools/render-entity-record.js.map +1 -1
  31. package/dist/services/openalex/html-entities.json +2237 -0
  32. package/dist/services/openalex/openalex-service.d.ts +1 -1
  33. package/dist/services/openalex/openalex-service.d.ts.map +1 -1
  34. package/dist/services/openalex/openalex-service.js +110 -76
  35. package/dist/services/openalex/openalex-service.js.map +1 -1
  36. package/dist/services/openalex/provider-text.d.ts +20 -0
  37. package/dist/services/openalex/provider-text.d.ts.map +1 -0
  38. package/dist/services/openalex/provider-text.js +276 -0
  39. package/dist/services/openalex/provider-text.js.map +1 -0
  40. package/dist/services/openalex/types.d.ts +5 -0
  41. package/dist/services/openalex/types.d.ts.map +1 -1
  42. package/package.json +4 -1
  43. package/server.json +3 -3
@@ -0,0 +1,276 @@
1
+ /**
2
+ * @fileoverview Normalization of provider text — the titles, abstracts, names, and affiliation
3
+ * strings OpenAlex passes through from its sources unsanitized. Decodes HTML character
4
+ * references against the full WHATWG table in one pass, then removes HTML comments and unwraps
5
+ * a small allowlist of HTML/JATS/MathML elements. Everything else stays literal, so text such as
6
+ * `A < B`, `Fish <Actinopterygii>`, or a SICI DOI survives untouched. Output is plain text;
7
+ * Markdown escaping for `content[]` happens at the render boundary, never here.
8
+ * @module services/openalex/provider-text
9
+ */
10
+ import entityTable from './html-entities.json' with { type: 'json' };
11
+ /**
12
+ * Named character references, keyed without the `&` and `;`. The data is the WHATWG table
13
+ * (https://html.spec.whatwg.org/entities.json), which the HTML standard freezes — it is never
14
+ * extended — so a vendored copy cannot drift. A `Map` rather than an object literal, because an
15
+ * object answers `constructor` and every other `Object.prototype` member as if it were a name.
16
+ */
17
+ const NAMED_REFERENCES = new Map(Object.entries(entityTable.named));
18
+ /** The 106 legacy names the standard also accepts without a trailing `;` (`&amp`, `&copy`, …). */
19
+ const LEGACY_REFERENCES = new Set(entityTable.legacy);
20
+ /**
21
+ * Common references OpenAlex also carries with their case changed, because a source upper- or
22
+ * title-cased the whole title entities and all (`BLEEDING IN&NBSP;CHILDREN`, `Nations:&Nbsp;`).
23
+ * A spelling the table does not hold decodes as the lowercase name; one it does hold keeps its
24
+ * own meaning, so `&Lt;` stays ≪.
25
+ */
26
+ const CASE_FOLDED_REFERENCES = new Set([
27
+ 'amp',
28
+ 'apos',
29
+ 'gt',
30
+ 'lt',
31
+ 'nbsp',
32
+ 'quot',
33
+ ]);
34
+ function namedReference(name) {
35
+ const exact = NAMED_REFERENCES.get(name);
36
+ if (exact !== undefined)
37
+ return exact;
38
+ const folded = name.toLowerCase();
39
+ return CASE_FOLDED_REFERENCES.has(folded) ? NAMED_REFERENCES.get(folded) : undefined;
40
+ }
41
+ const MAX_UNICODE_CODE_POINT = 0x10ffff;
42
+ /**
43
+ * One character reference: decimal, hex, or a maximal alphanumeric name, each with an optional
44
+ * `;`. The name run is maximal, so the character after an unterminated name is never an
45
+ * alphanumeric — the only lookahead the legacy rule still needs is `=`.
46
+ */
47
+ const CHARACTER_REFERENCE = /&(?:#(\d+)|#[xX]([0-9a-fA-F]+)|([A-Za-z][A-Za-z0-9]*))(;?)/g;
48
+ /** Whether `&name;` is a named character reference, for callers that must not let one decode. */
49
+ export function isNamedCharacterReference(name) {
50
+ return NAMED_REFERENCES.has(name);
51
+ }
52
+ function codePointToString(code, fallback) {
53
+ return Number.isInteger(code) && code >= 0 && code <= MAX_UNICODE_CODE_POINT
54
+ ? String.fromCodePoint(code)
55
+ : fallback;
56
+ }
57
+ /**
58
+ * Decode HTML character references in a single pass, so `&amp;lt;` becomes `&lt;` and never `<`.
59
+ *
60
+ * - Numeric references (`&#38;`, `&#x27E9;`) decode with or without the `;` — OpenAlex sends
61
+ * `&#38 Hepatology` — and a code point past U+10FFFF stays literal rather than corrupting text.
62
+ * - Named references are case-sensitive (`&Alpha;` is Α, `&alpha;` is α) and need the `;`,
63
+ * except for the legacy names, which follow the standard's attribute-value rule: decoded
64
+ * without the `;` unless an `=` follows, so a URL's `&copy=2` query parameter stays intact.
65
+ * A recased common reference with its `;` (`&NBSP;`, `&Quot;`) decodes as the lowercase name.
66
+ * - Anything else — `&constructor;`, `AT&T`, `&madeupentity;` — stays literal.
67
+ */
68
+ function decodeCharacterReferences(text) {
69
+ if (!text.includes('&'))
70
+ return text;
71
+ return text.replace(CHARACTER_REFERENCE, (match, decimal, hex, name, semicolon, offset) => {
72
+ if (decimal !== undefined)
73
+ return codePointToString(Number.parseInt(decimal, 10), match);
74
+ if (hex !== undefined)
75
+ return codePointToString(Number.parseInt(hex, 16), match);
76
+ const reference = name;
77
+ if (semicolon)
78
+ return namedReference(reference) ?? match;
79
+ const next = text[offset + match.length];
80
+ return LEGACY_REFERENCES.has(reference) && next !== '='
81
+ ? (NAMED_REFERENCES.get(reference) ?? match)
82
+ : match;
83
+ });
84
+ }
85
+ /** Inline elements whose tags are dropped and whose text is kept. */
86
+ const INLINE_ELEMENTS = new Set([
87
+ 'b',
88
+ 'em',
89
+ 'i',
90
+ 'inline-formula',
91
+ 'jats:bold',
92
+ 'jats:italic',
93
+ 'sc',
94
+ 'scp',
95
+ 'span',
96
+ 'strong',
97
+ 'tex-math',
98
+ // MathML presentation markup, bare; `mml:`-prefixed names are matched by prefix.
99
+ 'math',
100
+ 'menclose',
101
+ 'mfenced',
102
+ 'mfrac',
103
+ 'mi',
104
+ 'mmultiscripts',
105
+ 'mn',
106
+ 'mo',
107
+ 'mover',
108
+ 'mpadded',
109
+ 'mphantom',
110
+ 'mroot',
111
+ 'mrow',
112
+ 'mspace',
113
+ 'msqrt',
114
+ 'mstyle',
115
+ 'msub',
116
+ 'msubsup',
117
+ 'msup',
118
+ 'mtable',
119
+ 'mtd',
120
+ 'mtext',
121
+ 'mtr',
122
+ 'munder',
123
+ 'munderover',
124
+ 'semantics',
125
+ ]);
126
+ /** Block elements, each replaced by a paragraph break. */
127
+ const BLOCK_ELEMENTS = new Set([
128
+ 'br',
129
+ 'jats:p',
130
+ 'jats:sec',
131
+ 'jats:title',
132
+ 'li',
133
+ 'p',
134
+ ]);
135
+ const PARAGRAPH_BREAK = '\n\n';
136
+ /**
137
+ * Sub/superscripts open as TeX-style `_{`/`^{` and close with `}`. Unicode has no
138
+ * sub/superscript for most letters, unwrapping would read `10<sup>-3</sup>` as `10-3`, and
139
+ * OpenAlex's TeX-bearing abstracts already use this notation.
140
+ */
141
+ const SCRIPT_OPENERS = new Map([
142
+ ['sub', '_{'],
143
+ ['jats:sub', '_{'],
144
+ ['sup', '^{'],
145
+ ['jats:sup', '^{'],
146
+ ]);
147
+ /** A complete tag, checked against one `<…>` candidate that holds no other `<` or `>`. */
148
+ const TAG = /^<(\/?)([A-Za-z][\w:.-]*)(?:\s[^<>]*)?\/?>$/;
149
+ /**
150
+ * What an allowlisted tag becomes, or `undefined` for any other `<…>` span, which stays literal.
151
+ * Matching a whole tag name — never a prefix — is what keeps `i1<i2<` and `<Actinopterygii>`.
152
+ */
153
+ function replaceTag(candidate) {
154
+ const tag = TAG.exec(candidate);
155
+ if (!tag)
156
+ return;
157
+ const closing = tag[1] === '/';
158
+ const name = tag[2].toLowerCase();
159
+ if (INLINE_ELEMENTS.has(name) || name.startsWith('mml:'))
160
+ return '';
161
+ if (BLOCK_ELEMENTS.has(name))
162
+ return PARAGRAPH_BREAK;
163
+ const opener = SCRIPT_OPENERS.get(name);
164
+ if (opener === undefined)
165
+ return;
166
+ if (candidate.endsWith('/>'))
167
+ return '';
168
+ return closing ? '}' : opener;
169
+ }
170
+ const COMMENT_OPEN = '<!--';
171
+ /** `<!---->` — the shortest span that closes a comment it opened. */
172
+ const MIN_COMMENT_LENGTH = 7;
173
+ function endsWith(out, suffix) {
174
+ if (out.length < suffix.length)
175
+ return false;
176
+ for (let i = 0; i < suffix.length; i++) {
177
+ if (out[out.length - suffix.length + i] !== suffix[i])
178
+ return false;
179
+ }
180
+ return true;
181
+ }
182
+ /**
183
+ * Remove HTML comments and rewrite allowlisted tags in one left-to-right pass, reaching the
184
+ * result that repeating a regex replacement until nothing changes would reach — no allowlisted
185
+ * tag can re-form in the output — in linear time. A replace-until-stable loop costs one full
186
+ * pass per nesting level, which is quadratic on nested input such as `<<<i>i>i>`.
187
+ *
188
+ * Characters are copied to `out`. Removing a tag or comment can join the text on either side
189
+ * into a new one (`<<i>i>`, `<!-<!---->- x -->`), so every removal truncates `out` and scanning
190
+ * simply continues: `opens` holds the positions of each `<` that could still begin a tag, and
191
+ * `commentStart` the earliest unclosed `<!--`. A tag ends at the first `>` after its `<`, so a
192
+ * `>` settles the innermost open `<`: kept as text, it also blocks every `<` below it, except
193
+ * those before an unclosed comment, which come back into play if that comment closes. Each
194
+ * character is examined a bounded number of times.
195
+ */
196
+ function stripMarkup(text) {
197
+ if (!text.includes('<'))
198
+ return text;
199
+ const out = [];
200
+ const opens = [];
201
+ let commentStart = -1;
202
+ for (const ch of text) {
203
+ out.push(ch);
204
+ if (ch === '<') {
205
+ opens.push(out.length - 1);
206
+ continue;
207
+ }
208
+ if (ch === '-') {
209
+ if (commentStart < 0 && endsWith(out, COMMENT_OPEN))
210
+ commentStart = out.length - 4;
211
+ continue;
212
+ }
213
+ if (ch !== '>')
214
+ continue;
215
+ if (commentStart >= 0 &&
216
+ out.length - commentStart >= MIN_COMMENT_LENGTH &&
217
+ endsWith(out, '-->')) {
218
+ out.length = commentStart;
219
+ commentStart = -1;
220
+ while (opens.length > 0 && opens.at(-1) >= out.length)
221
+ opens.pop();
222
+ continue;
223
+ }
224
+ // Inside an unclosed comment, only a `<` opened after it can form a tag.
225
+ const top = opens.at(-1);
226
+ if (top === undefined || top <= commentStart)
227
+ continue;
228
+ opens.pop();
229
+ const replacement = replaceTag(out.slice(top).join(''));
230
+ if (replacement === undefined) {
231
+ while (opens.length > 0 && opens.at(-1) > commentStart)
232
+ opens.pop();
233
+ continue;
234
+ }
235
+ out.length = top;
236
+ if (replacement)
237
+ out.push(replacement);
238
+ }
239
+ // A break element can only come from a block tag (input is copied one character at a time),
240
+ // and one inside a comment was truncated with it.
241
+ const stripped = out.join('');
242
+ if (!out.includes(PARAGRAPH_BREAK))
243
+ return stripped;
244
+ // Block tags leave runs of breaks and stray edge whitespace; keep one break between paragraphs.
245
+ return stripped
246
+ .split(PARAGRAPH_BREAK)
247
+ .map((paragraph) => paragraph.trim())
248
+ .filter(Boolean)
249
+ .join(PARAGRAPH_BREAK);
250
+ }
251
+ /**
252
+ * Normalize one provider string to plain text: decode character references once, then handle
253
+ * markup. Decoding first is what lets an encoded tag (`&lt;i&gt;x&lt;/i&gt;`) unwrap; decoding
254
+ * never runs again afterward, so double-encoded text keeps its remaining level.
255
+ */
256
+ export function normalizeProviderText(text) {
257
+ return stripMarkup(decodeCharacterReferences(text));
258
+ }
259
+ function isRecord(value) {
260
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
261
+ }
262
+ /** Normalize every string leaf of a JSON value; keys, numbers, booleans, and nulls pass through. */
263
+ export function normalizeProviderValue(value) {
264
+ if (typeof value === 'string')
265
+ return normalizeProviderText(value);
266
+ if (Array.isArray(value))
267
+ return value.map(normalizeProviderValue);
268
+ if (isRecord(value)) {
269
+ const next = {};
270
+ for (const [k, v] of Object.entries(value))
271
+ next[k] = normalizeProviderValue(v);
272
+ return next;
273
+ }
274
+ return value;
275
+ }
276
+ //# sourceMappingURL=provider-text.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider-text.js","sourceRoot":"","sources":["../../../src/services/openalex/provider-text.ts"],"names":[],"mappings":"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"}
@@ -83,6 +83,11 @@ export interface EntityRecord {
83
83
  }
84
84
  export interface GroupRecord {
85
85
  count: number;
86
+ /**
87
+ * Set only on the missing-value bucket `include_unknown` adds, whose key is an OpenAlex
88
+ * sentinel (`-111`, `unknown`, `…/unknown`) rather than a measured value.
89
+ */
90
+ is_unknown?: true;
86
91
  key: string;
87
92
  key_display_name: string;
88
93
  }
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/services/openalex/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/services/openalex/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cyanheads/openalex-mcp-server",
3
- "version": "0.7.15",
3
+ "version": "0.7.16",
4
4
  "description": "Access the OpenAlex academic research catalog - 270M+ publications through MCP. STDIO & Streamable HTTP.",
5
5
  "mcpName": "io.github.cyanheads/openalex-mcp-server",
6
6
  "type": "module",
@@ -99,6 +99,9 @@
99
99
  "@types/node": "^26.6.2",
100
100
  "depcheck": "^1.4.7",
101
101
  "ignore": "^7.0.9",
102
+ "mdast-util-from-markdown": "^2.0.3",
103
+ "mdast-util-gfm": "^3.1.0",
104
+ "micromark-extension-gfm": "^3.0.0",
102
105
  "tsc-alias": "^1.9.5",
103
106
  "typescript": "^7.0.2",
104
107
  "vitest": "^5.0.1"
package/server.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "url": "https://github.com/cyanheads/openalex-mcp-server",
7
7
  "source": "github"
8
8
  },
9
- "version": "0.7.15",
9
+ "version": "0.7.16",
10
10
  "remotes": [
11
11
  {
12
12
  "type": "streamable-http",
@@ -19,7 +19,7 @@
19
19
  "registryBaseUrl": "https://registry.npmjs.org",
20
20
  "identifier": "@cyanheads/openalex-mcp-server",
21
21
  "runtimeHint": "node",
22
- "version": "0.7.15",
22
+ "version": "0.7.16",
23
23
  "packageArguments": [
24
24
  {
25
25
  "type": "positional",
@@ -67,7 +67,7 @@
67
67
  "registryBaseUrl": "https://registry.npmjs.org",
68
68
  "identifier": "@cyanheads/openalex-mcp-server",
69
69
  "runtimeHint": "node",
70
- "version": "0.7.15",
70
+ "version": "0.7.16",
71
71
  "packageArguments": [
72
72
  {
73
73
  "type": "positional",