@cyanheads/interpro-mcp-server 0.1.3 → 0.2.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.
Files changed (71) hide show
  1. package/AGENTS.md +38 -13
  2. package/CLAUDE.md +38 -13
  3. package/Dockerfile +9 -1
  4. package/README.md +82 -123
  5. package/changelog/0.2.x/0.2.0.md +31 -0
  6. package/dist/config/server-config.d.ts +13 -0
  7. package/dist/config/server-config.d.ts.map +1 -0
  8. package/dist/config/server-config.js +19 -0
  9. package/dist/config/server-config.js.map +1 -0
  10. package/dist/index.js +11 -1
  11. package/dist/index.js.map +1 -1
  12. package/dist/mcp-server/tools/definitions/interpro-find-proteins-by-domain.tool.d.ts +45 -0
  13. package/dist/mcp-server/tools/definitions/interpro-find-proteins-by-domain.tool.d.ts.map +1 -1
  14. package/dist/mcp-server/tools/definitions/interpro-find-proteins-by-domain.tool.js +37 -14
  15. package/dist/mcp-server/tools/definitions/interpro-find-proteins-by-domain.tool.js.map +1 -1
  16. package/dist/mcp-server/tools/definitions/interpro-get-protein-domains.tool.d.ts +1 -0
  17. package/dist/mcp-server/tools/definitions/interpro-get-protein-domains.tool.d.ts.map +1 -1
  18. package/dist/mcp-server/tools/definitions/interpro-get-protein-domains.tool.js +1 -1
  19. package/dist/mcp-server/tools/definitions/interpro-get-protein-domains.tool.js.map +1 -1
  20. package/dist/mcp-server/tools/definitions/interpro-get-scan-result.tool.d.ts +133 -0
  21. package/dist/mcp-server/tools/definitions/interpro-get-scan-result.tool.d.ts.map +1 -0
  22. package/dist/mcp-server/tools/definitions/interpro-get-scan-result.tool.js +128 -0
  23. package/dist/mcp-server/tools/definitions/interpro-get-scan-result.tool.js.map +1 -0
  24. package/dist/mcp-server/tools/definitions/interpro-get-structure-domains.tool.d.ts +171 -0
  25. package/dist/mcp-server/tools/definitions/interpro-get-structure-domains.tool.d.ts.map +1 -0
  26. package/dist/mcp-server/tools/definitions/interpro-get-structure-domains.tool.js +290 -0
  27. package/dist/mcp-server/tools/definitions/interpro-get-structure-domains.tool.js.map +1 -0
  28. package/dist/mcp-server/tools/definitions/interpro-list-reference.tool.js +1 -1
  29. package/dist/mcp-server/tools/definitions/interpro-list-reference.tool.js.map +1 -1
  30. package/dist/mcp-server/tools/definitions/interpro-scan-sequence.tool.d.ts +168 -0
  31. package/dist/mcp-server/tools/definitions/interpro-scan-sequence.tool.d.ts.map +1 -0
  32. package/dist/mcp-server/tools/definitions/interpro-scan-sequence.tool.js +187 -0
  33. package/dist/mcp-server/tools/definitions/interpro-scan-sequence.tool.js.map +1 -0
  34. package/dist/services/interpro/http-adapter.d.ts +11 -2
  35. package/dist/services/interpro/http-adapter.d.ts.map +1 -1
  36. package/dist/services/interpro/http-adapter.js +67 -9
  37. package/dist/services/interpro/http-adapter.js.map +1 -1
  38. package/dist/services/interpro/interpro-service.d.ts +1 -1
  39. package/dist/services/interpro/interpro-service.d.ts.map +1 -1
  40. package/dist/services/interpro/interpro-service.js +6 -24
  41. package/dist/services/interpro/interpro-service.js.map +1 -1
  42. package/dist/services/interpro/reference.d.ts.map +1 -1
  43. package/dist/services/interpro/reference.js +23 -2
  44. package/dist/services/interpro/reference.js.map +1 -1
  45. package/dist/services/interpro/schemas.d.ts +1 -0
  46. package/dist/services/interpro/schemas.d.ts.map +1 -1
  47. package/dist/services/interpro/schemas.js +5 -0
  48. package/dist/services/interpro/schemas.js.map +1 -1
  49. package/dist/services/interpro/structure-schemas.d.ts +116 -0
  50. package/dist/services/interpro/structure-schemas.d.ts.map +1 -0
  51. package/dist/services/interpro/structure-schemas.js +74 -0
  52. package/dist/services/interpro/structure-schemas.js.map +1 -0
  53. package/dist/services/sequence-scan/scan-output.d.ts +94 -0
  54. package/dist/services/sequence-scan/scan-output.d.ts.map +1 -0
  55. package/dist/services/sequence-scan/scan-output.js +174 -0
  56. package/dist/services/sequence-scan/scan-output.js.map +1 -0
  57. package/dist/services/sequence-scan/scan-schemas.d.ts +112 -0
  58. package/dist/services/sequence-scan/scan-schemas.d.ts.map +1 -0
  59. package/dist/services/sequence-scan/scan-schemas.js +97 -0
  60. package/dist/services/sequence-scan/scan-schemas.js.map +1 -0
  61. package/dist/services/sequence-scan/sequence-scan-service.d.ts +119 -0
  62. package/dist/services/sequence-scan/sequence-scan-service.d.ts.map +1 -0
  63. package/dist/services/sequence-scan/sequence-scan-service.js +453 -0
  64. package/dist/services/sequence-scan/sequence-scan-service.js.map +1 -0
  65. package/dist/services/sequence-scan/sequence.d.ts +32 -0
  66. package/dist/services/sequence-scan/sequence.d.ts.map +1 -0
  67. package/dist/services/sequence-scan/sequence.js +63 -0
  68. package/dist/services/sequence-scan/sequence.js.map +1 -0
  69. package/package.json +10 -7
  70. package/server.json +18 -4
  71. package/skills/interpro-guide/SKILL.md +72 -0
@@ -0,0 +1,174 @@
1
+ /**
2
+ * @fileoverview Output schema and budget-bounded segments of whole match records shared by the
3
+ * scan and scan-result tools, with cursors scoped to the sequence MD5 or the job ID.
4
+ * @module services/sequence-scan/scan-output
5
+ */
6
+ import { z } from '@cyanheads/mcp-ts-core';
7
+ import { validationError } from '@cyanheads/mcp-ts-core/errors';
8
+ import { BUDGET_LABEL, fitWithinBudget } from '../interpro/budget.js';
9
+ import { compareText, decodePositionCursor, encodePositionCursor, } from '../interpro/cursors.js';
10
+ import { ScanMatchSchema } from './scan-schemas.js';
11
+ const PRECALCULATED_OPERATION = 'interpro_scan_sequence.precalculated';
12
+ const JOB_OPERATION = 'interpro_get_scan_result.job';
13
+ const PositionSchema = z
14
+ .object({ offset: z.number().int().positive(), total: z.number().int().positive() })
15
+ .strict();
16
+ /** Output fields both tools return. */
17
+ export const ScanOutputSchema = z.object({
18
+ source: z
19
+ .enum(['precalculated', 'interproscan'])
20
+ .describe('Which path produced the result: precalculated matches from the InterPro Matches API, or an InterProScan 6 job run through the EBI Job Dispatcher.'),
21
+ status: z
22
+ .enum(['finished', 'queued', 'running', 'submitted'])
23
+ .describe('finished: matches are present. queued or running: the job is pending; read it later with interpro_get_scan_result. submitted: accepted in this call and not yet polled.'),
24
+ jobId: z
25
+ .string()
26
+ .optional()
27
+ .describe('InterProScan job ID on the interproscan path; results stay retrievable for seven days.'),
28
+ md5: z
29
+ .string()
30
+ .optional()
31
+ .describe('Uppercase hex MD5 of the uppercased residues, the UniParc sequence digest.'),
32
+ length: z.number().int().optional().describe('Sequence length in residues.'),
33
+ interproVersion: z
34
+ .string()
35
+ .optional()
36
+ .describe('InterPro release of a finished InterProScan job.'),
37
+ interproscanVersion: z.string().optional().describe('InterProScan version of a finished job.'),
38
+ matches: z
39
+ .array(ScanMatchSchema)
40
+ .optional()
41
+ .describe('Whole match records in this segment when finished, sorted by earliest location start then signature accession; coordinates are 1-based and inclusive. Each record keeps its own source and signatureLibraryRelease.'),
42
+ coverage: z
43
+ .object({
44
+ totalMatches: z.number().int().describe('Match records in the complete result.'),
45
+ offset: z.number().int().describe('Match records that precede this segment.'),
46
+ returnedMatches: z.number().int().describe('Match records in this segment.'),
47
+ hasMore: z.boolean().describe('Whether another segment remains.'),
48
+ nextCursor: z
49
+ .string()
50
+ .optional()
51
+ .describe('Opaque cursor for the next segment; pass it unchanged to the tool the notice names.'),
52
+ })
53
+ .optional()
54
+ .describe('Where this segment sits in the complete match list; present when finished.'),
55
+ sourceUrl: z.string().describe('Upstream endpoint that supplied this result.'),
56
+ });
57
+ /** Enrichment fields both tools declare. */
58
+ export const ScanEnrichment = {
59
+ notice: z.string().optional().describe('Job, continuation, or empty-result guidance.'),
60
+ totalCount: z.number().optional().describe('Match records in the complete result.'),
61
+ truncated: z.boolean().optional().describe('More segments remain.'),
62
+ shown: z.number().int().optional().describe('Match records in this segment.'),
63
+ };
64
+ const cursorTarget = (state, md5) => state.source === 'precalculated'
65
+ ? { operation: PRECALCULATED_OPERATION, scope: { md5 } }
66
+ : { operation: JOB_OPERATION, scope: { jobId: state.jobId } };
67
+ /** Decode a continuation for the given target before any upstream request. */
68
+ export function decodeScanCursor(cursor, target) {
69
+ return 'md5' in target
70
+ ? decodePositionCursor(cursor, PRECALCULATED_OPERATION, { md5: target.md5 }, PositionSchema)
71
+ : decodePositionCursor(cursor, JOB_OPERATION, { jobId: target.jobId }, PositionSchema);
72
+ }
73
+ function minStart(match) {
74
+ let start = Number.POSITIVE_INFINITY;
75
+ for (const location of match.locations)
76
+ start = Math.min(start, location.start);
77
+ return start;
78
+ }
79
+ /** Deterministic order, so a cursor offset names the same record on every read. */
80
+ function sortMatches(matches) {
81
+ return matches
82
+ .map((match) => ({ match, start: minStart(match), json: JSON.stringify(match) }))
83
+ .sort((a, b) => a.start - b.start ||
84
+ compareText(a.match.signature.accession, b.match.signature.accession) ||
85
+ compareText(a.match.source, b.match.source) ||
86
+ compareText(a.json, b.json))
87
+ .map(({ match }) => match);
88
+ }
89
+ /**
90
+ * Build one segment of a finished result, or the pending output, and the notices that go with
91
+ * it. `continuation` names the call that reads the next segment, as the agent should make it.
92
+ */
93
+ export function buildScanOutput(enrich, state, options) {
94
+ const base = {
95
+ source: state.source,
96
+ ...(state.source === 'interproscan' ? { jobId: state.jobId } : {}),
97
+ };
98
+ if (state.source === 'interproscan' && state.status !== 'FINISHED') {
99
+ const status = state.status === 'submitted' ? 'submitted' : state.status === 'QUEUED' ? 'queued' : 'running';
100
+ enrich.notice(`InterProScan job ${state.jobId} is ${status}; de novo scans commonly take about ten minutes. Call interpro_get_scan_result ${JSON.stringify({ jobId: state.jobId })} to wait for it. Results stay retrievable for seven days.`);
101
+ return {
102
+ ...base,
103
+ status,
104
+ ...(options.md5 ? { md5: options.md5 } : {}),
105
+ ...(options.length ? { length: options.length } : {}),
106
+ sourceUrl: state.sourceUrl,
107
+ };
108
+ }
109
+ const finished = state.source === 'precalculated'
110
+ ? { matches: state.matches, md5: options.md5, length: options.length }
111
+ : {
112
+ matches: state.result.matches,
113
+ md5: state.result.md5,
114
+ length: state.result.length,
115
+ interproVersion: state.result.interproVersion,
116
+ interproscanVersion: state.result.interproscanVersion,
117
+ };
118
+ const all = sortMatches(finished.matches);
119
+ const total = all.length;
120
+ const offset = options.start?.offset ?? 0;
121
+ if (options.start && (options.start.total !== total || offset >= total))
122
+ throw validationError('Cursor no longer matches this result; restart without cursor.', {
123
+ reason: 'invalid_cursor',
124
+ retryable: false,
125
+ });
126
+ const { operation, scope } = cursorTarget(state, finished.md5);
127
+ const remaining = total - offset;
128
+ const segment = (count) => {
129
+ const end = offset + count;
130
+ return {
131
+ ...base,
132
+ status: 'finished',
133
+ ...(finished.md5 ? { md5: finished.md5 } : {}),
134
+ ...(finished.length ? { length: finished.length } : {}),
135
+ ...('interproVersion' in finished
136
+ ? {
137
+ interproVersion: finished.interproVersion,
138
+ interproscanVersion: finished.interproscanVersion,
139
+ }
140
+ : {}),
141
+ matches: all.slice(offset, end),
142
+ coverage: {
143
+ totalMatches: total,
144
+ offset,
145
+ returnedMatches: count,
146
+ hasMore: end < total,
147
+ ...(end < total
148
+ ? { nextCursor: encodePositionCursor(operation, scope, { offset: end, total }) }
149
+ : {}),
150
+ },
151
+ sourceUrl: state.sourceUrl,
152
+ };
153
+ };
154
+ const { count, output, oversized } = remaining
155
+ ? fitWithinBudget(remaining, segment)
156
+ : { count: 0, output: segment(0), oversized: false };
157
+ const notices = [];
158
+ if (oversized)
159
+ notices.push(`Match ${all[offset]?.signature.accession} cannot be split, so this segment holds it alone and exceeds the ${BUDGET_LABEL}.`);
160
+ const next = output.coverage?.nextCursor;
161
+ if (next) {
162
+ notices.push(`This segment holds matches ${offset + 1}–${offset + count} of ${total} and stopped at the ${BUDGET_LABEL}. Continue with ${options.continuation(next)} before concluding a region has no match.`);
163
+ enrich.truncated({ shown: count });
164
+ }
165
+ else if (!total)
166
+ notices.push(state.source === 'precalculated'
167
+ ? 'The InterPro Matches API holds a precalculated result for this sequence with no signature matches.'
168
+ : 'InterProScan finished and reported no signature matches for this sequence.');
169
+ enrich.total(total);
170
+ if (notices.length)
171
+ enrich.notice(notices.join(' '));
172
+ return output;
173
+ }
174
+ //# sourceMappingURL=scan-output.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan-output.js","sourceRoot":"","sources":["../../../src/services/sequence-scan/scan-output.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,112 @@
1
+ /**
2
+ * @fileoverview Wire and output schemas for InterProScan match records, Matches API lookups, and
3
+ * Job Dispatcher iprscan6 jobs. Match records pass through verbatim, unknown fields included.
4
+ * @module services/sequence-scan/scan-schemas
5
+ */
6
+ import { z } from '@cyanheads/mcp-ts-core';
7
+ /**
8
+ * Job Dispatcher iprscan6 job identifier, e.g. `iprscan6-R20261028-131532-0930-13189043-p1m`:
9
+ * submission channel letter and date, time, two numeric fields, then a short host suffix. The
10
+ * channel letter and suffix are kept open beyond the observed `R` and `p1m`.
11
+ */
12
+ export declare const JOB_ID_PATTERN: RegExp;
13
+ /** Longest job identifier the pattern admits, so the input bound and the grammar agree. */
14
+ export declare const MAX_JOB_ID_CHARS = 64;
15
+ /** Job Dispatcher `status` vocabulary; NOT_FOUND arrives as HTTP 200 for unknown and expired IDs. */
16
+ export declare const JobStatusSchema: z.ZodEnum<{
17
+ ERROR: "ERROR";
18
+ FAILURE: "FAILURE";
19
+ FINISHED: "FINISHED";
20
+ NOT_FOUND: "NOT_FOUND";
21
+ QUEUED: "QUEUED";
22
+ RUNNING: "RUNNING";
23
+ }>;
24
+ export type JobStatus = z.infer<typeof JobStatusSchema>;
25
+ /** One InterProScan match record, the shape the Matches API and iprscan6 JSON share. */
26
+ export declare const ScanMatchSchema: z.ZodObject<{
27
+ signature: z.ZodObject<{
28
+ accession: z.ZodString;
29
+ signatureLibraryRelease: z.ZodObject<{
30
+ library: z.ZodString;
31
+ version: z.ZodString;
32
+ }, z.core.$catchall<z.ZodJSONSchema>>;
33
+ entry: z.ZodOptional<z.ZodNullable<z.ZodObject<{
34
+ accession: z.ZodString;
35
+ }, z.core.$catchall<z.ZodJSONSchema>>>>;
36
+ }, z.core.$catchall<z.ZodJSONSchema>>;
37
+ 'model-ac': z.ZodNullable<z.ZodString>;
38
+ source: z.ZodString;
39
+ locations: z.ZodArray<z.ZodObject<{
40
+ start: z.ZodNumber;
41
+ end: z.ZodNumber;
42
+ 'location-fragments': z.ZodArray<z.ZodObject<{
43
+ start: z.ZodNumber;
44
+ end: z.ZodNumber;
45
+ 'dc-status': z.ZodString;
46
+ }, z.core.$catchall<z.ZodJSONSchema>>>;
47
+ }, z.core.$catchall<z.ZodJSONSchema>>>;
48
+ }, z.core.$catchall<z.ZodJSONSchema>>;
49
+ export type ScanMatch = z.infer<typeof ScanMatchSchema>;
50
+ /** `POST /interpro/matches/api/matches` response for one MD5. */
51
+ export declare const MatchesLookupSchema: z.ZodObject<{
52
+ results: z.ZodArray<z.ZodObject<{
53
+ md5: z.ZodString;
54
+ found: z.ZodBoolean;
55
+ matches: z.ZodArray<z.ZodObject<{
56
+ signature: z.ZodObject<{
57
+ accession: z.ZodString;
58
+ signatureLibraryRelease: z.ZodObject<{
59
+ library: z.ZodString;
60
+ version: z.ZodString;
61
+ }, z.core.$catchall<z.ZodJSONSchema>>;
62
+ entry: z.ZodOptional<z.ZodNullable<z.ZodObject<{
63
+ accession: z.ZodString;
64
+ }, z.core.$catchall<z.ZodJSONSchema>>>>;
65
+ }, z.core.$catchall<z.ZodJSONSchema>>;
66
+ 'model-ac': z.ZodNullable<z.ZodString>;
67
+ source: z.ZodString;
68
+ locations: z.ZodArray<z.ZodObject<{
69
+ start: z.ZodNumber;
70
+ end: z.ZodNumber;
71
+ 'location-fragments': z.ZodArray<z.ZodObject<{
72
+ start: z.ZodNumber;
73
+ end: z.ZodNumber;
74
+ 'dc-status': z.ZodString;
75
+ }, z.core.$catchall<z.ZodJSONSchema>>>;
76
+ }, z.core.$catchall<z.ZodJSONSchema>>>;
77
+ }, z.core.$catchall<z.ZodJSONSchema>>>;
78
+ }, z.core.$strip>>;
79
+ }, z.core.$strip>;
80
+ /** `result/{jobId}/json` body for a single-sequence iprscan6 job. */
81
+ export declare const ScanResultSchema: z.ZodObject<{
82
+ 'interproscan-version': z.ZodString;
83
+ 'interpro-version': z.ZodString;
84
+ results: z.ZodArray<z.ZodObject<{
85
+ md5: z.ZodString;
86
+ sequence: z.ZodString;
87
+ matches: z.ZodArray<z.ZodObject<{
88
+ signature: z.ZodObject<{
89
+ accession: z.ZodString;
90
+ signatureLibraryRelease: z.ZodObject<{
91
+ library: z.ZodString;
92
+ version: z.ZodString;
93
+ }, z.core.$catchall<z.ZodJSONSchema>>;
94
+ entry: z.ZodOptional<z.ZodNullable<z.ZodObject<{
95
+ accession: z.ZodString;
96
+ }, z.core.$catchall<z.ZodJSONSchema>>>>;
97
+ }, z.core.$catchall<z.ZodJSONSchema>>;
98
+ 'model-ac': z.ZodNullable<z.ZodString>;
99
+ source: z.ZodString;
100
+ locations: z.ZodArray<z.ZodObject<{
101
+ start: z.ZodNumber;
102
+ end: z.ZodNumber;
103
+ 'location-fragments': z.ZodArray<z.ZodObject<{
104
+ start: z.ZodNumber;
105
+ end: z.ZodNumber;
106
+ 'dc-status': z.ZodString;
107
+ }, z.core.$catchall<z.ZodJSONSchema>>>;
108
+ }, z.core.$catchall<z.ZodJSONSchema>>>;
109
+ }, z.core.$catchall<z.ZodJSONSchema>>>;
110
+ }, z.core.$catchall<z.ZodJSONSchema>>>;
111
+ }, z.core.$strip>;
112
+ //# sourceMappingURL=scan-schemas.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan-schemas.d.ts","sourceRoot":"","sources":["../../../src/services/sequence-scan/scan-schemas.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAG3C;;;;GAIG;AACH,eAAO,MAAM,cAAc,QAA6D,CAAC;AACzF,2FAA2F;AAC3F,eAAO,MAAM,gBAAgB,KAAK,CAAC;AAEnC,qGAAqG;AACrG,eAAO,MAAM,eAAe;;;;;;;EAO1B,CAAC;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AA2BxD,wFAAwF;AACxF,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;qCAkC2B,CAAC;AACxD,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAIxD,iEAAiE;AACjE,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAI9B,CAAC;AAEH,qEAAqE;AACrE,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAU3B,CAAC"}
@@ -0,0 +1,97 @@
1
+ /**
2
+ * @fileoverview Wire and output schemas for InterProScan match records, Matches API lookups, and
3
+ * Job Dispatcher iprscan6 jobs. Match records pass through verbatim, unknown fields included.
4
+ * @module services/sequence-scan/scan-schemas
5
+ */
6
+ import { z } from '@cyanheads/mcp-ts-core';
7
+ import { JsonValueSchema } from '../interpro/schemas.js';
8
+ /**
9
+ * Job Dispatcher iprscan6 job identifier, e.g. `iprscan6-R20261028-131532-0930-13189043-p1m`:
10
+ * submission channel letter and date, time, two numeric fields, then a short host suffix. The
11
+ * channel letter and suffix are kept open beyond the observed `R` and `p1m`.
12
+ */
13
+ export const JOB_ID_PATTERN = /^iprscan6-[A-Z]\d{8}-\d{6}-\d{4}-\d{1,12}-[a-z0-9]{1,8}$/;
14
+ /** Longest job identifier the pattern admits, so the input bound and the grammar agree. */
15
+ export const MAX_JOB_ID_CHARS = 64;
16
+ /** Job Dispatcher `status` vocabulary; NOT_FOUND arrives as HTTP 200 for unknown and expired IDs. */
17
+ export const JobStatusSchema = z.enum([
18
+ 'QUEUED',
19
+ 'RUNNING',
20
+ 'FINISHED',
21
+ 'ERROR',
22
+ 'FAILURE',
23
+ 'NOT_FOUND',
24
+ ]);
25
+ const ScanFragmentSchema = z
26
+ .object({
27
+ start: z.number().int().positive().describe('Inclusive 1-based start residue.'),
28
+ end: z.number().int().positive().describe('Inclusive 1-based end residue.'),
29
+ 'dc-status': z
30
+ .string()
31
+ .describe('Discontinuity status, such as CONTINUOUS, N_TERMINAL_DISC, or C_TERMINAL_DISC.'),
32
+ })
33
+ .catchall(JsonValueSchema)
34
+ .describe('One continuous piece of a possibly discontinuous match location.');
35
+ const ScanLocationSchema = z
36
+ .object({
37
+ start: z.number().int().positive().describe('Inclusive 1-based start residue.'),
38
+ end: z.number().int().positive().describe('Inclusive 1-based end residue.'),
39
+ 'location-fragments': z
40
+ .array(ScanFragmentSchema)
41
+ .min(1)
42
+ .describe('Nonempty fragments of this location.'),
43
+ })
44
+ .catchall(JsonValueSchema)
45
+ .describe('One match location with every upstream field, such as scores, e-values, HMM bounds, envelope, sites, and alignments.');
46
+ /** One InterProScan match record, the shape the Matches API and iprscan6 JSON share. */
47
+ export const ScanMatchSchema = z
48
+ .object({
49
+ signature: z
50
+ .object({
51
+ accession: z.string().describe('Member-database signature accession, such as PF13499.'),
52
+ signatureLibraryRelease: z
53
+ .object({
54
+ library: z
55
+ .string()
56
+ .describe('Member database name exactly as the source spells it, such as Pfam.'),
57
+ version: z.string().describe('Member database release that produced this match.'),
58
+ })
59
+ .catchall(JsonValueSchema)
60
+ .describe('Library and release of this signature.'),
61
+ entry: z
62
+ .object({
63
+ accession: z.string().describe('Integrated InterPro entry accession.'),
64
+ })
65
+ .catchall(JsonValueSchema)
66
+ .nullable()
67
+ .optional()
68
+ .describe('InterPro entry the signature is integrated into; null when unintegrated.'),
69
+ })
70
+ .catchall(JsonValueSchema)
71
+ .describe('Matched signature, including name, description, and type when supplied.'),
72
+ 'model-ac': z.string().nullable().describe('Model accession within the signature.'),
73
+ source: z
74
+ .string()
75
+ .describe('Method that produced the match, such as Pfam or InterPro-N, exactly as the source spells it.'),
76
+ locations: z.array(ScanLocationSchema).describe('Match locations on the sequence.'),
77
+ })
78
+ .catchall(JsonValueSchema)
79
+ .describe('One InterProScan match record, verbatim.');
80
+ const Md5Schema = z.string().regex(/^[0-9a-fA-F]{32}$/);
81
+ /** `POST /interpro/matches/api/matches` response for one MD5. */
82
+ export const MatchesLookupSchema = z.object({
83
+ results: z
84
+ .array(z.object({ md5: Md5Schema, found: z.boolean(), matches: z.array(ScanMatchSchema) }))
85
+ .length(1),
86
+ });
87
+ /** `result/{jobId}/json` body for a single-sequence iprscan6 job. */
88
+ export const ScanResultSchema = z.object({
89
+ 'interproscan-version': z.string(),
90
+ 'interpro-version': z.string(),
91
+ results: z
92
+ .array(z
93
+ .object({ md5: Md5Schema, sequence: z.string(), matches: z.array(ScanMatchSchema) })
94
+ .catchall(JsonValueSchema))
95
+ .length(1),
96
+ });
97
+ //# sourceMappingURL=scan-schemas.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan-schemas.js","sourceRoot":"","sources":["../../../src/services/sequence-scan/scan-schemas.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,119 @@
1
+ /**
2
+ * @fileoverview Sequence scanning: precalculated lookup in the InterPro Matches API, then
3
+ * submission to the EBI Job Dispatcher iprscan6 service with a bounded, cancellable wait. The
4
+ * process-wide in-flight cap and the per-MD5 submission dedupe live on the service instance.
5
+ * @module services/sequence-scan/sequence-scan-service
6
+ */
7
+ import type { Context } from '@cyanheads/mcp-ts-core';
8
+ import { type InterproFetch } from '../interpro/http-adapter.js';
9
+ import { type ScanMatch } from './scan-schemas.js';
10
+ /** One budget per tool call, below the 60-second default MCP client request timeout. */
11
+ export declare const CALL_BUDGET_MS = 50000;
12
+ /** Minimum spacing between `status` polls of one job within a call. */
13
+ export declare const POLL_INTERVAL_MS = 5000;
14
+ /** Longest wait window a call can use: the budget less the result reserve. */
15
+ export declare const MAX_WAIT_SECONDS: number;
16
+ /** Job Dispatcher fair use asks for at most 30 jobs in flight per user; one address is one user. */
17
+ export declare const MAX_IN_FLIGHT = 25;
18
+ /** A tracked job nobody observed finishing frees its slot after this long (runs take ~10 min). */
19
+ export declare const IN_FLIGHT_TTL_MS: number;
20
+ export declare const MATCHES_URL = "https://www.ebi.ac.uk/interpro/matches/api/matches";
21
+ export declare const JOB_DISPATCHER_BASE = "https://www.ebi.ac.uk/Tools/services/rest/iprscan6/";
22
+ /** A tool invocation's single deadline, measured on the `performance` clock. */
23
+ export interface ScanCall {
24
+ readonly ctx: Context;
25
+ readonly deadline: number;
26
+ readonly started: number;
27
+ }
28
+ /** Everything a finished iprscan6 job returns that the tools surface. */
29
+ export interface JobResult {
30
+ readonly interproscanVersion: string;
31
+ readonly interproVersion: string;
32
+ readonly length: number;
33
+ readonly matches: ScanMatch[];
34
+ readonly md5: string;
35
+ }
36
+ /** Where a job stands when a call's wait window closes. */
37
+ export type JobState = {
38
+ readonly status: 'submitted' | 'QUEUED' | 'RUNNING';
39
+ } | {
40
+ readonly status: 'FINISHED';
41
+ readonly result: JobResult;
42
+ };
43
+ /** What one scan call produced. */
44
+ export type ScanOutcome = {
45
+ readonly source: 'precalculated';
46
+ readonly matches: ScanMatch[];
47
+ readonly sourceUrl: string;
48
+ } | ({
49
+ readonly source: 'interproscan';
50
+ readonly jobId: string;
51
+ readonly sourceUrl: string;
52
+ } & JobState);
53
+ /** Lookup, submission, polling, and result reads for one configured submission address. */
54
+ export declare class SequenceScanService {
55
+ private readonly email;
56
+ private readonly fetch;
57
+ private readonly inFlight;
58
+ constructor(email: string | undefined, fetch?: InterproFetch);
59
+ /** Start the call's single deadline. */
60
+ begin(ctx: Context): ScanCall;
61
+ /**
62
+ * Look the MD5 up in the Matches API; on a miss, submit (or join the submission already in
63
+ * flight for this MD5) and wait up to `waitMs`. `lookupOnly` resumes a precalculated walk and
64
+ * never submits.
65
+ */
66
+ scan(call: ScanCall, sequence: {
67
+ readonly md5: string;
68
+ readonly residues: string;
69
+ }, waitMs: number, lookupOnly?: boolean): Promise<ScanOutcome>;
70
+ /** Read a job's state, waiting up to `waitMs` for it to finish; polls at least once. */
71
+ result(call: ScanCall, jobId: string, waitMs: number): Promise<JobState & {
72
+ sourceUrl: string;
73
+ }>;
74
+ private jobUrl;
75
+ /** Name the in-flight job in any failure after submission, so the caller keeps its handle. */
76
+ private withJob;
77
+ /** Precalculated matches, or undefined when UniParc holds no result for the MD5. */
78
+ private lookup;
79
+ /** Expire tracked jobs nobody saw finish, so the map stays bounded without a timer. */
80
+ private sweep;
81
+ /** Free the slot of a job observed in a terminal state. */
82
+ private release;
83
+ /**
84
+ * The job for this MD5: one already tracked, one being submitted by another call, or a new
85
+ * submission. `submittedAt` is set only when this call's own `run` (or one it joined) made it.
86
+ */
87
+ private submit;
88
+ /**
89
+ * Await a shared submission. A caller leaving early detaches only itself; the request is
90
+ * aborted when the last waiting caller leaves before it settles.
91
+ */
92
+ private join;
93
+ /** One `run` request, never retried: a lost response may still have created a job. */
94
+ private run;
95
+ /**
96
+ * The `<description>` of a Job Dispatcher XML error, single-line, bounded, and with the
97
+ * configured address replaced, since rejections can echo submitted fields.
98
+ */
99
+ private describeRejection;
100
+ /**
101
+ * Poll `status` from `firstPollAt`, at most every five seconds, until a terminal status or
102
+ * the window closes, then read the result of a FINISHED job. The window is `waitMs` from the
103
+ * call's start, capped so the result read keeps its reserve of the deadline.
104
+ */
105
+ private awaitJob;
106
+ private status;
107
+ private read;
108
+ /**
109
+ * One fixed-origin request under the call deadline. Each attempt is bounded by the smaller of
110
+ * the operation's attempt bound and what remains. Reads get one more attempt on a transient
111
+ * failure; a `run` never does.
112
+ */
113
+ private send;
114
+ }
115
+ /** Initialize the process service in createApp.setup with the configured submission address. */
116
+ export declare function initSequenceScanService(email: string | undefined, fetch?: InterproFetch): void;
117
+ /** Obtain the initialized process service. */
118
+ export declare function getSequenceScanService(): SequenceScanService;
119
+ //# sourceMappingURL=sequence-scan-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sequence-scan-service.d.ts","sourceRoot":"","sources":["../../../src/services/sequence-scan/sequence-scan-service.ts"],"names":[],"mappings":"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"}