@cyanheads/protein-mcp-server 0.1.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/AGENTS.md +416 -0
- package/CLAUDE.md +416 -0
- package/Dockerfile +121 -0
- package/LICENSE +201 -0
- package/README.md +321 -0
- package/changelog/0.1.x/0.1.0.md +21 -0
- package/changelog/template.md +127 -0
- package/dist/config/server-config.d.ts +27 -0
- package/dist/config/server-config.d.ts.map +1 -0
- package/dist/config/server-config.js +110 -0
- package/dist/config/server-config.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +53 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp-server/resources/definitions/af-summary.resource.d.ts +29 -0
- package/dist/mcp-server/resources/definitions/af-summary.resource.d.ts.map +1 -0
- package/dist/mcp-server/resources/definitions/af-summary.resource.js +61 -0
- package/dist/mcp-server/resources/definitions/af-summary.resource.js.map +1 -0
- package/dist/mcp-server/resources/definitions/index.d.ts +7 -0
- package/dist/mcp-server/resources/definitions/index.d.ts.map +1 -0
- package/dist/mcp-server/resources/definitions/index.js +7 -0
- package/dist/mcp-server/resources/definitions/index.js.map +1 -0
- package/dist/mcp-server/resources/definitions/pdb-summary.resource.d.ts +31 -0
- package/dist/mcp-server/resources/definitions/pdb-summary.resource.d.ts.map +1 -0
- package/dist/mcp-server/resources/definitions/pdb-summary.resource.js +68 -0
- package/dist/mcp-server/resources/definitions/pdb-summary.resource.js.map +1 -0
- package/dist/mcp-server/tools/definitions/_schemas.d.ts +31 -0
- package/dist/mcp-server/tools/definitions/_schemas.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/_schemas.js +82 -0
- package/dist/mcp-server/tools/definitions/_schemas.js.map +1 -0
- package/dist/mcp-server/tools/definitions/analyze-collection.tool.d.ts +61 -0
- package/dist/mcp-server/tools/definitions/analyze-collection.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/analyze-collection.tool.js +115 -0
- package/dist/mcp-server/tools/definitions/analyze-collection.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/compare-structures.tool.d.ts +49 -0
- package/dist/mcp-server/tools/definitions/compare-structures.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/compare-structures.tool.js +168 -0
- package/dist/mcp-server/tools/definitions/compare-structures.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/find-similar.tool.d.ts +67 -0
- package/dist/mcp-server/tools/definitions/find-similar.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/find-similar.tool.js +274 -0
- package/dist/mcp-server/tools/definitions/find-similar.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/get-annotations.tool.d.ts +58 -0
- package/dist/mcp-server/tools/definitions/get-annotations.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/get-annotations.tool.js +180 -0
- package/dist/mcp-server/tools/definitions/get-annotations.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/get-structure.tool.d.ts +78 -0
- package/dist/mcp-server/tools/definitions/get-structure.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/get-structure.tool.js +365 -0
- package/dist/mcp-server/tools/definitions/get-structure.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/index.d.ts +12 -0
- package/dist/mcp-server/tools/definitions/index.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/index.js +12 -0
- package/dist/mcp-server/tools/definitions/index.js.map +1 -0
- package/dist/mcp-server/tools/definitions/search-structures.tool.d.ts +71 -0
- package/dist/mcp-server/tools/definitions/search-structures.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/search-structures.tool.js +216 -0
- package/dist/mcp-server/tools/definitions/search-structures.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/track-ligands.tool.d.ts +59 -0
- package/dist/mcp-server/tools/definitions/track-ligands.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/track-ligands.tool.js +195 -0
- package/dist/mcp-server/tools/definitions/track-ligands.tool.js.map +1 -0
- package/dist/services/alignment/alignment-service.d.ts +56 -0
- package/dist/services/alignment/alignment-service.d.ts.map +1 -0
- package/dist/services/alignment/alignment-service.js +129 -0
- package/dist/services/alignment/alignment-service.js.map +1 -0
- package/dist/services/alphafold/alphafold-service.d.ts +57 -0
- package/dist/services/alphafold/alphafold-service.d.ts.map +1 -0
- package/dist/services/alphafold/alphafold-service.js +84 -0
- package/dist/services/alphafold/alphafold-service.js.map +1 -0
- package/dist/services/beacons/beacons-service.d.ts +52 -0
- package/dist/services/beacons/beacons-service.d.ts.map +1 -0
- package/dist/services/beacons/beacons-service.js +63 -0
- package/dist/services/beacons/beacons-service.js.map +1 -0
- package/dist/services/foldseek/foldseek-service.d.ts +71 -0
- package/dist/services/foldseek/foldseek-service.d.ts.map +1 -0
- package/dist/services/foldseek/foldseek-service.js +131 -0
- package/dist/services/foldseek/foldseek-service.js.map +1 -0
- package/dist/services/rcsb/facets.d.ts +50 -0
- package/dist/services/rcsb/facets.d.ts.map +1 -0
- package/dist/services/rcsb/facets.js +59 -0
- package/dist/services/rcsb/facets.js.map +1 -0
- package/dist/services/rcsb/rcsb-service.d.ts +81 -0
- package/dist/services/rcsb/rcsb-service.d.ts.map +1 -0
- package/dist/services/rcsb/rcsb-service.js +473 -0
- package/dist/services/rcsb/rcsb-service.js.map +1 -0
- package/dist/services/rcsb/types.d.ts +150 -0
- package/dist/services/rcsb/types.d.ts.map +1 -0
- package/dist/services/rcsb/types.js +8 -0
- package/dist/services/rcsb/types.js.map +1 -0
- package/dist/services/shared/async.d.ts +41 -0
- package/dist/services/shared/async.d.ts.map +1 -0
- package/dist/services/shared/async.js +64 -0
- package/dist/services/shared/async.js.map +1 -0
- package/dist/services/shared/http.d.ts +54 -0
- package/dist/services/shared/http.d.ts.map +1 -0
- package/dist/services/shared/http.js +115 -0
- package/dist/services/shared/http.js.map +1 -0
- package/dist/services/shared/identifiers.d.ts +12 -0
- package/dist/services/shared/identifiers.d.ts.map +1 -0
- package/dist/services/shared/identifiers.js +23 -0
- package/dist/services/shared/identifiers.js.map +1 -0
- package/dist/services/uniprot/uniprot-service.d.ts +65 -0
- package/dist/services/uniprot/uniprot-service.d.ts.map +1 -0
- package/dist/services/uniprot/uniprot-service.js +146 -0
- package/dist/services/uniprot/uniprot-service.js.map +1 -0
- package/package.json +106 -0
- package/server.json +99 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/services/rcsb/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Shared async primitives: bounded submit→poll for the RCSB
|
|
3
|
+
* alignment and Foldseek services (both expose a job ticket that isn't ready on
|
|
4
|
+
* the first poll), and a concurrency-capped fan-out for per-ID / per-pair work.
|
|
5
|
+
* @module services/shared/async
|
|
6
|
+
*/
|
|
7
|
+
import type { Context } from '@cyanheads/mcp-ts-core';
|
|
8
|
+
/** Outcome of a bounded poll: the resolved value, or a "still computing" signal. */
|
|
9
|
+
export type PollOutcome<T> = {
|
|
10
|
+
status: 'complete';
|
|
11
|
+
value: T;
|
|
12
|
+
} | {
|
|
13
|
+
status: 'computing';
|
|
14
|
+
};
|
|
15
|
+
/** One poll attempt result: ready with a value, or not ready yet. */
|
|
16
|
+
export type PollStep<T> = {
|
|
17
|
+
ready: true;
|
|
18
|
+
value: T;
|
|
19
|
+
} | {
|
|
20
|
+
ready: false;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Poll `step` with backoff until it reports ready or the wall-clock budget is
|
|
24
|
+
* spent, returning `status: 'computing'` on timeout rather than blocking past the
|
|
25
|
+
* budget. The agent re-calls to resume — async jobs are modeled as success
|
|
26
|
+
* variants, not thrown errors.
|
|
27
|
+
*/
|
|
28
|
+
export declare function withAsyncPoll<T>(opts: {
|
|
29
|
+
step: () => Promise<PollStep<T>>;
|
|
30
|
+
timeoutMs: number;
|
|
31
|
+
ctx: Context;
|
|
32
|
+
intervalMs?: number;
|
|
33
|
+
maxIntervalMs?: number;
|
|
34
|
+
}): Promise<PollOutcome<T>>;
|
|
35
|
+
/**
|
|
36
|
+
* Map `items` through `fn` with at most `limit` concurrent calls, preserving
|
|
37
|
+
* input order. `fn` is expected to handle its own failures (return a result
|
|
38
|
+
* variant) so one slow/failed unit degrades its row, not the whole call.
|
|
39
|
+
*/
|
|
40
|
+
export declare function mapWithConcurrency<T, R>(items: readonly T[], limit: number, fn: (item: T, index: number) => Promise<R>): Promise<R[]>;
|
|
41
|
+
//# sourceMappingURL=async.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"async.d.ts","sourceRoot":"","sources":["../../../src/services/shared/async.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAEtD,oFAAoF;AACpF,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GAAG;IAAE,MAAM,EAAE,WAAW,CAAA;CAAE,CAAC;AAExF,qEAAqE;AACrE,MAAM,MAAM,QAAQ,CAAC,CAAC,IAAI;IAAE,KAAK,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,KAAK,CAAA;CAAE,CAAC;AAkBvE;;;;;GAKG;AACH,wBAAsB,aAAa,CAAC,CAAC,EAAE,IAAI,EAAE;IAC3C,IAAI,EAAE,MAAM,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,OAAO,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAY1B;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,CAAC,EAAE,CAAC,EAC3C,KAAK,EAAE,SAAS,CAAC,EAAE,EACnB,KAAK,EAAE,MAAM,EACb,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,GACzC,OAAO,CAAC,CAAC,EAAE,CAAC,CAed"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Shared async primitives: bounded submit→poll for the RCSB
|
|
3
|
+
* alignment and Foldseek services (both expose a job ticket that isn't ready on
|
|
4
|
+
* the first poll), and a concurrency-capped fan-out for per-ID / per-pair work.
|
|
5
|
+
* @module services/shared/async
|
|
6
|
+
*/
|
|
7
|
+
/** Sleep for `ms`, resolving early (without rejecting) if the signal aborts. */
|
|
8
|
+
function sleep(ms, signal) {
|
|
9
|
+
return new Promise((resolve) => {
|
|
10
|
+
if (signal.aborted)
|
|
11
|
+
return resolve();
|
|
12
|
+
const onAbort = () => {
|
|
13
|
+
clearTimeout(timer);
|
|
14
|
+
resolve();
|
|
15
|
+
};
|
|
16
|
+
const timer = setTimeout(() => {
|
|
17
|
+
signal.removeEventListener('abort', onAbort);
|
|
18
|
+
resolve();
|
|
19
|
+
}, ms);
|
|
20
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Poll `step` with backoff until it reports ready or the wall-clock budget is
|
|
25
|
+
* spent, returning `status: 'computing'` on timeout rather than blocking past the
|
|
26
|
+
* budget. The agent re-calls to resume — async jobs are modeled as success
|
|
27
|
+
* variants, not thrown errors.
|
|
28
|
+
*/
|
|
29
|
+
export async function withAsyncPoll(opts) {
|
|
30
|
+
const start = Date.now();
|
|
31
|
+
let interval = opts.intervalMs ?? 1000;
|
|
32
|
+
const maxInterval = opts.maxIntervalMs ?? 2000;
|
|
33
|
+
while (true) {
|
|
34
|
+
const step = await opts.step();
|
|
35
|
+
if (step.ready)
|
|
36
|
+
return { status: 'complete', value: step.value };
|
|
37
|
+
const elapsed = Date.now() - start;
|
|
38
|
+
if (opts.ctx.signal.aborted || elapsed >= opts.timeoutMs)
|
|
39
|
+
return { status: 'computing' };
|
|
40
|
+
await sleep(Math.min(interval, opts.timeoutMs - elapsed), opts.ctx.signal);
|
|
41
|
+
interval = Math.min(interval * 1.5, maxInterval);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Map `items` through `fn` with at most `limit` concurrent calls, preserving
|
|
46
|
+
* input order. `fn` is expected to handle its own failures (return a result
|
|
47
|
+
* variant) so one slow/failed unit degrades its row, not the whole call.
|
|
48
|
+
*/
|
|
49
|
+
export async function mapWithConcurrency(items, limit, fn) {
|
|
50
|
+
const results = new Array(items.length);
|
|
51
|
+
let cursor = 0;
|
|
52
|
+
const worker = async () => {
|
|
53
|
+
while (true) {
|
|
54
|
+
const index = cursor++;
|
|
55
|
+
if (index >= items.length)
|
|
56
|
+
return;
|
|
57
|
+
results[index] = await fn(items[index], index);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
const workers = Array.from({ length: Math.min(Math.max(1, limit), items.length) }, () => worker());
|
|
61
|
+
await Promise.all(workers);
|
|
62
|
+
return results;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=async.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"async.js","sourceRoot":"","sources":["../../../src/services/shared/async.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAUH,gFAAgF;AAChF,SAAS,KAAK,CAAC,EAAU,EAAE,MAAmB;IAC5C,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,IAAI,MAAM,CAAC,OAAO;YAAE,OAAO,OAAO,EAAE,CAAC;QACrC,MAAM,OAAO,GAAG,GAAG,EAAE;YACnB,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC;QACF,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC7C,OAAO,EAAE,CAAC;QACZ,CAAC,EAAE,EAAE,CAAC,CAAC;QACP,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAI,IAMtC;IACC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACzB,IAAI,QAAQ,GAAG,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC;IACvC,MAAM,WAAW,GAAG,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC;IAC/C,OAAO,IAAI,EAAE,CAAC;QACZ,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QAC/B,IAAI,IAAI,CAAC,KAAK;YAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC;QACjE,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC;QACnC,IAAI,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,IAAI,OAAO,IAAI,IAAI,CAAC,SAAS;YAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;QACzF,MAAM,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC3E,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,GAAG,GAAG,EAAE,WAAW,CAAC,CAAC;IACnD,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,KAAmB,EACnB,KAAa,EACb,EAA0C;IAE1C,MAAM,OAAO,GAAG,IAAI,KAAK,CAAI,KAAK,CAAC,MAAM,CAAC,CAAC;IAC3C,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,MAAM,MAAM,GAAG,KAAK,IAAmB,EAAE;QACvC,OAAO,IAAI,EAAE,CAAC;YACZ,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC;YACvB,IAAI,KAAK,IAAI,KAAK,CAAC,MAAM;gBAAE,OAAO;YAClC,OAAO,CAAC,KAAK,CAAC,GAAG,MAAM,EAAE,CAAC,KAAK,CAAC,KAAK,CAAM,EAAE,KAAK,CAAC,CAAC;QACtD,CAAC;IACH,CAAC,CAAC;IACF,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,GAAG,EAAE,CACtF,MAAM,EAAE,CACT,CAAC;IACF,MAAM,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAC3B,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Shared HTTP layer for the protein data providers. Wraps the
|
|
3
|
+
* framework's `fetchWithTimeout` + `withRetry` (retry covers the full fetch +
|
|
4
|
+
* parse pipeline) and centralizes HTML-error-page / empty-body detection so each
|
|
5
|
+
* service stays thin. The single `Context → RequestContext` cast the framework
|
|
6
|
+
* utilities expect is isolated here.
|
|
7
|
+
* @module services/shared/http
|
|
8
|
+
*/
|
|
9
|
+
import type { Context } from '@cyanheads/mcp-ts-core';
|
|
10
|
+
/** Options shared by the JSON/text fetch helpers. */
|
|
11
|
+
export interface FetchOptions {
|
|
12
|
+
/** Base retry backoff. Default 400ms. */
|
|
13
|
+
baseDelayMs?: number;
|
|
14
|
+
/** Request body (pre-serialized JSON string, FormData, etc.). */
|
|
15
|
+
body?: RequestInit['body'];
|
|
16
|
+
/** Request headers. */
|
|
17
|
+
headers?: Record<string, string>;
|
|
18
|
+
/** Human-readable provider label used in error messages. */
|
|
19
|
+
label: string;
|
|
20
|
+
/** Retry attempts after the initial call. Default 3. */
|
|
21
|
+
maxRetries?: number;
|
|
22
|
+
/** HTTP method. Default `GET`. */
|
|
23
|
+
method?: string;
|
|
24
|
+
/** Operation label for correlated retry logging. */
|
|
25
|
+
operation: string;
|
|
26
|
+
/** Per-attempt timeout. Default 30s. */
|
|
27
|
+
timeoutMs?: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Parse a JSON body, first rejecting HTML error pages and empty responses as
|
|
31
|
+
* transient `ServiceUnavailable` (so `withRetry` retries them rather than feeding
|
|
32
|
+
* an HTML page into `JSON.parse`).
|
|
33
|
+
*/
|
|
34
|
+
export declare function parseJson<T>(text: string, label: string): T;
|
|
35
|
+
/** GET/POST JSON with retry over the full fetch + parse pipeline. Throws a status-mapped `McpError` on non-2xx. */
|
|
36
|
+
export declare function fetchJson<T>(url: string, ctx: Context, opts: FetchOptions): Promise<T>;
|
|
37
|
+
/** Fetch raw text (coordinate files, etc.) with retry. Throws a status-mapped `McpError` on non-2xx. */
|
|
38
|
+
export declare function fetchText(url: string, ctx: Context, opts: FetchOptions): Promise<string>;
|
|
39
|
+
/**
|
|
40
|
+
* Fetch returning the raw `Response` without throwing on non-2xx status, so the
|
|
41
|
+
* caller can branch on the code (e.g. 3D-Beacons 404 → "no models", an async poll
|
|
42
|
+
* 404 → "not ready yet"). Retries only network-level failures; HTTP statuses pass
|
|
43
|
+
* through. Combines `ctx.signal` with a per-attempt timeout.
|
|
44
|
+
*/
|
|
45
|
+
export declare function fetchResponse(url: string, ctx: Context, opts: {
|
|
46
|
+
method?: string;
|
|
47
|
+
body?: RequestInit['body'];
|
|
48
|
+
headers?: Record<string, string>;
|
|
49
|
+
operation: string;
|
|
50
|
+
timeoutMs?: number;
|
|
51
|
+
baseDelayMs?: number;
|
|
52
|
+
maxRetries?: number;
|
|
53
|
+
}): Promise<Response>;
|
|
54
|
+
//# sourceMappingURL=http.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../../src/services/shared/http.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AA4BtD,qDAAqD;AACrD,MAAM,WAAW,YAAY;IAC3B,yCAAyC;IACzC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iEAAiE;IACjE,IAAI,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC3B,uBAAuB;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,4DAA4D;IAC5D,KAAK,EAAE,MAAM,CAAC;IACd,wDAAwD;IACxD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kCAAkC;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oDAAoD;IACpD,SAAS,EAAE,MAAM,CAAC;IAClB,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,CAAC,CAgB3D;AAED,mHAAmH;AACnH,wBAAgB,SAAS,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,CAAC,CAAC,CAqBtF;AAED,wGAAwG;AACxG,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAqBxF;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,OAAO,EACZ,IAAI,EAAE;IACJ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,GACA,OAAO,CAAC,QAAQ,CAAC,CAqBnB"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Shared HTTP layer for the protein data providers. Wraps the
|
|
3
|
+
* framework's `fetchWithTimeout` + `withRetry` (retry covers the full fetch +
|
|
4
|
+
* parse pipeline) and centralizes HTML-error-page / empty-body detection so each
|
|
5
|
+
* service stays thin. The single `Context → RequestContext` cast the framework
|
|
6
|
+
* utilities expect is isolated here.
|
|
7
|
+
* @module services/shared/http
|
|
8
|
+
*/
|
|
9
|
+
import { serializationError, serviceUnavailable } from '@cyanheads/mcp-ts-core/errors';
|
|
10
|
+
import { fetchWithTimeout, withRetry } from '@cyanheads/mcp-ts-core/utils';
|
|
11
|
+
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
12
|
+
/**
|
|
13
|
+
* Identify this client to upstream providers. Node's global `fetch` sends no
|
|
14
|
+
* User-Agent by default, which some providers' edge WAFs reject outright — the
|
|
15
|
+
* AlphaFold DB API returns 403 Forbidden on an absent UA. A stable project UA
|
|
16
|
+
* also lets providers correlate or contact us about heavy traffic.
|
|
17
|
+
*/
|
|
18
|
+
const USER_AGENT = 'protein-mcp-server (+https://github.com/cyanheads/protein-mcp-server)';
|
|
19
|
+
/** Merge the default `User-Agent` under any caller-supplied headers. */
|
|
20
|
+
function withDefaultHeaders(headers) {
|
|
21
|
+
return { 'User-Agent': USER_AGENT, ...headers };
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* The framework HTTP utilities type their context parameter as `RequestContext`;
|
|
25
|
+
* the handler `Context` is runtime-compatible (the logger strips non-serializable
|
|
26
|
+
* fields). Cast once here so callers pass `ctx` unchanged.
|
|
27
|
+
*/
|
|
28
|
+
function asRequestContext(ctx) {
|
|
29
|
+
return ctx;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Parse a JSON body, first rejecting HTML error pages and empty responses as
|
|
33
|
+
* transient `ServiceUnavailable` (so `withRetry` retries them rather than feeding
|
|
34
|
+
* an HTML page into `JSON.parse`).
|
|
35
|
+
*/
|
|
36
|
+
export function parseJson(text, label) {
|
|
37
|
+
if (/^\s*<(!DOCTYPE\s+html|html[\s>])/i.test(text)) {
|
|
38
|
+
throw serviceUnavailable(`${label} returned HTML instead of JSON — likely unavailable or rate-limited.`);
|
|
39
|
+
}
|
|
40
|
+
if (text.trim().length === 0) {
|
|
41
|
+
throw serviceUnavailable(`${label} returned an empty response — endpoint may be temporarily unavailable.`);
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
return JSON.parse(text);
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
throw serializationError(`${label} returned unparseable JSON: ${text.slice(0, 200)}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/** GET/POST JSON with retry over the full fetch + parse pipeline. Throws a status-mapped `McpError` on non-2xx. */
|
|
51
|
+
export function fetchJson(url, ctx, opts) {
|
|
52
|
+
const rctx = asRequestContext(ctx);
|
|
53
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
54
|
+
return withRetry(async () => {
|
|
55
|
+
const res = await fetchWithTimeout(url, timeoutMs, rctx, {
|
|
56
|
+
method: opts.method ?? 'GET',
|
|
57
|
+
...(opts.body !== undefined && { body: opts.body }),
|
|
58
|
+
headers: withDefaultHeaders(opts.headers),
|
|
59
|
+
signal: ctx.signal,
|
|
60
|
+
});
|
|
61
|
+
return parseJson(await res.text(), opts.label);
|
|
62
|
+
}, {
|
|
63
|
+
operation: opts.operation,
|
|
64
|
+
context: rctx,
|
|
65
|
+
baseDelayMs: opts.baseDelayMs ?? 400,
|
|
66
|
+
...(opts.maxRetries !== undefined && { maxRetries: opts.maxRetries }),
|
|
67
|
+
signal: ctx.signal,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
/** Fetch raw text (coordinate files, etc.) with retry. Throws a status-mapped `McpError` on non-2xx. */
|
|
71
|
+
export function fetchText(url, ctx, opts) {
|
|
72
|
+
const rctx = asRequestContext(ctx);
|
|
73
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
74
|
+
return withRetry(async () => {
|
|
75
|
+
const res = await fetchWithTimeout(url, timeoutMs, rctx, {
|
|
76
|
+
method: opts.method ?? 'GET',
|
|
77
|
+
...(opts.body !== undefined && { body: opts.body }),
|
|
78
|
+
headers: withDefaultHeaders(opts.headers),
|
|
79
|
+
signal: ctx.signal,
|
|
80
|
+
});
|
|
81
|
+
return res.text();
|
|
82
|
+
}, {
|
|
83
|
+
operation: opts.operation,
|
|
84
|
+
context: rctx,
|
|
85
|
+
baseDelayMs: opts.baseDelayMs ?? 400,
|
|
86
|
+
...(opts.maxRetries !== undefined && { maxRetries: opts.maxRetries }),
|
|
87
|
+
signal: ctx.signal,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Fetch returning the raw `Response` without throwing on non-2xx status, so the
|
|
92
|
+
* caller can branch on the code (e.g. 3D-Beacons 404 → "no models", an async poll
|
|
93
|
+
* 404 → "not ready yet"). Retries only network-level failures; HTTP statuses pass
|
|
94
|
+
* through. Combines `ctx.signal` with a per-attempt timeout.
|
|
95
|
+
*/
|
|
96
|
+
export function fetchResponse(url, ctx, opts) {
|
|
97
|
+
const rctx = asRequestContext(ctx);
|
|
98
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
99
|
+
return withRetry(() => {
|
|
100
|
+
const signal = AbortSignal.any([ctx.signal, AbortSignal.timeout(timeoutMs)]);
|
|
101
|
+
return fetch(url, {
|
|
102
|
+
method: opts.method ?? 'GET',
|
|
103
|
+
...(opts.body !== undefined && { body: opts.body }),
|
|
104
|
+
headers: withDefaultHeaders(opts.headers),
|
|
105
|
+
signal,
|
|
106
|
+
});
|
|
107
|
+
}, {
|
|
108
|
+
operation: opts.operation,
|
|
109
|
+
context: rctx,
|
|
110
|
+
baseDelayMs: opts.baseDelayMs ?? 400,
|
|
111
|
+
maxRetries: opts.maxRetries ?? 2,
|
|
112
|
+
signal: ctx.signal,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=http.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../../src/services/shared/http.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACvF,OAAO,EAAE,gBAAgB,EAAuB,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAEhG,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAElC;;;;;GAKG;AACH,MAAM,UAAU,GAAG,uEAAuE,CAAC;AAE3F,wEAAwE;AACxE,SAAS,kBAAkB,CAAC,OAAgC;IAC1D,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,GAAG,OAAO,EAAE,CAAC;AAClD,CAAC;AAED;;;;GAIG;AACH,SAAS,gBAAgB,CAAC,GAAY;IACpC,OAAO,GAAgC,CAAC;AAC1C,CAAC;AAsBD;;;;GAIG;AACH,MAAM,UAAU,SAAS,CAAI,IAAY,EAAE,KAAa;IACtD,IAAI,mCAAmC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACnD,MAAM,kBAAkB,CACtB,GAAG,KAAK,sEAAsE,CAC/E,CAAC;IACJ,CAAC;IACD,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7B,MAAM,kBAAkB,CACtB,GAAG,KAAK,wEAAwE,CACjF,CAAC;IACJ,CAAC;IACD,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAM,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,kBAAkB,CAAC,GAAG,KAAK,+BAA+B,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;IACxF,CAAC;AACH,CAAC;AAED,mHAAmH;AACnH,MAAM,UAAU,SAAS,CAAI,GAAW,EAAE,GAAY,EAAE,IAAkB;IACxE,MAAM,IAAI,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;IACnC,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,kBAAkB,CAAC;IACvD,OAAO,SAAS,CACd,KAAK,IAAI,EAAE;QACT,MAAM,GAAG,GAAG,MAAM,gBAAgB,CAAC,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE;YACvD,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,KAAK;YAC5B,GAAG,CAAC,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC;YACnD,OAAO,EAAE,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC;YACzC,MAAM,EAAE,GAAG,CAAC,MAAM;SACnB,CAAC,CAAC;QACH,OAAO,SAAS,CAAI,MAAM,GAAG,CAAC,IAAI,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;IACpD,CAAC,EACD;QACE,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,OAAO,EAAE,IAAI;QACb,WAAW,EAAE,IAAI,CAAC,WAAW,IAAI,GAAG;QACpC,GAAG,CAAC,IAAI,CAAC,UAAU,KAAK,SAAS,IAAI,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC;QACrE,MAAM,EAAE,GAAG,CAAC,MAAM;KACnB,CACF,CAAC;AACJ,CAAC;AAED,wGAAwG;AACxG,MAAM,UAAU,SAAS,CAAC,GAAW,EAAE,GAAY,EAAE,IAAkB;IACrE,MAAM,IAAI,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;IACnC,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,kBAAkB,CAAC;IACvD,OAAO,SAAS,CACd,KAAK,IAAI,EAAE;QACT,MAAM,GAAG,GAAG,MAAM,gBAAgB,CAAC,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE;YACvD,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,KAAK;YAC5B,GAAG,CAAC,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC;YACnD,OAAO,EAAE,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC;YACzC,MAAM,EAAE,GAAG,CAAC,MAAM;SACnB,CAAC,CAAC;QACH,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC;IACpB,CAAC,EACD;QACE,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,OAAO,EAAE,IAAI;QACb,WAAW,EAAE,IAAI,CAAC,WAAW,IAAI,GAAG;QACpC,GAAG,CAAC,IAAI,CAAC,UAAU,KAAK,SAAS,IAAI,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC;QACrE,MAAM,EAAE,GAAG,CAAC,MAAM;KACnB,CACF,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAC3B,GAAW,EACX,GAAY,EACZ,IAQC;IAED,MAAM,IAAI,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;IACnC,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,kBAAkB,CAAC;IACvD,OAAO,SAAS,CACd,GAAG,EAAE;QACH,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;QAC7E,OAAO,KAAK,CAAC,GAAG,EAAE;YAChB,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,KAAK;YAC5B,GAAG,CAAC,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC;YACnD,OAAO,EAAE,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC;YACzC,MAAM;SACP,CAAC,CAAC;IACL,CAAC,EACD;QACE,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,OAAO,EAAE,IAAI;QACb,WAAW,EAAE,IAAI,CAAC,WAAW,IAAI,GAAG;QACpC,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,CAAC;QAChC,MAAM,EAAE,GAAG,CAAC,MAAM;KACnB,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Identifier shape detection for routing between the experimental
|
|
3
|
+
* (PDB) and predicted (UniProt-keyed) halves of the federation. Pure, no I/O.
|
|
4
|
+
* @module services/shared/identifiers
|
|
5
|
+
*/
|
|
6
|
+
/** True when `value` is a syntactically valid UniProt accession. */
|
|
7
|
+
export declare function isUniProtAccession(value: string): boolean;
|
|
8
|
+
/** True when `value` is a syntactically valid PDB entry ID. */
|
|
9
|
+
export declare function isPdbId(value: string): boolean;
|
|
10
|
+
/** Extract the PDB entry ID from a polymer-entity ID (`4HHB_1` → `4HHB`). */
|
|
11
|
+
export declare function entryIdOf(identifier: string): string;
|
|
12
|
+
//# sourceMappingURL=identifiers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identifiers.d.ts","sourceRoot":"","sources":["../../../src/services/shared/identifiers.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAQH,oEAAoE;AACpE,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED,+DAA+D;AAC/D,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE9C;AAED,6EAA6E;AAC7E,wBAAgB,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAGpD"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Identifier shape detection for routing between the experimental
|
|
3
|
+
* (PDB) and predicted (UniProt-keyed) halves of the federation. Pure, no I/O.
|
|
4
|
+
* @module services/shared/identifiers
|
|
5
|
+
*/
|
|
6
|
+
/** UniProt accession pattern (6- or 10-character forms). */
|
|
7
|
+
const UNIPROT_RE = /^(?:[OPQ][0-9][A-Z0-9]{3}[0-9]|[A-NR-Z][0-9](?:[A-Z][A-Z0-9]{2}[0-9]){1,2})$/i;
|
|
8
|
+
/** PDB entry ID pattern: 4 alphanumerics, first character a digit (e.g. 4HHB, 1IEP). */
|
|
9
|
+
const PDB_RE = /^[0-9][A-Za-z0-9]{3}$/;
|
|
10
|
+
/** True when `value` is a syntactically valid UniProt accession. */
|
|
11
|
+
export function isUniProtAccession(value) {
|
|
12
|
+
return UNIPROT_RE.test(value.trim());
|
|
13
|
+
}
|
|
14
|
+
/** True when `value` is a syntactically valid PDB entry ID. */
|
|
15
|
+
export function isPdbId(value) {
|
|
16
|
+
return PDB_RE.test(value.trim());
|
|
17
|
+
}
|
|
18
|
+
/** Extract the PDB entry ID from a polymer-entity ID (`4HHB_1` → `4HHB`). */
|
|
19
|
+
export function entryIdOf(identifier) {
|
|
20
|
+
const base = identifier.split(/[_.]/)[0] ?? identifier;
|
|
21
|
+
return base.toUpperCase();
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=identifiers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identifiers.js","sourceRoot":"","sources":["../../../src/services/shared/identifiers.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,4DAA4D;AAC5D,MAAM,UAAU,GAAG,+EAA+E,CAAC;AAEnG,wFAAwF;AACxF,MAAM,MAAM,GAAG,uBAAuB,CAAC;AAEvC,oEAAoE;AACpE,MAAM,UAAU,kBAAkB,CAAC,KAAa;IAC9C,OAAO,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;AACvC,CAAC;AAED,+DAA+D;AAC/D,MAAM,UAAU,OAAO,CAAC,KAAa;IACnC,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;AACnC,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,SAAS,CAAC,UAAkB;IAC1C,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,UAAU,CAAC;IACvD,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC;AAC5B,CAAC"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview UniProt service — wraps the UniProt REST API (sequence features,
|
|
3
|
+
* function, variants) and the InterPro REST API (domain/family memberships across
|
|
4
|
+
* Pfam, PROSITE, … with GO terms). Backs `protein_get_annotations`. Field
|
|
5
|
+
* selection trims UniProt payloads to what the requested `include` scope needs.
|
|
6
|
+
* @module services/uniprot/uniprot-service
|
|
7
|
+
*/
|
|
8
|
+
import type { Context } from '@cyanheads/mcp-ts-core';
|
|
9
|
+
import type { AppConfig } from '@cyanheads/mcp-ts-core/config';
|
|
10
|
+
import type { StorageService } from '@cyanheads/mcp-ts-core/storage';
|
|
11
|
+
import type { ServerConfig } from '../../config/server-config.js';
|
|
12
|
+
/** Which annotation classes to fetch. */
|
|
13
|
+
export type AnnotationInclude = 'features' | 'domains' | 'variants' | 'all';
|
|
14
|
+
/** A positional sequence feature (domain, binding site, PTM, or variant). */
|
|
15
|
+
export interface SequenceFeature {
|
|
16
|
+
/** `feature` (structural/functional) or `variant`. */
|
|
17
|
+
category: 'feature' | 'variant';
|
|
18
|
+
/** Free-text description. */
|
|
19
|
+
description?: string;
|
|
20
|
+
/** End residue (1-based). */
|
|
21
|
+
end?: number;
|
|
22
|
+
/** Start residue (1-based). */
|
|
23
|
+
start?: number;
|
|
24
|
+
/** UniProt feature type (e.g. "Domain", "Binding site", "Natural variant"). */
|
|
25
|
+
type: string;
|
|
26
|
+
}
|
|
27
|
+
/** Normalized UniProt entry. */
|
|
28
|
+
export interface UniProtEntry {
|
|
29
|
+
accession: string;
|
|
30
|
+
features: SequenceFeature[];
|
|
31
|
+
function?: string;
|
|
32
|
+
geneNames: string[];
|
|
33
|
+
organism?: string;
|
|
34
|
+
proteinName?: string;
|
|
35
|
+
sequenceLength?: number;
|
|
36
|
+
taxonId?: number;
|
|
37
|
+
}
|
|
38
|
+
/** A GO term annotation. */
|
|
39
|
+
export interface GoTerm {
|
|
40
|
+
category?: string;
|
|
41
|
+
id: string;
|
|
42
|
+
name: string;
|
|
43
|
+
}
|
|
44
|
+
/** An InterPro domain/family membership for a protein. */
|
|
45
|
+
export interface InterProEntry {
|
|
46
|
+
accession: string;
|
|
47
|
+
goTerms: GoTerm[];
|
|
48
|
+
memberDatabases: string[];
|
|
49
|
+
name: string;
|
|
50
|
+
type: string;
|
|
51
|
+
}
|
|
52
|
+
export declare class UniProtService {
|
|
53
|
+
private readonly uniprotBase;
|
|
54
|
+
private readonly interproBase;
|
|
55
|
+
constructor(_config: AppConfig, _storage: StorageService, serverConfig: ServerConfig);
|
|
56
|
+
/** Fetch a UniProt entry, selecting only the fields the include scope needs. */
|
|
57
|
+
getEntry(accession: string, include: AnnotationInclude, ctx: Context): Promise<UniProtEntry>;
|
|
58
|
+
/** Fetch the one-letter amino-acid sequence for a UniProt accession. */
|
|
59
|
+
getSequence(accession: string, ctx: Context): Promise<string | null>;
|
|
60
|
+
/** Fetch InterPro domain/family memberships for a UniProt accession. */
|
|
61
|
+
getInterPro(accession: string, ctx: Context): Promise<InterProEntry[]>;
|
|
62
|
+
}
|
|
63
|
+
export declare function initUniProtService(config: AppConfig, storage: StorageService, serverConfig: ServerConfig): void;
|
|
64
|
+
export declare function getUniProtService(): UniProtService;
|
|
65
|
+
//# sourceMappingURL=uniprot-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uniprot-service.d.ts","sourceRoot":"","sources":["../../../src/services/uniprot/uniprot-service.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAE/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAG9D,yCAAyC;AACzC,MAAM,MAAM,iBAAiB,GAAG,UAAU,GAAG,SAAS,GAAG,UAAU,GAAG,KAAK,CAAC;AAE5E,6EAA6E;AAC7E,MAAM,WAAW,eAAe;IAC9B,sDAAsD;IACtD,QAAQ,EAAE,SAAS,GAAG,SAAS,CAAC;IAChC,6BAA6B;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6BAA6B;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,+BAA+B;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+EAA+E;IAC/E,IAAI,EAAE,MAAM,CAAC;CACd;AAED,gCAAgC;AAChC,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,4BAA4B;AAC5B,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd;AAED,0DAA0D;AAC1D,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAyBD,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;gBAE1B,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY;IAKpF,gFAAgF;IAC1E,QAAQ,CACZ,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,iBAAiB,EAC1B,GAAG,EAAE,OAAO,GACX,OAAO,CAAC,YAAY,CAAC;IAaxB,wEAAwE;IAClE,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAU1E,wEAAwE;IAClE,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;CAmB7E;AA2FD,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,cAAc,EACvB,YAAY,EAAE,YAAY,GACzB,IAAI,CAEN;AAED,wBAAgB,iBAAiB,IAAI,cAAc,CAIlD"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview UniProt service — wraps the UniProt REST API (sequence features,
|
|
3
|
+
* function, variants) and the InterPro REST API (domain/family memberships across
|
|
4
|
+
* Pfam, PROSITE, … with GO terms). Backs `protein_get_annotations`. Field
|
|
5
|
+
* selection trims UniProt payloads to what the requested `include` scope needs.
|
|
6
|
+
* @module services/uniprot/uniprot-service
|
|
7
|
+
*/
|
|
8
|
+
import { JsonRpcErrorCode, McpError } from '@cyanheads/mcp-ts-core/errors';
|
|
9
|
+
import { fetchJson } from '../shared/http.js';
|
|
10
|
+
const BASE_FIELDS = [
|
|
11
|
+
'accession',
|
|
12
|
+
'protein_name',
|
|
13
|
+
'gene_names',
|
|
14
|
+
'organism_name',
|
|
15
|
+
'cc_function',
|
|
16
|
+
'sequence',
|
|
17
|
+
];
|
|
18
|
+
const FEATURE_FIELDS = [
|
|
19
|
+
'ft_domain',
|
|
20
|
+
'ft_binding',
|
|
21
|
+
'ft_act_site',
|
|
22
|
+
'ft_site',
|
|
23
|
+
'ft_mod_res',
|
|
24
|
+
'ft_carbohyd',
|
|
25
|
+
'ft_disulfid',
|
|
26
|
+
'ft_signal',
|
|
27
|
+
'ft_transmem',
|
|
28
|
+
'ft_motif',
|
|
29
|
+
'ft_region',
|
|
30
|
+
];
|
|
31
|
+
const VARIANT_FIELDS = ['ft_variant'];
|
|
32
|
+
export class UniProtService {
|
|
33
|
+
uniprotBase;
|
|
34
|
+
interproBase;
|
|
35
|
+
constructor(_config, _storage, serverConfig) {
|
|
36
|
+
this.uniprotBase = serverConfig.uniprotBaseUrl;
|
|
37
|
+
this.interproBase = serverConfig.interproBaseUrl;
|
|
38
|
+
}
|
|
39
|
+
/** Fetch a UniProt entry, selecting only the fields the include scope needs. */
|
|
40
|
+
async getEntry(accession, include, ctx) {
|
|
41
|
+
const fields = [...BASE_FIELDS];
|
|
42
|
+
if (include === 'features' || include === 'all')
|
|
43
|
+
fields.push(...FEATURE_FIELDS);
|
|
44
|
+
if (include === 'variants' || include === 'all')
|
|
45
|
+
fields.push(...VARIANT_FIELDS);
|
|
46
|
+
const url = `${this.uniprotBase}/uniprotkb/${encodeURIComponent(accession.toUpperCase())}?format=json&fields=${fields.join(',')}`;
|
|
47
|
+
const raw = await fetchJson(url, ctx, {
|
|
48
|
+
operation: 'UniProtService.getEntry',
|
|
49
|
+
label: 'UniProt',
|
|
50
|
+
baseDelayMs: 400,
|
|
51
|
+
});
|
|
52
|
+
return normalizeEntry(accession.toUpperCase(), raw);
|
|
53
|
+
}
|
|
54
|
+
/** Fetch the one-letter amino-acid sequence for a UniProt accession. */
|
|
55
|
+
async getSequence(accession, ctx) {
|
|
56
|
+
const url = `${this.uniprotBase}/uniprotkb/${encodeURIComponent(accession.toUpperCase())}?format=json&fields=sequence`;
|
|
57
|
+
const raw = await fetchJson(url, ctx, {
|
|
58
|
+
operation: 'UniProtService.getSequence',
|
|
59
|
+
label: 'UniProt',
|
|
60
|
+
baseDelayMs: 400,
|
|
61
|
+
});
|
|
62
|
+
return raw.sequence?.value ?? null;
|
|
63
|
+
}
|
|
64
|
+
/** Fetch InterPro domain/family memberships for a UniProt accession. */
|
|
65
|
+
async getInterPro(accession, ctx) {
|
|
66
|
+
const url = `${this.interproBase}/entry/interpro/protein/UniProt/${encodeURIComponent(accession.toUpperCase())}/`;
|
|
67
|
+
let raw;
|
|
68
|
+
try {
|
|
69
|
+
raw = await fetchJson(url, ctx, {
|
|
70
|
+
operation: 'UniProtService.getInterPro',
|
|
71
|
+
label: 'InterPro',
|
|
72
|
+
baseDelayMs: 400,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
// InterPro returns 404 when no entries match — that's "no domains", not a failure.
|
|
77
|
+
if (err instanceof McpError && err.code === JsonRpcErrorCode.NotFound)
|
|
78
|
+
return [];
|
|
79
|
+
throw err;
|
|
80
|
+
}
|
|
81
|
+
return (raw.results ?? [])
|
|
82
|
+
.map((r) => r.metadata)
|
|
83
|
+
.filter((m) => m != null && !!m.accession)
|
|
84
|
+
.map(normalizeInterPro);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// ─── Normalizers ──────────────────────────────────────────────────────────────
|
|
88
|
+
function normalizeEntry(accession, raw) {
|
|
89
|
+
const proteinName = raw.proteinDescription?.recommendedName?.fullName?.value;
|
|
90
|
+
const geneNames = (raw.genes ?? []).map((g) => g.geneName?.value).filter((n) => !!n);
|
|
91
|
+
const functionText = raw.comments
|
|
92
|
+
?.find((c) => c.commentType === 'FUNCTION')
|
|
93
|
+
?.texts?.map((t) => t.value)
|
|
94
|
+
.filter((v) => !!v)
|
|
95
|
+
.join(' ');
|
|
96
|
+
const features = (raw.features ?? []).map(normalizeFeature);
|
|
97
|
+
return {
|
|
98
|
+
accession: raw.primaryAccession ?? accession,
|
|
99
|
+
...(proteinName ? { proteinName } : {}),
|
|
100
|
+
geneNames,
|
|
101
|
+
...(raw.organism?.scientificName ? { organism: raw.organism.scientificName } : {}),
|
|
102
|
+
...(typeof raw.organism?.taxonId === 'number' ? { taxonId: raw.organism.taxonId } : {}),
|
|
103
|
+
...(functionText ? { function: functionText } : {}),
|
|
104
|
+
...(typeof raw.sequence?.length === 'number' ? { sequenceLength: raw.sequence.length } : {}),
|
|
105
|
+
features,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
function normalizeFeature(raw) {
|
|
109
|
+
const type = raw.type ?? 'Feature';
|
|
110
|
+
return {
|
|
111
|
+
category: /variant/i.test(type) ? 'variant' : 'feature',
|
|
112
|
+
type,
|
|
113
|
+
...(raw.description ? { description: raw.description } : {}),
|
|
114
|
+
...(typeof raw.location?.start?.value === 'number' ? { start: raw.location.start.value } : {}),
|
|
115
|
+
...(typeof raw.location?.end?.value === 'number' ? { end: raw.location.end.value } : {}),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
function normalizeInterPro(raw) {
|
|
119
|
+
const memberDatabases = raw.member_databases ? Object.keys(raw.member_databases) : [];
|
|
120
|
+
const goTerms = (raw.go_terms ?? [])
|
|
121
|
+
.map((g) => g.identifier && g.name
|
|
122
|
+
? {
|
|
123
|
+
id: g.identifier,
|
|
124
|
+
name: g.name,
|
|
125
|
+
...(g.category?.name ? { category: g.category.name } : {}),
|
|
126
|
+
}
|
|
127
|
+
: undefined)
|
|
128
|
+
.filter((g) => g != null);
|
|
129
|
+
return {
|
|
130
|
+
accession: raw.accession,
|
|
131
|
+
name: raw.name ?? raw.accession ?? '',
|
|
132
|
+
type: raw.type ?? 'unknown',
|
|
133
|
+
memberDatabases,
|
|
134
|
+
goTerms,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
let _service;
|
|
138
|
+
export function initUniProtService(config, storage, serverConfig) {
|
|
139
|
+
_service = new UniProtService(config, storage, serverConfig);
|
|
140
|
+
}
|
|
141
|
+
export function getUniProtService() {
|
|
142
|
+
if (!_service)
|
|
143
|
+
throw new Error('UniProtService not initialized — call initUniProtService() in setup()');
|
|
144
|
+
return _service;
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=uniprot-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uniprot-service.js","sourceRoot":"","sources":["../../../src/services/uniprot/uniprot-service.ts"],"names":[],"mappings":"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"}
|