@burdenoff/mcp-libs 2026.720.2 → 2026.731.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -310,9 +310,10 @@ var ARCHIVE_DATASET = `
|
|
|
310
310
|
}
|
|
311
311
|
`;
|
|
312
312
|
var LIST_PIPELINES = `
|
|
313
|
-
query LabsofscienceListPipelines {
|
|
314
|
-
labListPipelines {
|
|
315
|
-
${PIPELINE_FIELDS}
|
|
313
|
+
query LabsofscienceListPipelines($pagination: PaginationInput) {
|
|
314
|
+
labListPipelines(pagination: $pagination) {
|
|
315
|
+
items { ${PIPELINE_FIELDS} }
|
|
316
|
+
${PAGE_INFO}
|
|
316
317
|
}
|
|
317
318
|
}
|
|
318
319
|
`;
|
|
@@ -362,9 +363,14 @@ var GET_INSTRUMENT = `
|
|
|
362
363
|
}
|
|
363
364
|
`;
|
|
364
365
|
var LIST_INSTRUMENTS = `
|
|
365
|
-
query LabsofscienceListInstruments(
|
|
366
|
-
|
|
367
|
-
|
|
366
|
+
query LabsofscienceListInstruments(
|
|
367
|
+
$status: LabInstrumentStatus
|
|
368
|
+
$search: String
|
|
369
|
+
$pagination: PaginationInput
|
|
370
|
+
) {
|
|
371
|
+
labListInstruments(status: $status, search: $search, pagination: $pagination) {
|
|
372
|
+
items { ${INSTRUMENT_FIELDS} }
|
|
373
|
+
${PAGE_INFO}
|
|
368
374
|
}
|
|
369
375
|
}
|
|
370
376
|
`;
|
|
@@ -445,6 +451,13 @@ var LIST_NOTEBOOK_ENTRIES = `
|
|
|
445
451
|
}
|
|
446
452
|
}
|
|
447
453
|
`;
|
|
454
|
+
var GET_NOTEBOOK_ENTRY = `
|
|
455
|
+
query LabsofscienceGetNotebookEntry($id: ID!) {
|
|
456
|
+
labNotebookEntry(id: $id) {
|
|
457
|
+
${NOTEBOOK_FIELDS}
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
`;
|
|
448
461
|
var CREATE_NOTEBOOK_ENTRY = `
|
|
449
462
|
mutation LabsofscienceCreateNotebookEntry($input: LabCreateNotebookEntryInput!) {
|
|
450
463
|
labCreateNotebookEntry(input: $input) {
|
|
@@ -459,6 +472,13 @@ var SIGN_NOTEBOOK_ENTRY = `
|
|
|
459
472
|
}
|
|
460
473
|
}
|
|
461
474
|
`;
|
|
475
|
+
var WITNESS_NOTEBOOK_ENTRY = `
|
|
476
|
+
mutation LabsofscienceWitnessNotebookEntry($id: ID!) {
|
|
477
|
+
labWitnessNotebookEntry(id: $id) {
|
|
478
|
+
${NOTEBOOK_FIELDS}
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
`;
|
|
462
482
|
var HEALTH = `
|
|
463
483
|
query LabsofscienceHealth {
|
|
464
484
|
labHealth
|
|
@@ -643,7 +663,11 @@ function registerProjectTools(server, ctx, track) {
|
|
|
643
663
|
{
|
|
644
664
|
title: "Archive Research Project",
|
|
645
665
|
description: "Archive a research project. Returns true on success.",
|
|
646
|
-
inputSchema: { id: zod.z.string().describe("Project ID") }
|
|
666
|
+
inputSchema: { id: zod.z.string().describe("Project ID") },
|
|
667
|
+
// BOFF-3611: irreversible via this tool surface — see datasets.ts's
|
|
668
|
+
// datasets_archive for why this is an explicit per-tool override
|
|
669
|
+
// rather than a fleet-wide tool-name.ts regex change.
|
|
670
|
+
annotations: { destructiveHint: true }
|
|
647
671
|
},
|
|
648
672
|
async ({ id }) => {
|
|
649
673
|
try {
|
|
@@ -1071,7 +1095,12 @@ function registerProtocolTools(server, ctx, track) {
|
|
|
1071
1095
|
{
|
|
1072
1096
|
title: "Publish Protocol",
|
|
1073
1097
|
description: "Publish a draft protocol, freezing its version for reproducibility. Returns the published protocol with its steps.",
|
|
1074
|
-
inputSchema: { id: zod.z.string().describe("Protocol ID") }
|
|
1098
|
+
inputSchema: { id: zod.z.string().describe("Protocol ID") },
|
|
1099
|
+
// BOFF-3611: irreversible via this tool surface — publishing freezes
|
|
1100
|
+
// the version; there is no "unpublish" mutation. Explicit override —
|
|
1101
|
+
// see datasets.ts's datasets_archive for why per-tool, not a
|
|
1102
|
+
// fleet-wide tool-name.ts regex change.
|
|
1103
|
+
annotations: { destructiveHint: true }
|
|
1075
1104
|
},
|
|
1076
1105
|
async ({ id }) => {
|
|
1077
1106
|
try {
|
|
@@ -1205,7 +1234,12 @@ function registerDatasetTools(server, ctx, track) {
|
|
|
1205
1234
|
{
|
|
1206
1235
|
title: "Archive Dataset",
|
|
1207
1236
|
description: "Archive a dataset. Returns true on success.",
|
|
1208
|
-
inputSchema: { id: zod.z.string().describe("Dataset ID") }
|
|
1237
|
+
inputSchema: { id: zod.z.string().describe("Dataset ID") },
|
|
1238
|
+
// BOFF-3611: irreversible via this tool surface — the backend has no
|
|
1239
|
+
// "unarchive" mutation. Explicit override because tool-name.ts's
|
|
1240
|
+
// fleet-wide inference only matches "delete"; declaring it here (not
|
|
1241
|
+
// by widening that shared regex) keeps the blast radius to this tool.
|
|
1242
|
+
annotations: { destructiveHint: true }
|
|
1209
1243
|
},
|
|
1210
1244
|
async ({ id }) => {
|
|
1211
1245
|
try {
|
|
@@ -1228,12 +1262,18 @@ function registerPipelineTools(server, ctx, track) {
|
|
|
1228
1262
|
track(mcpToolName(ctx.productName, "pipelines_list")),
|
|
1229
1263
|
{
|
|
1230
1264
|
title: "List Analysis Pipelines",
|
|
1231
|
-
description: "List
|
|
1232
|
-
inputSchema: {
|
|
1265
|
+
description: "List analysis pipelines in the bound workspace, with pagination. Returns a connection ({ items, pageInfo }).",
|
|
1266
|
+
inputSchema: {
|
|
1267
|
+
limit: zod.z.number().int().optional().describe("Page size (default 50)"),
|
|
1268
|
+
offset: zod.z.number().int().optional().describe("Pagination offset (default 0)"),
|
|
1269
|
+
cursor: zod.z.string().optional().describe("Opaque pagination cursor")
|
|
1270
|
+
}
|
|
1233
1271
|
},
|
|
1234
|
-
async () => {
|
|
1272
|
+
async ({ limit, offset, cursor }) => {
|
|
1235
1273
|
try {
|
|
1236
|
-
const data = await ctx.gql.workspace.request(LIST_PIPELINES, {
|
|
1274
|
+
const data = await ctx.gql.workspace.request(LIST_PIPELINES, {
|
|
1275
|
+
pagination: { limit: limit ?? 50, offset: offset ?? 0, cursor }
|
|
1276
|
+
});
|
|
1237
1277
|
return jsonResult(data.labListPipelines);
|
|
1238
1278
|
} catch (error) {
|
|
1239
1279
|
ctx.logger.error("labsofscience pipelines_list failed", { error: String(error) });
|
|
@@ -1383,14 +1423,22 @@ function registerInstrumentTools(server, ctx, track) {
|
|
|
1383
1423
|
track(mcpToolName(ctx.productName, "instruments_list")),
|
|
1384
1424
|
{
|
|
1385
1425
|
title: "List Instruments",
|
|
1386
|
-
description: "List lab instruments, optionally filtered by availability status.",
|
|
1426
|
+
description: "List lab instruments, optionally filtered by availability status and/or a name/model search term, with pagination. Returns a connection ({ items, pageInfo }).",
|
|
1387
1427
|
inputSchema: {
|
|
1388
|
-
status: InstrumentStatusEnum.optional().describe("Filter by instrument status")
|
|
1428
|
+
status: InstrumentStatusEnum.optional().describe("Filter by instrument status"),
|
|
1429
|
+
search: zod.z.string().optional().describe("Search by name or model"),
|
|
1430
|
+
limit: zod.z.number().int().optional().describe("Page size (default 50)"),
|
|
1431
|
+
offset: zod.z.number().int().optional().describe("Pagination offset (default 0)"),
|
|
1432
|
+
cursor: zod.z.string().optional().describe("Opaque pagination cursor")
|
|
1389
1433
|
}
|
|
1390
1434
|
},
|
|
1391
|
-
async ({ status }) => {
|
|
1435
|
+
async ({ status, search, limit, offset, cursor }) => {
|
|
1392
1436
|
try {
|
|
1393
|
-
const data = await ctx.gql.workspace.request(LIST_INSTRUMENTS, {
|
|
1437
|
+
const data = await ctx.gql.workspace.request(LIST_INSTRUMENTS, {
|
|
1438
|
+
status,
|
|
1439
|
+
search,
|
|
1440
|
+
pagination: { limit: limit ?? 50, offset: offset ?? 0, cursor }
|
|
1441
|
+
});
|
|
1394
1442
|
return jsonResult(data.labListInstruments);
|
|
1395
1443
|
} catch (error) {
|
|
1396
1444
|
ctx.logger.error("labsofscience instruments_list failed", { error: String(error) });
|
|
@@ -1617,7 +1665,11 @@ function registerInventoryTools(server, ctx, track) {
|
|
|
1617
1665
|
{
|
|
1618
1666
|
title: "Archive Inventory Item",
|
|
1619
1667
|
description: "Archive an inventory item. Returns true on success.",
|
|
1620
|
-
inputSchema: { id: zod.z.string().describe("Inventory item ID") }
|
|
1668
|
+
inputSchema: { id: zod.z.string().describe("Inventory item ID") },
|
|
1669
|
+
// BOFF-3611: irreversible via this tool surface — see datasets.ts's
|
|
1670
|
+
// datasets_archive for why this is an explicit per-tool override
|
|
1671
|
+
// rather than a fleet-wide tool-name.ts regex change.
|
|
1672
|
+
annotations: { destructiveHint: true }
|
|
1621
1673
|
},
|
|
1622
1674
|
async ({ id }) => {
|
|
1623
1675
|
try {
|
|
@@ -1667,6 +1719,26 @@ function registerNotebookTools(server, ctx, track) {
|
|
|
1667
1719
|
}
|
|
1668
1720
|
}
|
|
1669
1721
|
);
|
|
1722
|
+
server.registerTool(
|
|
1723
|
+
track(mcpToolName(ctx.productName, "notebook_get")),
|
|
1724
|
+
{
|
|
1725
|
+
title: "Get Notebook Entry",
|
|
1726
|
+
description: "Fetch a single ELN entry by ID.",
|
|
1727
|
+
inputSchema: { id: zod.z.string().describe("Notebook entry ID") }
|
|
1728
|
+
},
|
|
1729
|
+
async ({ id }) => {
|
|
1730
|
+
try {
|
|
1731
|
+
const data = await ctx.gql.workspace.request(GET_NOTEBOOK_ENTRY, { id });
|
|
1732
|
+
if (!data.labNotebookEntry) return errorResult(`Notebook entry ${id} not found`);
|
|
1733
|
+
return jsonResult(data.labNotebookEntry);
|
|
1734
|
+
} catch (error) {
|
|
1735
|
+
ctx.logger.error("labsofscience notebook_get failed", { error: String(error) });
|
|
1736
|
+
return errorResult(
|
|
1737
|
+
`Failed to get notebook entry: ${error instanceof Error ? error.message : String(error)}`
|
|
1738
|
+
);
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
);
|
|
1670
1742
|
server.registerTool(
|
|
1671
1743
|
track(mcpToolName(ctx.productName, "notebook_create")),
|
|
1672
1744
|
{
|
|
@@ -1705,7 +1777,11 @@ function registerNotebookTools(server, ctx, track) {
|
|
|
1705
1777
|
{
|
|
1706
1778
|
title: "Sign Notebook Entry",
|
|
1707
1779
|
description: "Sign an ELN entry for record integrity (locks editable content). Returns the signed entry.",
|
|
1708
|
-
inputSchema: { id: zod.z.string().describe("Notebook entry ID") }
|
|
1780
|
+
inputSchema: { id: zod.z.string().describe("Notebook entry ID") },
|
|
1781
|
+
// BOFF-3611: irreversible — there is no "unsign" mutation. Explicit
|
|
1782
|
+
// override — see datasets.ts's datasets_archive for why per-tool,
|
|
1783
|
+
// not a fleet-wide tool-name.ts regex change.
|
|
1784
|
+
annotations: { destructiveHint: true }
|
|
1709
1785
|
},
|
|
1710
1786
|
async ({ id }) => {
|
|
1711
1787
|
try {
|
|
@@ -1719,6 +1795,29 @@ function registerNotebookTools(server, ctx, track) {
|
|
|
1719
1795
|
}
|
|
1720
1796
|
}
|
|
1721
1797
|
);
|
|
1798
|
+
server.registerTool(
|
|
1799
|
+
track(mcpToolName(ctx.productName, "notebook_witness")),
|
|
1800
|
+
{
|
|
1801
|
+
title: "Witness Notebook Entry",
|
|
1802
|
+
description: "Witness a SIGNED ELN entry as a second party for record integrity. Returns the witnessed entry.",
|
|
1803
|
+
inputSchema: { id: zod.z.string().describe("Notebook entry ID") },
|
|
1804
|
+
// BOFF-3611: irreversible — there is no "unwitness" mutation. Explicit
|
|
1805
|
+
// override — see datasets.ts's datasets_archive for why per-tool,
|
|
1806
|
+
// not a fleet-wide tool-name.ts regex change.
|
|
1807
|
+
annotations: { destructiveHint: true }
|
|
1808
|
+
},
|
|
1809
|
+
async ({ id }) => {
|
|
1810
|
+
try {
|
|
1811
|
+
const data = await ctx.gql.workspace.request(WITNESS_NOTEBOOK_ENTRY, { id });
|
|
1812
|
+
return jsonResult(data.labWitnessNotebookEntry);
|
|
1813
|
+
} catch (error) {
|
|
1814
|
+
ctx.logger.error("labsofscience notebook_witness failed", { error: String(error) });
|
|
1815
|
+
return errorResult(
|
|
1816
|
+
`Failed to witness notebook entry: ${error instanceof Error ? error.message : String(error)}`
|
|
1817
|
+
);
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
);
|
|
1722
1821
|
}
|
|
1723
1822
|
|
|
1724
1823
|
// src/modules/labsofscience/tools/health.ts
|
|
@@ -1793,9 +1892,9 @@ Science & discovery platform \u2014 a research LIMS / ELN.
|
|
|
1793
1892
|
|
|
1794
1893
|
## Tool groups
|
|
1795
1894
|
projects, experiments, runs, protocols, datasets, pipelines, instruments, inventory, notebook, health.
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
(workspace-scoped) except the public health probe.
|
|
1895
|
+
Every list query \u2014 including instruments_list and pipelines_list \u2014 returns { items, pageInfo }
|
|
1896
|
+
connections and accepts a PaginationInput { limit, offset, cursor }. All run against the WORKSPACE
|
|
1897
|
+
gateway (workspace-scoped) except the public health probe.
|
|
1799
1898
|
`;
|
|
1800
1899
|
return {
|
|
1801
1900
|
contents: [{ uri: schemaUri, text: schema, mimeType: "text/plain" }]
|
|
@@ -1845,7 +1944,7 @@ instruments_list and pipelines_list return plain arrays. All run against the WOR
|
|
|
1845
1944
|
instrumentsUri,
|
|
1846
1945
|
{
|
|
1847
1946
|
title: "Labsofscience Instrument Catalog",
|
|
1848
|
-
description: "
|
|
1947
|
+
description: "The first 50 lab instruments in the bound workspace.",
|
|
1849
1948
|
mimeType: "application/json"
|
|
1850
1949
|
},
|
|
1851
1950
|
async () => {
|
|
@@ -1853,7 +1952,7 @@ instruments_list and pipelines_list return plain arrays. All run against the WOR
|
|
|
1853
1952
|
try {
|
|
1854
1953
|
const data = await ctx.gql.workspace.request(
|
|
1855
1954
|
LIST_INSTRUMENTS,
|
|
1856
|
-
{}
|
|
1955
|
+
{ pagination: { limit: 50, offset: 0 } }
|
|
1857
1956
|
);
|
|
1858
1957
|
return {
|
|
1859
1958
|
contents: [
|
|
@@ -1884,7 +1983,7 @@ instruments_list and pipelines_list return plain arrays. All run against the WOR
|
|
|
1884
1983
|
pipelinesUri,
|
|
1885
1984
|
{
|
|
1886
1985
|
title: "Labsofscience Analysis Pipelines",
|
|
1887
|
-
description: "
|
|
1986
|
+
description: "The first 50 analysis pipelines in the bound workspace.",
|
|
1888
1987
|
mimeType: "application/json"
|
|
1889
1988
|
},
|
|
1890
1989
|
async () => {
|
|
@@ -1892,7 +1991,7 @@ instruments_list and pipelines_list return plain arrays. All run against the WOR
|
|
|
1892
1991
|
try {
|
|
1893
1992
|
const data = await ctx.gql.workspace.request(
|
|
1894
1993
|
LIST_PIPELINES,
|
|
1895
|
-
{}
|
|
1994
|
+
{ pagination: { limit: 50, offset: 0 } }
|
|
1896
1995
|
);
|
|
1897
1996
|
return {
|
|
1898
1997
|
contents: [
|