@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,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview RCSB Structural Comparison (alignment) service — wraps the hosted
|
|
3
|
+
* async pairwise alignment API (`/api/v1/structures/submit` → `/results?uuid=`).
|
|
4
|
+
* Native mode is `pairwise`; multi-structure comparison fans these out. Submit
|
|
5
|
+
* returns a bare UUID; the results poll returns 404 until the job is computed.
|
|
6
|
+
* Backs `protein_compare_structures`. No in-process alignment — edge-deployable.
|
|
7
|
+
* @module services/alignment/alignment-service
|
|
8
|
+
*/
|
|
9
|
+
import { withAsyncPoll } from '../shared/async.js';
|
|
10
|
+
import { fetchResponse, fetchText, parseJson } from '../shared/http.js';
|
|
11
|
+
export class AlignmentService {
|
|
12
|
+
submitUrl;
|
|
13
|
+
resultsUrl;
|
|
14
|
+
constructor(_config, _storage, serverConfig) {
|
|
15
|
+
this.submitUrl = `${serverConfig.rcsbAlignmentBaseUrl}/api/v1/structures/submit`;
|
|
16
|
+
this.resultsUrl = `${serverConfig.rcsbAlignmentBaseUrl}/api/v1/structures/results`;
|
|
17
|
+
}
|
|
18
|
+
/** Submit a pairwise alignment job; returns the job UUID. */
|
|
19
|
+
async submit(a, b, method, ctx) {
|
|
20
|
+
const query = {
|
|
21
|
+
context: {
|
|
22
|
+
mode: 'pairwise',
|
|
23
|
+
// The API's method schema is additionalProperties:false with only `name`
|
|
24
|
+
// for tm-align — sending an (even empty) `parameters` object fails
|
|
25
|
+
// validation. Methods that accept parameters all default sensibly.
|
|
26
|
+
method: { name: method },
|
|
27
|
+
structures: [toQueryStructure(a), toQueryStructure(b)],
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
const url = `${this.submitUrl}?query=${encodeURIComponent(JSON.stringify(query))}`;
|
|
31
|
+
const text = await fetchText(url, ctx, {
|
|
32
|
+
method: 'POST',
|
|
33
|
+
headers: { 'Content-Type': 'application/json' },
|
|
34
|
+
operation: 'AlignmentService.submit',
|
|
35
|
+
label: 'RCSB Alignment API',
|
|
36
|
+
baseDelayMs: 500,
|
|
37
|
+
});
|
|
38
|
+
return text.trim().replace(/^"|"$/g, '');
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Run one pairwise alignment end-to-end: submit, then bounded-poll the result.
|
|
42
|
+
* Never throws — a submit/poll failure degrades to `{ status: 'failed' }` so
|
|
43
|
+
* one bad pair doesn't sink the whole comparison.
|
|
44
|
+
*/
|
|
45
|
+
async comparePair(a, b, method, timeoutMs, ctx) {
|
|
46
|
+
let uuid;
|
|
47
|
+
try {
|
|
48
|
+
uuid = await this.submit(a, b, method, ctx);
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
return { status: 'failed', error: err instanceof Error ? err.message : String(err) };
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
const outcome = await withAsyncPoll({
|
|
55
|
+
step: () => this.pollResult(uuid, ctx),
|
|
56
|
+
timeoutMs,
|
|
57
|
+
ctx,
|
|
58
|
+
intervalMs: 1500,
|
|
59
|
+
maxIntervalMs: 2500,
|
|
60
|
+
});
|
|
61
|
+
return outcome.status === 'complete'
|
|
62
|
+
? { status: 'complete', uuid, scores: outcome.value }
|
|
63
|
+
: { status: 'computing', uuid };
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
return { status: 'failed', error: err instanceof Error ? err.message : String(err) };
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/** Single poll of a job's results. 404 = still computing; a result body = ready. */
|
|
70
|
+
async pollResult(uuid, ctx) {
|
|
71
|
+
const res = await fetchResponse(`${this.resultsUrl}?uuid=${encodeURIComponent(uuid)}`, ctx, {
|
|
72
|
+
operation: 'AlignmentService.pollResult',
|
|
73
|
+
timeoutMs: 15_000,
|
|
74
|
+
baseDelayMs: 400,
|
|
75
|
+
});
|
|
76
|
+
if (res.status === 404)
|
|
77
|
+
return { ready: false };
|
|
78
|
+
if (!res.ok)
|
|
79
|
+
throw new Error(`RCSB Alignment results returned HTTP ${res.status}`);
|
|
80
|
+
const raw = parseJson(await res.text(), 'RCSB Alignment API');
|
|
81
|
+
const first = raw.results?.[0];
|
|
82
|
+
if (!first)
|
|
83
|
+
return { ready: false };
|
|
84
|
+
return { ready: true, value: normalizeScores(first) };
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function toQueryStructure(s) {
|
|
88
|
+
return {
|
|
89
|
+
entry_id: s.entryId.toUpperCase(),
|
|
90
|
+
...(s.asymId ? { selection: { asym_id: s.asymId } } : {}),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Extract TM-score / RMSD / aligned-residue count from a result row. The summary
|
|
95
|
+
* carries a heterogeneous `scores` array (`{ type, value }`); match by type name
|
|
96
|
+
* so a field rename upstream degrades a single metric rather than the whole row.
|
|
97
|
+
*/
|
|
98
|
+
function normalizeScores(result) {
|
|
99
|
+
const summary = result.summary ?? {};
|
|
100
|
+
const scores = summary.scores ?? [];
|
|
101
|
+
const byType = (re) => scores.find((s) => s.type && re.test(s.type))?.value;
|
|
102
|
+
const out = {};
|
|
103
|
+
const tm = byType(/tm[\s_-]?score/i);
|
|
104
|
+
const rmsd = byType(/rmsd/i);
|
|
105
|
+
const seqId = byType(/seq.*id|identity/i);
|
|
106
|
+
const aligned = summary.n_aln_residue_pairs ??
|
|
107
|
+
summary.aligned_residues ??
|
|
108
|
+
summary.n_aligned_residues ??
|
|
109
|
+
byType(/aligned/i);
|
|
110
|
+
if (typeof tm === 'number')
|
|
111
|
+
out.tmScore = tm;
|
|
112
|
+
if (typeof rmsd === 'number')
|
|
113
|
+
out.rmsd = rmsd;
|
|
114
|
+
if (typeof seqId === 'number')
|
|
115
|
+
out.sequenceIdentity = seqId;
|
|
116
|
+
if (typeof aligned === 'number')
|
|
117
|
+
out.alignedResidues = aligned;
|
|
118
|
+
return out;
|
|
119
|
+
}
|
|
120
|
+
let _service;
|
|
121
|
+
export function initAlignmentService(config, storage, serverConfig) {
|
|
122
|
+
_service = new AlignmentService(config, storage, serverConfig);
|
|
123
|
+
}
|
|
124
|
+
export function getAlignmentService() {
|
|
125
|
+
if (!_service)
|
|
126
|
+
throw new Error('AlignmentService not initialized — call initAlignmentService() in setup()');
|
|
127
|
+
return _service;
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=alignment-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"alignment-service.js","sourceRoot":"","sources":["../../../src/services/alignment/alignment-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview AlphaFold service — wraps the AlphaFold Protein Structure
|
|
3
|
+
* Database API (`/api/prediction/{accession}`). Kept as a direct provider (not
|
|
4
|
+
* only via 3D-Beacons) because it carries confidence detail the Beacons summary
|
|
5
|
+
* abbreviates: mean pLDDT, the four confidence-bucket fractions, and the PAE
|
|
6
|
+
* documentation URL. Backs `protein_get_structure` (predicted) and the `af://`
|
|
7
|
+
* resource.
|
|
8
|
+
* @module services/alphafold/alphafold-service
|
|
9
|
+
*/
|
|
10
|
+
import type { Context } from '@cyanheads/mcp-ts-core';
|
|
11
|
+
import type { AppConfig } from '@cyanheads/mcp-ts-core/config';
|
|
12
|
+
import type { StorageService } from '@cyanheads/mcp-ts-core/storage';
|
|
13
|
+
import type { ServerConfig } from '../../config/server-config.js';
|
|
14
|
+
/** Fractions of residues in each pLDDT confidence band (0–1). */
|
|
15
|
+
export interface ConfidenceBuckets {
|
|
16
|
+
confident: number;
|
|
17
|
+
low: number;
|
|
18
|
+
veryHigh: number;
|
|
19
|
+
veryLow: number;
|
|
20
|
+
}
|
|
21
|
+
/** A normalized AlphaFold prediction. */
|
|
22
|
+
export interface AlphaFoldModel {
|
|
23
|
+
bcifUrl?: string;
|
|
24
|
+
/** Coordinate / metadata URLs. */
|
|
25
|
+
cifUrl?: string;
|
|
26
|
+
/** Residue fractions in each confidence band. */
|
|
27
|
+
confidenceBuckets?: ConfidenceBuckets;
|
|
28
|
+
/** AlphaFold model entry ID (e.g. `AF-P69905-F1`). */
|
|
29
|
+
entryId?: string;
|
|
30
|
+
/** Mean pLDDT across the model (0–100). */
|
|
31
|
+
meanPlddt?: number;
|
|
32
|
+
/** Model version (latest). */
|
|
33
|
+
modelVersion?: number;
|
|
34
|
+
/** Source organism scientific name. */
|
|
35
|
+
organism?: string;
|
|
36
|
+
paeDocUrl?: string;
|
|
37
|
+
paeImageUrl?: string;
|
|
38
|
+
pdbUrl?: string;
|
|
39
|
+
/** Predicted sequence length. */
|
|
40
|
+
sequenceLength?: number;
|
|
41
|
+
/** UniProt accession the model predicts. */
|
|
42
|
+
uniprotAccession: string;
|
|
43
|
+
/** UniProt protein description. */
|
|
44
|
+
uniprotDescription?: string;
|
|
45
|
+
}
|
|
46
|
+
export declare class AlphaFoldService {
|
|
47
|
+
private readonly baseUrl;
|
|
48
|
+
constructor(_config: AppConfig, _storage: StorageService, serverConfig: ServerConfig);
|
|
49
|
+
/**
|
|
50
|
+
* Fetch the AlphaFold prediction for a UniProt accession. Returns `null` when
|
|
51
|
+
* no model exists (404); a malformed accession (400) bubbles as `InvalidParams`.
|
|
52
|
+
*/
|
|
53
|
+
getPrediction(accession: string, ctx: Context): Promise<AlphaFoldModel | null>;
|
|
54
|
+
}
|
|
55
|
+
export declare function initAlphaFoldService(config: AppConfig, storage: StorageService, serverConfig: ServerConfig): void;
|
|
56
|
+
export declare function getAlphaFoldService(): AlphaFoldService;
|
|
57
|
+
//# sourceMappingURL=alphafold-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"alphafold-service.d.ts","sourceRoot":"","sources":["../../../src/services/alphafold/alphafold-service.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;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,iEAAiE;AACjE,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,yCAAyC;AACzC,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kCAAkC;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iDAAiD;IACjD,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,sDAAsD;IACtD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,2CAA2C;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8BAA8B;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,uCAAuC;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iCAAiC;IACjC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,4CAA4C;IAC5C,gBAAgB,EAAE,MAAM,CAAC;IACzB,mCAAmC;IACnC,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAqBD,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAErB,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY;IAIpF;;;OAGG;IACG,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;CAiBrF;AA0CD,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,cAAc,EACvB,YAAY,EAAE,YAAY,GACzB,IAAI,CAEN;AAED,wBAAgB,mBAAmB,IAAI,gBAAgB,CAItD"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview AlphaFold service — wraps the AlphaFold Protein Structure
|
|
3
|
+
* Database API (`/api/prediction/{accession}`). Kept as a direct provider (not
|
|
4
|
+
* only via 3D-Beacons) because it carries confidence detail the Beacons summary
|
|
5
|
+
* abbreviates: mean pLDDT, the four confidence-bucket fractions, and the PAE
|
|
6
|
+
* documentation URL. Backs `protein_get_structure` (predicted) and the `af://`
|
|
7
|
+
* resource.
|
|
8
|
+
* @module services/alphafold/alphafold-service
|
|
9
|
+
*/
|
|
10
|
+
import { JsonRpcErrorCode, McpError } from '@cyanheads/mcp-ts-core/errors';
|
|
11
|
+
import { fetchJson } from '../shared/http.js';
|
|
12
|
+
export class AlphaFoldService {
|
|
13
|
+
baseUrl;
|
|
14
|
+
constructor(_config, _storage, serverConfig) {
|
|
15
|
+
this.baseUrl = serverConfig.alphafoldBaseUrl;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Fetch the AlphaFold prediction for a UniProt accession. Returns `null` when
|
|
19
|
+
* no model exists (404); a malformed accession (400) bubbles as `InvalidParams`.
|
|
20
|
+
*/
|
|
21
|
+
async getPrediction(accession, ctx) {
|
|
22
|
+
const url = `${this.baseUrl}/api/prediction/${encodeURIComponent(accession.toUpperCase())}`;
|
|
23
|
+
let raw;
|
|
24
|
+
try {
|
|
25
|
+
raw = await fetchJson(url, ctx, {
|
|
26
|
+
operation: 'AlphaFoldService.getPrediction',
|
|
27
|
+
label: 'AlphaFold DB',
|
|
28
|
+
baseDelayMs: 300,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
if (err instanceof McpError && err.code === JsonRpcErrorCode.NotFound)
|
|
33
|
+
return null;
|
|
34
|
+
throw err;
|
|
35
|
+
}
|
|
36
|
+
const first = raw[0];
|
|
37
|
+
if (!first)
|
|
38
|
+
return null;
|
|
39
|
+
return normalizePrediction(accession.toUpperCase(), first);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function normalizePrediction(accession, raw) {
|
|
43
|
+
const buckets = confidenceBuckets(raw);
|
|
44
|
+
return {
|
|
45
|
+
uniprotAccession: raw.uniprotAccession ?? accession,
|
|
46
|
+
...(raw.entryId ? { entryId: raw.entryId } : {}),
|
|
47
|
+
...(typeof raw.globalMetricValue === 'number' ? { meanPlddt: raw.globalMetricValue } : {}),
|
|
48
|
+
...(buckets ? { confidenceBuckets: buckets } : {}),
|
|
49
|
+
...(raw.cifUrl ? { cifUrl: raw.cifUrl } : {}),
|
|
50
|
+
...(raw.pdbUrl ? { pdbUrl: raw.pdbUrl } : {}),
|
|
51
|
+
...(raw.bcifUrl ? { bcifUrl: raw.bcifUrl } : {}),
|
|
52
|
+
...(raw.paeImageUrl ? { paeImageUrl: raw.paeImageUrl } : {}),
|
|
53
|
+
...(raw.paeDocUrl ? { paeDocUrl: raw.paeDocUrl } : {}),
|
|
54
|
+
...(typeof raw.latestVersion === 'number' ? { modelVersion: raw.latestVersion } : {}),
|
|
55
|
+
...(raw.organismScientificName ? { organism: raw.organismScientificName } : {}),
|
|
56
|
+
...(raw.uniprotDescription ? { uniprotDescription: raw.uniprotDescription } : {}),
|
|
57
|
+
...(raw.uniprotSequence ? { sequenceLength: raw.uniprotSequence.length } : {}),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function confidenceBuckets(raw) {
|
|
61
|
+
const { fractionPlddtVeryLow, fractionPlddtLow, fractionPlddtConfident, fractionPlddtVeryHigh } = raw;
|
|
62
|
+
if (typeof fractionPlddtVeryLow !== 'number' &&
|
|
63
|
+
typeof fractionPlddtLow !== 'number' &&
|
|
64
|
+
typeof fractionPlddtConfident !== 'number' &&
|
|
65
|
+
typeof fractionPlddtVeryHigh !== 'number') {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
veryLow: fractionPlddtVeryLow ?? 0,
|
|
70
|
+
low: fractionPlddtLow ?? 0,
|
|
71
|
+
confident: fractionPlddtConfident ?? 0,
|
|
72
|
+
veryHigh: fractionPlddtVeryHigh ?? 0,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
let _service;
|
|
76
|
+
export function initAlphaFoldService(config, storage, serverConfig) {
|
|
77
|
+
_service = new AlphaFoldService(config, storage, serverConfig);
|
|
78
|
+
}
|
|
79
|
+
export function getAlphaFoldService() {
|
|
80
|
+
if (!_service)
|
|
81
|
+
throw new Error('AlphaFoldService not initialized — call initAlphaFoldService() in setup()');
|
|
82
|
+
return _service;
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=alphafold-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"alphafold-service.js","sourceRoot":"","sources":["../../../src/services/alphafold/alphafold-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview 3D-Beacons service — wraps the EMBL-EBI federated structure API
|
|
3
|
+
* (`/uniprot/summary/{accession}.json`), which fronts experimental (PDBe) and
|
|
4
|
+
* predicted (AlphaFold DB, AlphaFill, SWISS-MODEL, BFVD, …) models behind one
|
|
5
|
+
* UniProt-keyed interface. Backs the predicted half of `protein_search_structures`
|
|
6
|
+
* and `best_available` resolution in `protein_get_structure`. A 404 returns an
|
|
7
|
+
* empty object upstream — treated as "no models", distinct from a transport error.
|
|
8
|
+
* @module services/beacons/beacons-service
|
|
9
|
+
*/
|
|
10
|
+
import type { Context } from '@cyanheads/mcp-ts-core';
|
|
11
|
+
import type { AppConfig } from '@cyanheads/mcp-ts-core/config';
|
|
12
|
+
import type { StorageService } from '@cyanheads/mcp-ts-core/storage';
|
|
13
|
+
import type { ServerConfig } from '../../config/server-config.js';
|
|
14
|
+
/** A single federated model summary. */
|
|
15
|
+
export interface BeaconModel {
|
|
16
|
+
/** Average local confidence score (predicted models only). */
|
|
17
|
+
confidenceAvgLocalScore?: number;
|
|
18
|
+
/** Confidence metric type (e.g. "pLDDT", "QMEANDisCo") when present. */
|
|
19
|
+
confidenceType?: string;
|
|
20
|
+
/** UniProt residue coverage fraction (0–1) the model spans. */
|
|
21
|
+
coverage?: number;
|
|
22
|
+
/** Experimental method (experimental models only). */
|
|
23
|
+
experimentalMethod?: string;
|
|
24
|
+
/** `EXPERIMENTALLY DETERMINED`, `AB-INITIO`, `TEMPLATE-BASED`, … */
|
|
25
|
+
modelCategory?: string;
|
|
26
|
+
/** Provider model identifier. */
|
|
27
|
+
modelIdentifier: string;
|
|
28
|
+
/** Coordinate file URL. */
|
|
29
|
+
modelUrl?: string;
|
|
30
|
+
/** Originating provider (e.g. "AlphaFold DB", "PDBe", "SWISS-MODEL"). */
|
|
31
|
+
provider?: string;
|
|
32
|
+
/** Resolution in Å (experimental models only). */
|
|
33
|
+
resolution?: number;
|
|
34
|
+
}
|
|
35
|
+
/** A UniProt accession's federated model set. */
|
|
36
|
+
export interface BeaconSummary {
|
|
37
|
+
/** The queried UniProt accession. */
|
|
38
|
+
accession: string;
|
|
39
|
+
/** Whether the upstream returned any entry at all. */
|
|
40
|
+
found: boolean;
|
|
41
|
+
/** Federated models, experimental and predicted. */
|
|
42
|
+
models: BeaconModel[];
|
|
43
|
+
}
|
|
44
|
+
export declare class BeaconsService {
|
|
45
|
+
private readonly baseUrl;
|
|
46
|
+
constructor(_config: AppConfig, _storage: StorageService, serverConfig: ServerConfig);
|
|
47
|
+
/** Fetch the federated model summary for a UniProt accession. */
|
|
48
|
+
getSummary(accession: string, ctx: Context): Promise<BeaconSummary>;
|
|
49
|
+
}
|
|
50
|
+
export declare function initBeaconsService(config: AppConfig, storage: StorageService, serverConfig: ServerConfig): void;
|
|
51
|
+
export declare function getBeaconsService(): BeaconsService;
|
|
52
|
+
//# sourceMappingURL=beacons-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"beacons-service.d.ts","sourceRoot":"","sources":["../../../src/services/beacons/beacons-service.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAG9D,wCAAwC;AACxC,MAAM,WAAW,WAAW;IAC1B,8DAA8D;IAC9D,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,wEAAwE;IACxE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sDAAsD;IACtD,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,oEAAoE;IACpE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iCAAiC;IACjC,eAAe,EAAE,MAAM,CAAC;IACxB,2BAA2B;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yEAAyE;IACzE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kDAAkD;IAClD,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,iDAAiD;AACjD,MAAM,WAAW,aAAa;IAC5B,qCAAqC;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,sDAAsD;IACtD,KAAK,EAAE,OAAO,CAAC;IACf,oDAAoD;IACpD,MAAM,EAAE,WAAW,EAAE,CAAC;CACvB;AAmBD,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAErB,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY;IAIpF,iEAAiE;IAC3D,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,aAAa,CAAC;CAoB1E;AAoBD,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,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview 3D-Beacons service — wraps the EMBL-EBI federated structure API
|
|
3
|
+
* (`/uniprot/summary/{accession}.json`), which fronts experimental (PDBe) and
|
|
4
|
+
* predicted (AlphaFold DB, AlphaFill, SWISS-MODEL, BFVD, …) models behind one
|
|
5
|
+
* UniProt-keyed interface. Backs the predicted half of `protein_search_structures`
|
|
6
|
+
* and `best_available` resolution in `protein_get_structure`. A 404 returns an
|
|
7
|
+
* empty object upstream — treated as "no models", distinct from a transport error.
|
|
8
|
+
* @module services/beacons/beacons-service
|
|
9
|
+
*/
|
|
10
|
+
import { fetchResponse, parseJson } from '../shared/http.js';
|
|
11
|
+
export class BeaconsService {
|
|
12
|
+
baseUrl;
|
|
13
|
+
constructor(_config, _storage, serverConfig) {
|
|
14
|
+
this.baseUrl = serverConfig.beaconsBaseUrl;
|
|
15
|
+
}
|
|
16
|
+
/** Fetch the federated model summary for a UniProt accession. */
|
|
17
|
+
async getSummary(accession, ctx) {
|
|
18
|
+
const acc = accession.toUpperCase();
|
|
19
|
+
const url = `${this.baseUrl}/uniprot/summary/${encodeURIComponent(acc)}.json`;
|
|
20
|
+
const res = await fetchResponse(url, ctx, {
|
|
21
|
+
operation: 'BeaconsService.getSummary',
|
|
22
|
+
baseDelayMs: 500,
|
|
23
|
+
});
|
|
24
|
+
if (res.status === 404)
|
|
25
|
+
return { accession: acc, found: false, models: [] };
|
|
26
|
+
if (!res.ok) {
|
|
27
|
+
// Surface non-404 failures via the framework's status mapping by re-reading the body.
|
|
28
|
+
throw new Error(`3D-Beacons returned HTTP ${res.status} for ${acc}`);
|
|
29
|
+
}
|
|
30
|
+
const text = await res.text();
|
|
31
|
+
const raw = parseJson(text, '3D-Beacons');
|
|
32
|
+
const models = (raw.structures ?? [])
|
|
33
|
+
.map((s) => s.summary)
|
|
34
|
+
.filter((s) => s != null && !!s.model_identifier)
|
|
35
|
+
.map(normalizeModel);
|
|
36
|
+
return { accession: raw.uniprot_entry?.ac ?? acc, found: models.length > 0, models };
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function normalizeModel(raw) {
|
|
40
|
+
return {
|
|
41
|
+
modelIdentifier: raw.model_identifier,
|
|
42
|
+
...(raw.model_category ? { modelCategory: raw.model_category } : {}),
|
|
43
|
+
...(raw.provider ? { provider: raw.provider } : {}),
|
|
44
|
+
...(raw.model_url ? { modelUrl: raw.model_url } : {}),
|
|
45
|
+
...(typeof raw.coverage === 'number' ? { coverage: raw.coverage } : {}),
|
|
46
|
+
...(typeof raw.resolution === 'number' ? { resolution: raw.resolution } : {}),
|
|
47
|
+
...(raw.experimental_method ? { experimentalMethod: raw.experimental_method } : {}),
|
|
48
|
+
...(raw.confidence_type ? { confidenceType: raw.confidence_type } : {}),
|
|
49
|
+
...(typeof raw.confidence_avg_local_score === 'number'
|
|
50
|
+
? { confidenceAvgLocalScore: raw.confidence_avg_local_score }
|
|
51
|
+
: {}),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
let _service;
|
|
55
|
+
export function initBeaconsService(config, storage, serverConfig) {
|
|
56
|
+
_service = new BeaconsService(config, storage, serverConfig);
|
|
57
|
+
}
|
|
58
|
+
export function getBeaconsService() {
|
|
59
|
+
if (!_service)
|
|
60
|
+
throw new Error('BeaconsService not initialized — call initBeaconsService() in setup()');
|
|
61
|
+
return _service;
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=beacons-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"beacons-service.js","sourceRoot":"","sources":["../../../src/services/beacons/beacons-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Foldseek service — wraps the public Foldseek structural-search
|
|
3
|
+
* ticket API (`/api/ticket` → poll `/api/ticket/{id}` → `/api/result/{id}/{n}`).
|
|
4
|
+
* Submits a query coordinate file against experimental + predicted databases and
|
|
5
|
+
* returns fold-similarity hits. Backs `protein_find_similar` (`by: structure`).
|
|
6
|
+
* Async: the ticket status field drives completion, not the HTTP code.
|
|
7
|
+
* @module services/foldseek/foldseek-service
|
|
8
|
+
*/
|
|
9
|
+
import type { Context } from '@cyanheads/mcp-ts-core';
|
|
10
|
+
import type { AppConfig } from '@cyanheads/mcp-ts-core/config';
|
|
11
|
+
import type { StorageService } from '@cyanheads/mcp-ts-core/storage';
|
|
12
|
+
import type { ServerConfig } from '../../config/server-config.js';
|
|
13
|
+
/** A single fold-similarity hit. */
|
|
14
|
+
export interface FoldseekHit {
|
|
15
|
+
/** Aligned length. */
|
|
16
|
+
alignmentLength?: number;
|
|
17
|
+
/** Chain ID (pdb targets). */
|
|
18
|
+
chain?: string;
|
|
19
|
+
/** Source database the hit came from. */
|
|
20
|
+
database: string;
|
|
21
|
+
/** Alignment E-value. */
|
|
22
|
+
evalue?: number;
|
|
23
|
+
/** PDB entry ID (pdb targets). */
|
|
24
|
+
pdbId?: string;
|
|
25
|
+
/** Match probability (0–1). */
|
|
26
|
+
probability?: number;
|
|
27
|
+
/** Alignment bit score. */
|
|
28
|
+
score?: number;
|
|
29
|
+
/** Sequence identity (0–1) over the alignment. */
|
|
30
|
+
sequenceIdentity?: number;
|
|
31
|
+
/** Raw target identifier as returned by Foldseek. */
|
|
32
|
+
target: string;
|
|
33
|
+
/** Resolved target kind. */
|
|
34
|
+
targetType: 'pdb' | 'alphafold' | 'other';
|
|
35
|
+
/** UniProt accession (AlphaFold targets). */
|
|
36
|
+
uniprotAccession?: string;
|
|
37
|
+
}
|
|
38
|
+
/** Outcome of a structural search. */
|
|
39
|
+
export type FoldseekOutcome = {
|
|
40
|
+
status: 'complete';
|
|
41
|
+
ticketId: string;
|
|
42
|
+
hits: FoldseekHit[];
|
|
43
|
+
} | {
|
|
44
|
+
status: 'computing';
|
|
45
|
+
ticketId: string;
|
|
46
|
+
} | {
|
|
47
|
+
status: 'failed';
|
|
48
|
+
error: string;
|
|
49
|
+
};
|
|
50
|
+
export declare class FoldseekService {
|
|
51
|
+
private readonly baseUrl;
|
|
52
|
+
constructor(_config: AppConfig, _storage: StorageService, serverConfig: ServerConfig);
|
|
53
|
+
/**
|
|
54
|
+
* Submit a structure and bounded-poll for fold-similarity hits. Never throws —
|
|
55
|
+
* a submit/poll failure degrades to `{ status: 'failed' }`.
|
|
56
|
+
*/
|
|
57
|
+
search(params: {
|
|
58
|
+
fileContent: string;
|
|
59
|
+
fileName: string;
|
|
60
|
+
databases: string[];
|
|
61
|
+
mode: string;
|
|
62
|
+
limit: number;
|
|
63
|
+
timeoutMs: number;
|
|
64
|
+
}, ctx: Context): Promise<FoldseekOutcome>;
|
|
65
|
+
private submit;
|
|
66
|
+
private pollTicket;
|
|
67
|
+
private fetchResults;
|
|
68
|
+
}
|
|
69
|
+
export declare function initFoldseekService(config: AppConfig, storage: StorageService, serverConfig: ServerConfig): void;
|
|
70
|
+
export declare function getFoldseekService(): FoldseekService;
|
|
71
|
+
//# sourceMappingURL=foldseek-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"foldseek-service.d.ts","sourceRoot":"","sources":["../../../src/services/foldseek/foldseek-service.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAI9D,oCAAoC;AACpC,MAAM,WAAW,WAAW;IAC1B,sBAAsB;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,8BAA8B;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yCAAyC;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,yBAAyB;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kCAAkC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+BAA+B;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2BAA2B;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kDAAkD;IAClD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,qDAAqD;IACrD,MAAM,EAAE,MAAM,CAAC;IACf,4BAA4B;IAC5B,UAAU,EAAE,KAAK,GAAG,WAAW,GAAG,OAAO,CAAC;IAC1C,6CAA6C;IAC7C,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,sCAAsC;AACtC,MAAM,MAAM,eAAe,GACvB;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,WAAW,EAAE,CAAA;CAAE,GAC7D;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GACzC;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAExC,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAErB,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY;IAIpF;;;OAGG;IACG,MAAM,CACV,MAAM,EAAE;QACN,WAAW,EAAE,MAAM,CAAC;QACpB,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,EAAE,CAAC;QACpB,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,SAAS,EAAE,MAAM,CAAC;KACnB,EACD,GAAG,EAAE,OAAO,GACX,OAAO,CAAC,eAAe,CAAC;YA8Bb,MAAM;YA4BN,UAAU;YAsBV,YAAY;CAuB3B;AA0CD,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,cAAc,EACvB,YAAY,EAAE,YAAY,GACzB,IAAI,CAEN;AAED,wBAAgB,kBAAkB,IAAI,eAAe,CAIpD"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Foldseek service — wraps the public Foldseek structural-search
|
|
3
|
+
* ticket API (`/api/ticket` → poll `/api/ticket/{id}` → `/api/result/{id}/{n}`).
|
|
4
|
+
* Submits a query coordinate file against experimental + predicted databases and
|
|
5
|
+
* returns fold-similarity hits. Backs `protein_find_similar` (`by: structure`).
|
|
6
|
+
* Async: the ticket status field drives completion, not the HTTP code.
|
|
7
|
+
* @module services/foldseek/foldseek-service
|
|
8
|
+
*/
|
|
9
|
+
import { withAsyncPoll } from '../shared/async.js';
|
|
10
|
+
import { fetchJson } from '../shared/http.js';
|
|
11
|
+
export class FoldseekService {
|
|
12
|
+
baseUrl;
|
|
13
|
+
constructor(_config, _storage, serverConfig) {
|
|
14
|
+
this.baseUrl = serverConfig.foldseekBaseUrl;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Submit a structure and bounded-poll for fold-similarity hits. Never throws —
|
|
18
|
+
* a submit/poll failure degrades to `{ status: 'failed' }`.
|
|
19
|
+
*/
|
|
20
|
+
async search(params, ctx) {
|
|
21
|
+
let ticketId;
|
|
22
|
+
try {
|
|
23
|
+
ticketId = await this.submit(params.fileContent, params.fileName, params.databases, params.mode, ctx);
|
|
24
|
+
}
|
|
25
|
+
catch (err) {
|
|
26
|
+
return { status: 'failed', error: err instanceof Error ? err.message : String(err) };
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
const outcome = await withAsyncPoll({
|
|
30
|
+
step: () => this.pollTicket(ticketId, params.limit, ctx),
|
|
31
|
+
timeoutMs: params.timeoutMs,
|
|
32
|
+
ctx,
|
|
33
|
+
intervalMs: 1500,
|
|
34
|
+
maxIntervalMs: 2500,
|
|
35
|
+
});
|
|
36
|
+
return outcome.status === 'complete'
|
|
37
|
+
? { status: 'complete', ticketId, hits: outcome.value }
|
|
38
|
+
: { status: 'computing', ticketId };
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
return { status: 'failed', error: err instanceof Error ? err.message : String(err) };
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async submit(fileContent, fileName, databases, mode, ctx) {
|
|
45
|
+
const form = new FormData();
|
|
46
|
+
form.append('q', new Blob([fileContent], { type: 'chemical/x-pdb' }), fileName);
|
|
47
|
+
for (const db of databases)
|
|
48
|
+
form.append('database[]', db);
|
|
49
|
+
form.append('mode', mode);
|
|
50
|
+
// fetchWithTimeout sets the multipart boundary header from the FormData body.
|
|
51
|
+
const raw = await fetchJson(`${this.baseUrl}/api/ticket`, ctx, {
|
|
52
|
+
method: 'POST',
|
|
53
|
+
body: form,
|
|
54
|
+
operation: 'FoldseekService.submit',
|
|
55
|
+
label: 'Foldseek',
|
|
56
|
+
baseDelayMs: 1000,
|
|
57
|
+
maxRetries: 1,
|
|
58
|
+
});
|
|
59
|
+
if (!raw.id)
|
|
60
|
+
throw new Error('Foldseek did not return a ticket ID');
|
|
61
|
+
return raw.id;
|
|
62
|
+
}
|
|
63
|
+
async pollTicket(ticketId, limit, ctx) {
|
|
64
|
+
const ticket = await fetchJson(`${this.baseUrl}/api/ticket/${encodeURIComponent(ticketId)}`, ctx, {
|
|
65
|
+
operation: 'FoldseekService.pollTicket',
|
|
66
|
+
label: 'Foldseek',
|
|
67
|
+
timeoutMs: 15_000,
|
|
68
|
+
baseDelayMs: 400,
|
|
69
|
+
});
|
|
70
|
+
const status = (ticket.status ?? '').toUpperCase();
|
|
71
|
+
if (status === 'ERROR')
|
|
72
|
+
throw new Error('Foldseek reported an error processing the structure');
|
|
73
|
+
if (status !== 'COMPLETE')
|
|
74
|
+
return { ready: false };
|
|
75
|
+
const hits = await this.fetchResults(ticketId, limit, ctx);
|
|
76
|
+
return { ready: true, value: hits };
|
|
77
|
+
}
|
|
78
|
+
async fetchResults(ticketId, limit, ctx) {
|
|
79
|
+
const raw = await fetchJson(`${this.baseUrl}/api/result/${encodeURIComponent(ticketId)}/0`, ctx, { operation: 'FoldseekService.fetchResults', label: 'Foldseek', baseDelayMs: 400 });
|
|
80
|
+
const hits = [];
|
|
81
|
+
for (const dbResult of raw.results ?? []) {
|
|
82
|
+
const db = dbResult.db ?? 'unknown';
|
|
83
|
+
for (const group of dbResult.alignments ?? []) {
|
|
84
|
+
for (const aln of group ?? []) {
|
|
85
|
+
if (!aln.target)
|
|
86
|
+
continue;
|
|
87
|
+
hits.push(normalizeHit(aln, db));
|
|
88
|
+
if (hits.length >= limit)
|
|
89
|
+
return hits;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return hits;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// ─── Normalizers ──────────────────────────────────────────────────────────────
|
|
97
|
+
function normalizeHit(aln, database) {
|
|
98
|
+
const target = aln.target;
|
|
99
|
+
const hit = { target, database, ...parseTarget(target) };
|
|
100
|
+
if (typeof aln.seqId === 'number')
|
|
101
|
+
hit.sequenceIdentity = aln.seqId;
|
|
102
|
+
if (typeof aln.alnLength === 'number')
|
|
103
|
+
hit.alignmentLength = aln.alnLength;
|
|
104
|
+
if (typeof aln.prob === 'number')
|
|
105
|
+
hit.probability = aln.prob;
|
|
106
|
+
if (typeof aln.eval === 'number')
|
|
107
|
+
hit.evalue = aln.eval;
|
|
108
|
+
if (typeof aln.score === 'number')
|
|
109
|
+
hit.score = aln.score;
|
|
110
|
+
return hit;
|
|
111
|
+
}
|
|
112
|
+
/** Resolve a Foldseek target header to an addressable identifier. */
|
|
113
|
+
function parseTarget(target) {
|
|
114
|
+
const af = /^AF-([A-Za-z0-9]+)-F\d+/.exec(target);
|
|
115
|
+
if (af)
|
|
116
|
+
return { targetType: 'alphafold', uniprotAccession: af[1] };
|
|
117
|
+
const pdb = /^(\d[A-Za-z0-9]{3})[_-]([A-Za-z0-9]+)/.exec(target);
|
|
118
|
+
if (pdb)
|
|
119
|
+
return { targetType: 'pdb', pdbId: pdb[1].toUpperCase(), chain: pdb[2] };
|
|
120
|
+
return { targetType: 'other' };
|
|
121
|
+
}
|
|
122
|
+
let _service;
|
|
123
|
+
export function initFoldseekService(config, storage, serverConfig) {
|
|
124
|
+
_service = new FoldseekService(config, storage, serverConfig);
|
|
125
|
+
}
|
|
126
|
+
export function getFoldseekService() {
|
|
127
|
+
if (!_service)
|
|
128
|
+
throw new Error('FoldseekService not initialized — call initFoldseekService() in setup()');
|
|
129
|
+
return _service;
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=foldseek-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"foldseek-service.js","sourceRoot":"","sources":["../../../src/services/foldseek/foldseek-service.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,OAAO,EAAiB,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAClE,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAkC9C,MAAM,OAAO,eAAe;IACT,OAAO,CAAS;IAEjC,YAAY,OAAkB,EAAE,QAAwB,EAAE,YAA0B;QAClF,IAAI,CAAC,OAAO,GAAG,YAAY,CAAC,eAAe,CAAC;IAC9C,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,MAAM,CACV,MAOC,EACD,GAAY;QAEZ,IAAI,QAAgB,CAAC;QACrB,IAAI,CAAC;YACH,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAC1B,MAAM,CAAC,WAAW,EAClB,MAAM,CAAC,QAAQ,EACf,MAAM,CAAC,SAAS,EAChB,MAAM,CAAC,IAAI,EACX,GAAG,CACJ,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;QACvF,CAAC;QAED,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,aAAa,CAAgB;gBACjD,IAAI,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC;gBACxD,SAAS,EAAE,MAAM,CAAC,SAAS;gBAC3B,GAAG;gBACH,UAAU,EAAE,IAAI;gBAChB,aAAa,EAAE,IAAI;aACpB,CAAC,CAAC;YACH,OAAO,OAAO,CAAC,MAAM,KAAK,UAAU;gBAClC,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAC,KAAK,EAAE;gBACvD,CAAC,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC;QACxC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;QACvF,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,MAAM,CAClB,WAAmB,EACnB,QAAgB,EAChB,SAAmB,EACnB,IAAY,EACZ,GAAY;QAEZ,MAAM,IAAI,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC5B,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,CAAC,WAAW,CAAC,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE,CAAC,EAAE,QAAQ,CAAC,CAAC;QAChF,KAAK,MAAM,EAAE,IAAI,SAAS;YAAE,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;QAC1D,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAC1B,8EAA8E;QAC9E,MAAM,GAAG,GAAG,MAAM,SAAS,CACzB,GAAG,IAAI,CAAC,OAAO,aAAa,EAC5B,GAAG,EACH;YACE,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,IAAI;YACV,SAAS,EAAE,wBAAwB;YACnC,KAAK,EAAE,UAAU;YACjB,WAAW,EAAE,IAAI;YACjB,UAAU,EAAE,CAAC;SACd,CACF,CAAC;QACF,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;QACpE,OAAO,GAAG,CAAC,EAAE,CAAC;IAChB,CAAC;IAEO,KAAK,CAAC,UAAU,CACtB,QAAgB,EAChB,KAAa,EACb,GAAY;QAEZ,MAAM,MAAM,GAAG,MAAM,SAAS,CAC5B,GAAG,IAAI,CAAC,OAAO,eAAe,kBAAkB,CAAC,QAAQ,CAAC,EAAE,EAC5D,GAAG,EACH;YACE,SAAS,EAAE,4BAA4B;YACvC,KAAK,EAAE,UAAU;YACjB,SAAS,EAAE,MAAM;YACjB,WAAW,EAAE,GAAG;SACjB,CACF,CAAC;QACF,MAAM,MAAM,GAAG,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;QACnD,IAAI,MAAM,KAAK,OAAO;YAAE,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;QAC/F,IAAI,MAAM,KAAK,UAAU;YAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;QACnD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;QAC3D,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IACtC,CAAC;IAEO,KAAK,CAAC,YAAY,CACxB,QAAgB,EAChB,KAAa,EACb,GAAY;QAEZ,MAAM,GAAG,GAAG,MAAM,SAAS,CACzB,GAAG,IAAI,CAAC,OAAO,eAAe,kBAAkB,CAAC,QAAQ,CAAC,IAAI,EAC9D,GAAG,EACH,EAAE,SAAS,EAAE,8BAA8B,EAAE,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,GAAG,EAAE,CACnF,CAAC;QACF,MAAM,IAAI,GAAkB,EAAE,CAAC;QAC/B,KAAK,MAAM,QAAQ,IAAI,GAAG,CAAC,OAAO,IAAI,EAAE,EAAE,CAAC;YACzC,MAAM,EAAE,GAAG,QAAQ,CAAC,EAAE,IAAI,SAAS,CAAC;YACpC,KAAK,MAAM,KAAK,IAAI,QAAQ,CAAC,UAAU,IAAI,EAAE,EAAE,CAAC;gBAC9C,KAAK,MAAM,GAAG,IAAI,KAAK,IAAI,EAAE,EAAE,CAAC;oBAC9B,IAAI,CAAC,GAAG,CAAC,MAAM;wBAAE,SAAS;oBAC1B,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC;oBACjC,IAAI,IAAI,CAAC,MAAM,IAAI,KAAK;wBAAE,OAAO,IAAI,CAAC;gBACxC,CAAC;YACH,CAAC;QACH,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;CACF;AAED,iFAAiF;AAEjF,SAAS,YAAY,CAAC,GAAiB,EAAE,QAAgB;IACvD,MAAM,MAAM,GAAG,GAAG,CAAC,MAAgB,CAAC;IACpC,MAAM,GAAG,GAAgB,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC;IACtE,IAAI,OAAO,GAAG,CAAC,KAAK,KAAK,QAAQ;QAAE,GAAG,CAAC,gBAAgB,GAAG,GAAG,CAAC,KAAK,CAAC;IACpE,IAAI,OAAO,GAAG,CAAC,SAAS,KAAK,QAAQ;QAAE,GAAG,CAAC,eAAe,GAAG,GAAG,CAAC,SAAS,CAAC;IAC3E,IAAI,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ;QAAE,GAAG,CAAC,WAAW,GAAG,GAAG,CAAC,IAAI,CAAC;IAC7D,IAAI,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ;QAAE,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC;IACxD,IAAI,OAAO,GAAG,CAAC,KAAK,KAAK,QAAQ;QAAE,GAAG,CAAC,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC;IACzD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,qEAAqE;AACrE,SAAS,WAAW,CAClB,MAAc;IAEd,MAAM,EAAE,GAAG,yBAAyB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAClD,IAAI,EAAE;QAAE,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,gBAAgB,EAAE,EAAE,CAAC,CAAC,CAAW,EAAE,CAAC;IAC9E,MAAM,GAAG,GAAG,uCAAuC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACjE,IAAI,GAAG;QACL,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,EAAG,GAAG,CAAC,CAAC,CAAY,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC,CAAW,EAAE,CAAC;IACjG,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC;AACjC,CAAC;AAeD,IAAI,QAAqC,CAAC;AAE1C,MAAM,UAAU,mBAAmB,CACjC,MAAiB,EACjB,OAAuB,EACvB,YAA0B;IAE1B,QAAQ,GAAG,IAAI,eAAe,CAAC,MAAM,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC;AAChE,CAAC;AAED,MAAM,UAAU,kBAAkB;IAChC,IAAI,CAAC,QAAQ;QACX,MAAM,IAAI,KAAK,CAAC,yEAAyE,CAAC,CAAC;IAC7F,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|