@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($status: LabInstrumentStatus) {
366
- labListInstruments(status: $status) {
367
- ${INSTRUMENT_FIELDS}
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 all analysis pipelines in the bound workspace.",
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, { status });
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
- List queries return { items, pageInfo } connections and accept a PaginationInput { limit, offset, cursor };
1797
- instruments_list and pipelines_list return plain arrays. All run against the WORKSPACE gateway
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: "All lab instruments in the bound workspace.",
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: "All analysis pipelines in the bound workspace.",
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: [