@cyanheads/zenodo-mcp-server 0.1.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.
Files changed (97) hide show
  1. package/AGENTS.md +435 -0
  2. package/CLAUDE.md +435 -0
  3. package/Dockerfile +140 -0
  4. package/LICENSE +201 -0
  5. package/README.md +292 -0
  6. package/changelog/0.1.x/0.1.1.md +21 -0
  7. package/changelog/template.md +151 -0
  8. package/dist/config/server-config.d.ts +13 -0
  9. package/dist/config/server-config.d.ts.map +1 -0
  10. package/dist/config/server-config.js +19 -0
  11. package/dist/config/server-config.js.map +1 -0
  12. package/dist/index.d.ts +7 -0
  13. package/dist/index.d.ts.map +1 -0
  14. package/dist/index.js +21 -0
  15. package/dist/index.js.map +1 -0
  16. package/dist/mcp-server/tools/definitions/get-record.tool.d.ts +186 -0
  17. package/dist/mcp-server/tools/definitions/get-record.tool.d.ts.map +1 -0
  18. package/dist/mcp-server/tools/definitions/get-record.tool.js +568 -0
  19. package/dist/mcp-server/tools/definitions/get-record.tool.js.map +1 -0
  20. package/dist/mcp-server/tools/definitions/index.d.ts +721 -0
  21. package/dist/mcp-server/tools/definitions/index.d.ts.map +1 -0
  22. package/dist/mcp-server/tools/definitions/index.js +21 -0
  23. package/dist/mcp-server/tools/definitions/index.js.map +1 -0
  24. package/dist/mcp-server/tools/definitions/list-files.tool.d.ts +108 -0
  25. package/dist/mcp-server/tools/definitions/list-files.tool.d.ts.map +1 -0
  26. package/dist/mcp-server/tools/definitions/list-files.tool.js +348 -0
  27. package/dist/mcp-server/tools/definitions/list-files.tool.js.map +1 -0
  28. package/dist/mcp-server/tools/definitions/list-versions.tool.d.ts +92 -0
  29. package/dist/mcp-server/tools/definitions/list-versions.tool.d.ts.map +1 -0
  30. package/dist/mcp-server/tools/definitions/list-versions.tool.js +268 -0
  31. package/dist/mcp-server/tools/definitions/list-versions.tool.js.map +1 -0
  32. package/dist/mcp-server/tools/definitions/lookup-vocabulary.tool.d.ts +99 -0
  33. package/dist/mcp-server/tools/definitions/lookup-vocabulary.tool.d.ts.map +1 -0
  34. package/dist/mcp-server/tools/definitions/lookup-vocabulary.tool.js +324 -0
  35. package/dist/mcp-server/tools/definitions/lookup-vocabulary.tool.js.map +1 -0
  36. package/dist/mcp-server/tools/definitions/read-file.tool.d.ts +104 -0
  37. package/dist/mcp-server/tools/definitions/read-file.tool.d.ts.map +1 -0
  38. package/dist/mcp-server/tools/definitions/read-file.tool.js +370 -0
  39. package/dist/mcp-server/tools/definitions/read-file.tool.js.map +1 -0
  40. package/dist/mcp-server/tools/definitions/search-records.tool.d.ts +181 -0
  41. package/dist/mcp-server/tools/definitions/search-records.tool.d.ts.map +1 -0
  42. package/dist/mcp-server/tools/definitions/search-records.tool.js +531 -0
  43. package/dist/mcp-server/tools/definitions/search-records.tool.js.map +1 -0
  44. package/dist/mcp-server/tools/record-miss.d.ts +37 -0
  45. package/dist/mcp-server/tools/record-miss.d.ts.map +1 -0
  46. package/dist/mcp-server/tools/record-miss.js +51 -0
  47. package/dist/mcp-server/tools/record-miss.js.map +1 -0
  48. package/dist/mcp-server/tools/render.d.ts +31 -0
  49. package/dist/mcp-server/tools/render.d.ts.map +1 -0
  50. package/dist/mcp-server/tools/render.js +53 -0
  51. package/dist/mcp-server/tools/render.js.map +1 -0
  52. package/dist/mcp-server/tools/schema-helpers.d.ts +20 -0
  53. package/dist/mcp-server/tools/schema-helpers.d.ts.map +1 -0
  54. package/dist/mcp-server/tools/schema-helpers.js +44 -0
  55. package/dist/mcp-server/tools/schema-helpers.js.map +1 -0
  56. package/dist/services/zenodo/cache.d.ts +25 -0
  57. package/dist/services/zenodo/cache.d.ts.map +1 -0
  58. package/dist/services/zenodo/cache.js +58 -0
  59. package/dist/services/zenodo/cache.js.map +1 -0
  60. package/dist/services/zenodo/html-to-text.d.ts +23 -0
  61. package/dist/services/zenodo/html-to-text.d.ts.map +1 -0
  62. package/dist/services/zenodo/html-to-text.js +148 -0
  63. package/dist/services/zenodo/html-to-text.js.map +1 -0
  64. package/dist/services/zenodo/http.d.ts +73 -0
  65. package/dist/services/zenodo/http.d.ts.map +1 -0
  66. package/dist/services/zenodo/http.js +378 -0
  67. package/dist/services/zenodo/http.js.map +1 -0
  68. package/dist/services/zenodo/identifiers.d.ts +104 -0
  69. package/dist/services/zenodo/identifiers.d.ts.map +1 -0
  70. package/dist/services/zenodo/identifiers.js +261 -0
  71. package/dist/services/zenodo/identifiers.js.map +1 -0
  72. package/dist/services/zenodo/normalize.d.ts +29 -0
  73. package/dist/services/zenodo/normalize.d.ts.map +1 -0
  74. package/dist/services/zenodo/normalize.js +324 -0
  75. package/dist/services/zenodo/normalize.js.map +1 -0
  76. package/dist/services/zenodo/query-builder.d.ts +87 -0
  77. package/dist/services/zenodo/query-builder.d.ts.map +1 -0
  78. package/dist/services/zenodo/query-builder.js +221 -0
  79. package/dist/services/zenodo/query-builder.js.map +1 -0
  80. package/dist/services/zenodo/resource-types.d.ts +34 -0
  81. package/dist/services/zenodo/resource-types.d.ts.map +1 -0
  82. package/dist/services/zenodo/resource-types.js +97 -0
  83. package/dist/services/zenodo/resource-types.js.map +1 -0
  84. package/dist/services/zenodo/text-preview.d.ts +56 -0
  85. package/dist/services/zenodo/text-preview.d.ts.map +1 -0
  86. package/dist/services/zenodo/text-preview.js +151 -0
  87. package/dist/services/zenodo/text-preview.js.map +1 -0
  88. package/dist/services/zenodo/types.d.ts +517 -0
  89. package/dist/services/zenodo/types.d.ts.map +1 -0
  90. package/dist/services/zenodo/types.js +8 -0
  91. package/dist/services/zenodo/types.js.map +1 -0
  92. package/dist/services/zenodo/zenodo-service.d.ts +98 -0
  93. package/dist/services/zenodo/zenodo-service.d.ts.map +1 -0
  94. package/dist/services/zenodo/zenodo-service.js +392 -0
  95. package/dist/services/zenodo/zenodo-service.js.map +1 -0
  96. package/package.json +110 -0
  97. package/server.json +113 -0
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @fileoverview Raw Zenodo (InvenioRDM) response shapes and the normalized domain
3
+ * types the tools consume. Raw fields are optional unless verified always present:
4
+ * upstream omits fields rather than sending null, and old records are sparse.
5
+ * @module services/zenodo/types
6
+ */
7
+ export {};
8
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/services/zenodo/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
@@ -0,0 +1,98 @@
1
+ /**
2
+ * @fileoverview Zenodo REST API service: records, DOI resolution, versions,
3
+ * citations, file content and ZIP listings, and the reference vocabularies. Every
4
+ * call goes through the paced, retried fetch boundary in `http.ts`; results are
5
+ * cached process-wide (Zenodo data is public).
6
+ * @module services/zenodo/zenodo-service
7
+ */
8
+ import type { Context } from '@cyanheads/mcp-ts-core';
9
+ import type { AppConfig } from '@cyanheads/mcp-ts-core/config';
10
+ import { type FunderRef } from './identifiers.js';
11
+ import { type SearchFilters } from './query-builder.js';
12
+ import type { CitationStyle, CommunityEntry, ContainerLookup, ContentRead, FunderEntry, RecordLookup, RemoteVocabulary, SearchPage, VersionsLookup, VocabularyPage, ZenodoRecord } from './types.js';
13
+ /** A search page plus the exact `q` that was sent. */
14
+ export type SearchResult = SearchPage & {
15
+ q?: string;
16
+ };
17
+ /** Outcome of resolving a non-Zenodo DOI through search. */
18
+ export type DoiResolution = {
19
+ record: ZenodoRecord;
20
+ status: 'found';
21
+ } | {
22
+ status: 'not_on_zenodo';
23
+ };
24
+ /** Zenodo REST API client with process-local caching. */
25
+ export declare class ZenodoService {
26
+ #private;
27
+ constructor(options: {
28
+ accessToken?: string | undefined;
29
+ userAgent: string;
30
+ });
31
+ /** Releases the pacers' timers and queued waiters. */
32
+ dispose(): void;
33
+ /** Record search (search bucket). Only allowlisted params reach the URL. */
34
+ searchRecords(filters: SearchFilters, ctx: Context): Promise<SearchResult>;
35
+ /**
36
+ * Record GET (follows a concept id's 302 to the latest version). A 404, 403, or
37
+ * 410 is a result, not an error.
38
+ */
39
+ getRecord(recid: string, ctx: Context): Promise<RecordLookup>;
40
+ /**
41
+ * Resolves a DOI through record search with `all_versions=true` (a superseded
42
+ * version's DOI misses without it). When the as-given DOI misses and a form with
43
+ * trailing punctuation stripped exists, that form is tried once. A hit is cached
44
+ * as `doi/{doi}` → recid next to the record, so the drill-down tools reuse it
45
+ * instead of spending the search budget again.
46
+ */
47
+ resolveDoi(doi: string, strippedDoi: string | undefined, ctx: Context): Promise<DoiResolution>;
48
+ /**
49
+ * Latest version's recid for any recid in a series (`/versions/latest`, Location
50
+ * only). Its failures stay untyped: the record itself already resolved, so
51
+ * `record_unavailable` would misdescribe them.
52
+ */
53
+ latestRecid(recid: string, ctx: Context): Promise<string | undefined>;
54
+ /**
55
+ * One page of a version series, newest first. A 404 (a concept id), 403, or 410
56
+ * means the id names no listable series; the caller classifies it with the record GET.
57
+ */
58
+ listVersions(recid: string, page: number, size: number, ctx: Context): Promise<VersionsLookup>;
59
+ /**
60
+ * A formatted citation for a version recid. Always call with the resolved
61
+ * version: a concept recid's redirect drops `?style=`.
62
+ */
63
+ getCitation(recid: string, style: CitationStyle, ctx: Context): Promise<string>;
64
+ /**
65
+ * Member listing of a ZIP file (`/container`). Call only for `.zip` keys — other
66
+ * archives answer 500, and so do some ZIPs, which surfaces as `archive_unavailable`.
67
+ */
68
+ getContainer(recid: string, key: string, ctx: Context): Promise<ContainerLookup>;
69
+ /** Byte-range read of a top-level file. A 200 (range ignored) is streamed and cut at the cap. */
70
+ readContent(recid: string, key: string, offsetBytes: number, maxBytes: number, ctx: Context): Promise<ContentRead>;
71
+ /** Reads the head of a ZIP member. The member endpoint ignores Range, so the stream is cancelled at the cap. */
72
+ readMember(recid: string, key: string, member: string, maxBytes: number, ctx: Context): Promise<ContentRead>;
73
+ /**
74
+ * Resolves a community slug or UUID to its entry. Slugs are case-sensitive, so a
75
+ * 404 is retried once lowercased when that differs. Misses are cached.
76
+ */
77
+ getCommunity(slugOrId: string, ctx: Context): Promise<CommunityEntry | undefined>;
78
+ /** Validates a ROR id against Zenodo's funder vocabulary. Misses are cached. */
79
+ getFunder(ror: string, ctx: Context): Promise<FunderEntry | undefined>;
80
+ /** Finds the funder carrying a Crossref Funder DOI (one-to-one). Misses are cached. */
81
+ findFunderByDoi(doi: string, ctx: Context): Promise<FunderEntry | undefined>;
82
+ /** Resolves a parsed funder reference (ROR id or Funder DOI) to a funder entry. */
83
+ resolveFunder(ref: FunderRef, ctx: Context): Promise<FunderEntry | undefined>;
84
+ /** One page of a remote vocabulary. `funderRor` scopes awards to one funder. */
85
+ searchVocabulary(vocabulary: RemoteVocabulary, params: {
86
+ funderRor?: string | undefined;
87
+ page: number;
88
+ query?: string | undefined;
89
+ size: number;
90
+ }, ctx: Context): Promise<VocabularyPage>;
91
+ }
92
+ /** Creates the service. Called once from `createApp({ setup })`. */
93
+ export declare function initZenodoService(config: AppConfig): void;
94
+ /** The initialized service. Throws when `initZenodoService()` has not run. */
95
+ export declare function getZenodoService(): ZenodoService;
96
+ /** Disposes the service's pacers. Called from `createApp({ teardown })`. */
97
+ export declare function disposeZenodoService(): void;
98
+ //# sourceMappingURL=zenodo-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zenodo-service.d.ts","sourceRoot":"","sources":["../../../src/services/zenodo/zenodo-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,392 @@
1
+ /**
2
+ * @fileoverview Zenodo REST API service: records, DOI resolution, versions,
3
+ * citations, file content and ZIP listings, and the reference vocabularies. Every
4
+ * call goes through the paced, retried fetch boundary in `http.ts`; results are
5
+ * cached process-wide (Zenodo data is public).
6
+ * @module services/zenodo/zenodo-service
7
+ */
8
+ import { validationError } from '@cyanheads/mcp-ts-core/errors';
9
+ import { getServerConfig } from '../../config/server-config.js';
10
+ import { TtlLruCache } from './cache.js';
11
+ import { CONTENT_ATTEMPT_MS, discardBody, JSON_ATTEMPT_MS, readCapped, readJson, readText, ZenodoHttp, } from './http.js';
12
+ import { downloadUrl, encodeKeySegments, hasDotSegment } from './identifiers.js';
13
+ import { normalizeAward, normalizeCommunity, normalizeContainer, normalizeFacets, normalizeFunder, normalizeLicense, normalizeRecord, normalizeSearchHit, normalizeTombstone, normalizeVersionHit, opt, } from './normalize.js';
14
+ import { buildSearch, escapeVocabularyQuery, quoteValue, } from './query-builder.js';
15
+ const RDM = 'application/vnd.inveniordm.v1+json';
16
+ const JSON_ACCEPT = 'application/json';
17
+ const MINUTE = 60_000;
18
+ const HOUR = 60 * MINUTE;
19
+ const CITATION_ACCEPT = {
20
+ bibtex: 'application/x-bibtex',
21
+ 'csl-json': 'application/vnd.citationstyles.csl+json',
22
+ apa: 'text/x-bibliography',
23
+ 'chicago-author-date': 'text/x-bibliography',
24
+ 'harvard-cite-them-right': 'text/x-bibliography',
25
+ ieee: 'text/x-bibliography',
26
+ 'modern-language-association': 'text/x-bibliography',
27
+ nature: 'text/x-bibliography',
28
+ };
29
+ const VOCABULARY_PATHS = {
30
+ communities: '/communities',
31
+ funders: '/funders',
32
+ awards: '/awards',
33
+ licenses: '/vocabularies/licenses',
34
+ };
35
+ /**
36
+ * Request path of a file endpoint: `/records/{recid}/files/{key}` plus any further
37
+ * segments, each key or member path percent-encoded. A `.` or `..` segment is
38
+ * refused, since the URL parser would resolve it to another zenodo.org endpoint.
39
+ */
40
+ function filePath(recid, key, ...rest) {
41
+ for (const part of [key, ...rest]) {
42
+ if (hasDotSegment(part)) {
43
+ throw validationError('A file key or archive member path with a "." or ".." segment names no file on Zenodo.');
44
+ }
45
+ }
46
+ return `/records/${recid}/files/${[key, ...rest].map(encodeKeySegments).join('/')}`;
47
+ }
48
+ /** Zenodo REST API client with process-local caching. */
49
+ export class ZenodoService {
50
+ #cache = new TtlLruCache({ maxBytes: 64 * 1024 * 1024, maxEntryBytes: 4 * 1024 * 1024 });
51
+ #http;
52
+ constructor(options) {
53
+ this.#http = new ZenodoHttp(options);
54
+ }
55
+ /** Releases the pacers' timers and queued waiters. */
56
+ dispose() {
57
+ this.#http.dispose();
58
+ this.#cache.clear();
59
+ }
60
+ /** Record search (search bucket). Only allowlisted params reach the URL. */
61
+ async searchRecords(filters, ctx) {
62
+ const built = buildSearch(filters);
63
+ const cacheKey = `search/${JSON.stringify(built.params)}`;
64
+ const cached = this.#cache.get(cacheKey);
65
+ if (cached)
66
+ return cached;
67
+ const raw = await this.#http.request(this.#jsonRequest('/records', RDM, 'search', 'search', [200], 'searchRecords', built.params), (res) => readJson(res), ctx);
68
+ const result = {
69
+ total: raw.hits?.total ?? 0,
70
+ hits: (raw.hits?.hits ?? []).map(normalizeSearchHit),
71
+ facets: normalizeFacets(raw.aggregations),
72
+ ...(built.q ? { q: built.q } : {}),
73
+ };
74
+ this.#cache.set(cacheKey, result, MINUTE);
75
+ return result;
76
+ }
77
+ /**
78
+ * Record GET (follows a concept id's 302 to the latest version). A 404, 403, or
79
+ * 410 is a result, not an error.
80
+ */
81
+ async getRecord(recid, ctx) {
82
+ const cached = this.#cachedRecord(recid);
83
+ if (cached)
84
+ return { status: 'found', record: cached };
85
+ const lookup = await this.#http.request(this.#jsonRequest(`/records/${recid}`, RDM, 'general', 'record', [200, 403, 404, 410], 'getRecord'), async (res) => {
86
+ if (res.status === 200) {
87
+ return { status: 'found', record: normalizeRecord(await readJson(res)) };
88
+ }
89
+ if (res.status === 410) {
90
+ const body = await readJson(res).catch(() => ({}));
91
+ return { status: 'deleted', tombstone: normalizeTombstone(body.tombstone) };
92
+ }
93
+ await discardBody(res);
94
+ return { status: res.status === 403 ? 'restricted' : 'not_found' };
95
+ }, ctx);
96
+ if (lookup.status === 'found')
97
+ this.#storeRecord(lookup.record, recid);
98
+ return lookup;
99
+ }
100
+ /**
101
+ * Resolves a DOI through record search with `all_versions=true` (a superseded
102
+ * version's DOI misses without it). When the as-given DOI misses and a form with
103
+ * trailing punctuation stripped exists, that form is tried once. A hit is cached
104
+ * as `doi/{doi}` → recid next to the record, so the drill-down tools reuse it
105
+ * instead of spending the search budget again.
106
+ */
107
+ async resolveDoi(doi, strippedDoi, ctx) {
108
+ for (const candidate of strippedDoi ? [doi, strippedDoi] : [doi]) {
109
+ const cachedRecid = this.#cache.get(`doi/${candidate.toLowerCase()}`);
110
+ const cached = cachedRecid && this.#cache.get(`record/${cachedRecid}`);
111
+ if (cached)
112
+ return { status: 'found', record: cached };
113
+ const raw = await this.#http.request(this.#jsonRequest('/records', RDM, 'search', 'doi_lookup', [200], 'resolveDoi', [
114
+ ['q', `doi:${quoteValue(candidate)}`],
115
+ ['all_versions', 'true'],
116
+ ['size', '2'],
117
+ ]), (res) => readJson(res), ctx);
118
+ const hits = raw.hits?.hits ?? [];
119
+ const wanted = candidate.toLowerCase();
120
+ const hit = hits.find((h) => h.pids?.doi?.identifier?.toLowerCase() === wanted) ?? hits[0];
121
+ if (hit) {
122
+ const record = normalizeRecord(hit);
123
+ this.#storeRecord(record, record.recid);
124
+ this.#cache.set(`doi/${candidate.toLowerCase()}`, record.recid, 5 * MINUTE);
125
+ return { status: 'found', record };
126
+ }
127
+ }
128
+ return { status: 'not_on_zenodo' };
129
+ }
130
+ /**
131
+ * Latest version's recid for any recid in a series (`/versions/latest`, Location
132
+ * only). Its failures stay untyped: the record itself already resolved, so
133
+ * `record_unavailable` would misdescribe them.
134
+ */
135
+ async latestRecid(recid, ctx) {
136
+ const cacheKey = `concept/${recid}`;
137
+ const cached = this.#cache.get(cacheKey);
138
+ if (cached)
139
+ return cached;
140
+ const latest = await this.#http.request({
141
+ ...this.#jsonRequest(`/records/${recid}/versions/latest`, RDM, 'general', 'other', [301, 302, 404], 'latestRecid'),
142
+ redirect: 'manual',
143
+ }, async (res) => {
144
+ await discardBody(res);
145
+ if (res.status === 404)
146
+ return;
147
+ return /\/records\/(\d+)/.exec(res.headers.get('location') ?? '')?.[1];
148
+ }, ctx);
149
+ if (latest)
150
+ this.#cache.set(cacheKey, latest, 5 * MINUTE);
151
+ return latest;
152
+ }
153
+ /**
154
+ * One page of a version series, newest first. A 404 (a concept id), 403, or 410
155
+ * means the id names no listable series; the caller classifies it with the record GET.
156
+ */
157
+ listVersions(recid, page, size, ctx) {
158
+ return this.#http.request(this.#jsonRequest(`/records/${recid}/versions`, RDM, 'general', 'versions', [200, 403, 404, 410], 'listVersions', [
159
+ ['page', String(page)],
160
+ ['size', String(size)],
161
+ ['sort', 'version'],
162
+ ]), async (res) => {
163
+ if (res.status !== 200) {
164
+ await discardBody(res);
165
+ return { status: 'not_found' };
166
+ }
167
+ const raw = await readJson(res);
168
+ const hits = raw.hits?.hits ?? [];
169
+ const parent = hits[0]?.parent;
170
+ return {
171
+ status: 'ok',
172
+ total: raw.hits?.total ?? 0,
173
+ hits: hits.map(normalizeVersionHit),
174
+ ...opt('concept_recid', parent?.id),
175
+ ...opt('concept_doi', parent?.pids?.doi?.identifier),
176
+ };
177
+ }, ctx);
178
+ }
179
+ /**
180
+ * A formatted citation for a version recid. Always call with the resolved
181
+ * version: a concept recid's redirect drops `?style=`.
182
+ */
183
+ getCitation(recid, style, ctx) {
184
+ const accept = CITATION_ACCEPT[style];
185
+ const query = accept === 'text/x-bibliography' ? [['style', style]] : [];
186
+ return this.#http.request(this.#jsonRequest(`/records/${recid}`, accept, 'general', 'other', [200], 'getCitation', query), async (res) => (await readText(res)).trim(), ctx);
187
+ }
188
+ /**
189
+ * Member listing of a ZIP file (`/container`). Call only for `.zip` keys — other
190
+ * archives answer 500, and so do some ZIPs, which surfaces as `archive_unavailable`.
191
+ */
192
+ async getContainer(recid, key, ctx) {
193
+ const cacheKey = `container/${recid}/${key}`;
194
+ const cached = this.#cache.get(cacheKey);
195
+ if (cached)
196
+ return cached;
197
+ const lookup = await this.#http.request({
198
+ ...this.#jsonRequest(filePath(recid, key, 'container'), JSON_ACCEPT, 'general', 'archive', [200, 404], 'getContainer'),
199
+ downloadUrl: downloadUrl(recid, key),
200
+ }, async (res) => {
201
+ if (res.status === 404) {
202
+ await discardBody(res);
203
+ return { status: 'not_found' };
204
+ }
205
+ return { status: 'ok', listing: normalizeContainer(await readJson(res)) };
206
+ }, ctx);
207
+ if (lookup.status === 'ok')
208
+ this.#cache.set(cacheKey, lookup, 10 * MINUTE);
209
+ return lookup;
210
+ }
211
+ /** Byte-range read of a top-level file. A 200 (range ignored) is streamed and cut at the cap. */
212
+ readContent(recid, key, offsetBytes, maxBytes, ctx) {
213
+ return this.#http.request({
214
+ ...this.#jsonRequest(filePath(recid, key, 'content'), JSON_ACCEPT, 'general', 'other', [200, 206, 403, 404, 416], 'readContent'),
215
+ attemptMs: CONTENT_ATTEMPT_MS,
216
+ range: `bytes=${offsetBytes}-${offsetBytes + maxBytes - 1}`,
217
+ }, async (res) => {
218
+ const miss = await contentMiss(res);
219
+ if (miss)
220
+ return miss;
221
+ if (res.status === 206) {
222
+ const total = Number(/\/(\d+)\s*$/.exec(res.headers.get('content-range') ?? '')?.[1]);
223
+ const { bytes, more } = await readCapped(res, maxBytes);
224
+ const fileSize = Number.isFinite(total) ? total : undefined;
225
+ return {
226
+ status: 'ok',
227
+ bytes,
228
+ moreRemains: fileSize !== undefined ? offsetBytes + bytes.length < fileSize : more,
229
+ ...(fileSize !== undefined ? { fileSize } : {}),
230
+ };
231
+ }
232
+ const { bytes, more } = await readCapped(res, maxBytes, offsetBytes);
233
+ return { status: 'ok', bytes, moreRemains: more };
234
+ }, ctx);
235
+ }
236
+ /** Reads the head of a ZIP member. The member endpoint ignores Range, so the stream is cancelled at the cap. */
237
+ readMember(recid, key, member, maxBytes, ctx) {
238
+ return this.#http.request({
239
+ ...this.#jsonRequest(filePath(recid, key, 'container', member), JSON_ACCEPT, 'general', 'archive', [200, 403, 404], 'readMember'),
240
+ attemptMs: CONTENT_ATTEMPT_MS,
241
+ downloadUrl: downloadUrl(recid, key),
242
+ }, async (res) => {
243
+ const miss = await contentMiss(res);
244
+ if (miss)
245
+ return miss;
246
+ const { bytes, more } = await readCapped(res, maxBytes);
247
+ return { status: 'ok', bytes, moreRemains: more };
248
+ }, ctx);
249
+ }
250
+ /**
251
+ * Resolves a community slug or UUID to its entry. Slugs are case-sensitive, so a
252
+ * 404 is retried once lowercased when that differs. Misses are cached.
253
+ */
254
+ async getCommunity(slugOrId, ctx) {
255
+ const found = await this.#fetchCommunity(slugOrId, ctx);
256
+ if (found)
257
+ return found;
258
+ const lower = slugOrId.toLowerCase();
259
+ return lower === slugOrId ? undefined : this.#fetchCommunity(lower, ctx);
260
+ }
261
+ /** Validates a ROR id against Zenodo's funder vocabulary. Misses are cached. */
262
+ getFunder(ror, ctx) {
263
+ return this.#cachedLookup(`funder/${ror}`, () => this.#http.request(this.#jsonRequest(`/funders/${encodeURIComponent(ror)}`, JSON_ACCEPT, 'general', 'other', [200, 404], 'getFunder'), async (res) => {
264
+ if (res.status === 404) {
265
+ await discardBody(res);
266
+ return;
267
+ }
268
+ return normalizeFunder(await readJson(res));
269
+ }, ctx));
270
+ }
271
+ /** Finds the funder carrying a Crossref Funder DOI (one-to-one). Misses are cached. */
272
+ findFunderByDoi(doi, ctx) {
273
+ return this.#cachedLookup(`funderdoi/${doi}`, () => this.#http.request(this.#jsonRequest('/funders', JSON_ACCEPT, 'general', 'other', [200], 'findFunderByDoi', [
274
+ ['q', `identifiers.identifier:${quoteValue(doi)}`],
275
+ ['size', '1'],
276
+ ]), async (res) => {
277
+ const hit = (await readJson(res)).hits?.hits?.[0];
278
+ return hit ? normalizeFunder(hit) : undefined;
279
+ }, ctx));
280
+ }
281
+ /** Resolves a parsed funder reference (ROR id or Funder DOI) to a funder entry. */
282
+ resolveFunder(ref, ctx) {
283
+ return ref.kind === 'ror' ? this.getFunder(ref.ror, ctx) : this.findFunderByDoi(ref.doi, ctx);
284
+ }
285
+ /** One page of a remote vocabulary. `funderRor` scopes awards to one funder. */
286
+ async searchVocabulary(vocabulary, params, ctx) {
287
+ const query = [];
288
+ if (params.query)
289
+ query.push(['q', escapeVocabularyQuery(params.query)]);
290
+ if (vocabulary === 'awards' && params.funderRor)
291
+ query.push(['funders', params.funderRor]);
292
+ query.push(['page', String(params.page)], ['size', String(params.size)]);
293
+ const cacheKey = `vocab/${vocabulary}/${JSON.stringify(query)}`;
294
+ const cached = this.#cache.get(cacheKey);
295
+ if (cached)
296
+ return cached;
297
+ const raw = await this.#http.request(this.#jsonRequest(VOCABULARY_PATHS[vocabulary], JSON_ACCEPT, 'general', 'other', [200], 'searchVocabulary', query), (res) => readJson(res), ctx);
298
+ const total = raw.hits?.total ?? 0;
299
+ const hits = raw.hits?.hits ?? [];
300
+ const page = vocabulary === 'communities'
301
+ ? { vocabulary, total, entries: hits.map(normalizeCommunity) }
302
+ : vocabulary === 'funders'
303
+ ? { vocabulary, total, entries: hits.map(normalizeFunder) }
304
+ : vocabulary === 'awards'
305
+ ? { vocabulary, total, entries: hits.map(normalizeAward) }
306
+ : { vocabulary, total, entries: hits.map(normalizeLicense) };
307
+ this.#cache.set(cacheKey, page, HOUR);
308
+ return page;
309
+ }
310
+ #jsonRequest(path, accept, bucket, endpoint, okStatuses, operation, query) {
311
+ return {
312
+ path,
313
+ accept,
314
+ bucket,
315
+ endpoint,
316
+ okStatuses,
317
+ operation,
318
+ attemptMs: JSON_ATTEMPT_MS,
319
+ ...(query ? { query } : {}),
320
+ };
321
+ }
322
+ /** A cached record for `recid` — directly, or via a cached concept → latest mapping. */
323
+ #cachedRecord(recid) {
324
+ const direct = this.#cache.get(`record/${recid}`);
325
+ if (direct)
326
+ return direct;
327
+ const latest = this.#cache.get(`concept/${recid}`);
328
+ if (!latest)
329
+ return;
330
+ const record = this.#cache.get(`record/${latest}`);
331
+ // `concept/{recid}` also holds latest-of-series for version ids; only a concept id may redirect.
332
+ return record?.concept_recid === recid ? record : undefined;
333
+ }
334
+ #storeRecord(record, requested) {
335
+ this.#cache.set(`record/${record.recid}`, record, 5 * MINUTE);
336
+ if (requested !== record.recid)
337
+ this.#cache.set(`concept/${requested}`, record.recid, 5 * MINUTE);
338
+ }
339
+ #fetchCommunity(slugOrId, ctx) {
340
+ return this.#cachedLookup(`community/${slugOrId}`, () => this.#http.request(this.#jsonRequest(`/communities/${encodeURIComponent(slugOrId)}`, JSON_ACCEPT, 'general', 'other', [200, 404], 'getCommunity'), async (res) => {
341
+ if (res.status === 404) {
342
+ await discardBody(res);
343
+ return;
344
+ }
345
+ return normalizeCommunity(await readJson(res));
346
+ }, ctx));
347
+ }
348
+ /** Cache wrapper for entry lookups: hits for 1 h, misses (stored as `null`) for 10 min. */
349
+ async #cachedLookup(key, load) {
350
+ const cached = this.#cache.get(key);
351
+ if (cached !== undefined)
352
+ return cached ?? undefined;
353
+ const value = await load();
354
+ this.#cache.set(key, value ?? null, value === undefined ? 10 * MINUTE : HOUR);
355
+ return value;
356
+ }
357
+ }
358
+ /** Maps a content endpoint's non-content statuses to a result, consuming the body. */
359
+ async function contentMiss(res) {
360
+ const status = res.status === 403
361
+ ? 'forbidden'
362
+ : res.status === 404
363
+ ? 'not_found'
364
+ : res.status === 416
365
+ ? 'range_not_satisfiable'
366
+ : undefined;
367
+ if (!status)
368
+ return;
369
+ await discardBody(res);
370
+ return { status, bytes: new Uint8Array(0), moreRemains: false };
371
+ }
372
+ // --- Init/accessor ---
373
+ let _service;
374
+ /** Creates the service. Called once from `createApp({ setup })`. */
375
+ export function initZenodoService(config) {
376
+ _service = new ZenodoService({
377
+ accessToken: getServerConfig().accessToken,
378
+ userAgent: `zenodo-mcp-server/${config.mcpServerVersion} (+https://github.com/cyanheads/zenodo-mcp-server)`,
379
+ });
380
+ }
381
+ /** The initialized service. Throws when `initZenodoService()` has not run. */
382
+ export function getZenodoService() {
383
+ if (!_service)
384
+ throw new Error('ZenodoService not initialized — call initZenodoService() in setup()');
385
+ return _service;
386
+ }
387
+ /** Disposes the service's pacers. Called from `createApp({ teardown })`. */
388
+ export function disposeZenodoService() {
389
+ _service?.dispose();
390
+ _service = undefined;
391
+ }
392
+ //# sourceMappingURL=zenodo-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zenodo-service.js","sourceRoot":"","sources":["../../../src/services/zenodo/zenodo-service.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,110 @@
1
+ {
2
+ "name": "@cyanheads/zenodo-mcp-server",
3
+ "version": "0.1.1",
4
+ "mcpName": "io.github.cyanheads/zenodo-mcp-server",
5
+ "description": "Search and resolve Zenodo datasets, software, and publications by DOI; trace versions and funding, list files, and preview text files via MCP. STDIO or Streamable HTTP.",
6
+ "type": "module",
7
+ "main": "dist/index.js",
8
+ "types": "dist/index.d.ts",
9
+ "bin": {
10
+ "zenodo-mcp-server": "dist/index.js"
11
+ },
12
+ "files": [
13
+ "changelog/",
14
+ "dist/",
15
+ "README.md",
16
+ "LICENSE",
17
+ "CLAUDE.md",
18
+ "AGENTS.md",
19
+ "Dockerfile",
20
+ "server.json"
21
+ ],
22
+ "scripts": {
23
+ "build": "bun run scripts/build.ts",
24
+ "rebuild": "bun run scripts/clean.ts && bun run scripts/build.ts",
25
+ "clean": "bun run scripts/clean.ts",
26
+ "devcheck": "bun run scripts/devcheck.ts",
27
+ "audit:fix": "bun audit fix",
28
+ "audit:refresh": "rm -f bun.lock && bun install && bun audit",
29
+ "tree": "bun run scripts/tree.ts",
30
+ "list-skills": "bun run scripts/list-skills.ts",
31
+ "format": "biome check --write .",
32
+ "format:unsafe": "biome check --write --unsafe .",
33
+ "lint:mcp": "bun run scripts/lint-mcp.ts",
34
+ "lint:packaging": "bun run scripts/lint-packaging.ts",
35
+ "bundle": "bun run build && npx -y @anthropic-ai/mcpb pack . dist/zenodo-mcp-server.mcpb && bun run scripts/clean-mcpb.ts dist/zenodo-mcp-server.mcpb",
36
+ "changelog:build": "bun run scripts/build-changelog.ts",
37
+ "changelog:check": "bun run scripts/build-changelog.ts --check",
38
+ "release:github": "bun run scripts/release-github.ts",
39
+ "test": "vitest run",
40
+ "test:coverage": "vitest run --coverage",
41
+ "start": "node dist/index.js",
42
+ "start:stdio": "MCP_TRANSPORT_TYPE=stdio node dist/index.js",
43
+ "start:http": "MCP_TRANSPORT_TYPE=http node dist/index.js"
44
+ },
45
+ "keywords": [
46
+ "zenodo",
47
+ "cern",
48
+ "research",
49
+ "datasets",
50
+ "doi",
51
+ "open-access",
52
+ "open-science",
53
+ "research-data",
54
+ "research-software",
55
+ "citations",
56
+ "mcp",
57
+ "model-context-protocol",
58
+ "mcp-server",
59
+ "typescript",
60
+ "bun",
61
+ "stdio",
62
+ "streamable-http",
63
+ "ai-agent"
64
+ ],
65
+ "repository": {
66
+ "type": "git",
67
+ "url": "git+https://github.com/cyanheads/zenodo-mcp-server.git"
68
+ },
69
+ "bugs": {
70
+ "url": "https://github.com/cyanheads/zenodo-mcp-server/issues"
71
+ },
72
+ "homepage": "https://github.com/cyanheads/zenodo-mcp-server#readme",
73
+ "author": "Casey Hand <casey@caseyjhand.com> (https://caseyjhand.com)",
74
+ "funding": [
75
+ {
76
+ "type": "github",
77
+ "url": "https://github.com/sponsors/cyanheads"
78
+ },
79
+ {
80
+ "type": "buy_me_a_coffee",
81
+ "url": "https://www.buymeacoffee.com/cyanheads"
82
+ }
83
+ ],
84
+ "license": "Apache-2.0",
85
+ "packageManager": "bun@1.4.0",
86
+ "engines": {
87
+ "bun": ">=1.4.0",
88
+ "node": ">=24.0.0"
89
+ },
90
+ "publishConfig": {
91
+ "access": "public"
92
+ },
93
+ "dependencies": {
94
+ "@cyanheads/mcp-ts-core": "^0.13.6",
95
+ "pino-pretty": "^13.1.3",
96
+ "zod": "^4.6.5"
97
+ },
98
+ "devDependencies": {
99
+ "@biomejs/biome": "2.5.14",
100
+ "@socketsecurity/bun-security-scanner": "^1.1.2",
101
+ "@types/node": "26.6.2",
102
+ "@vitest/coverage-istanbul": "5.0.1",
103
+ "depcheck": "^1.4.7",
104
+ "fast-check": "^4.10.2",
105
+ "ignore": "^7.0.9",
106
+ "tsc-alias": "^1.9.5",
107
+ "typescript": "^7.0.2",
108
+ "vitest": "^5.0.1"
109
+ }
110
+ }