@burdenoff/mcp-libs 2026.720.1 → 2026.720.3

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.
@@ -249,9 +249,10 @@ var ARCHIVE_DATASET = `
249
249
  }
250
250
  `;
251
251
  var LIST_PIPELINES = `
252
- query LabsofscienceListPipelines {
253
- labListPipelines {
254
- ${PIPELINE_FIELDS}
252
+ query LabsofscienceListPipelines($pagination: PaginationInput) {
253
+ labListPipelines(pagination: $pagination) {
254
+ items { ${PIPELINE_FIELDS} }
255
+ ${PAGE_INFO}
255
256
  }
256
257
  }
257
258
  `;
@@ -301,9 +302,14 @@ var GET_INSTRUMENT = `
301
302
  }
302
303
  `;
303
304
  var LIST_INSTRUMENTS = `
304
- query LabsofscienceListInstruments($status: LabInstrumentStatus) {
305
- labListInstruments(status: $status) {
306
- ${INSTRUMENT_FIELDS}
305
+ query LabsofscienceListInstruments(
306
+ $status: LabInstrumentStatus
307
+ $search: String
308
+ $pagination: PaginationInput
309
+ ) {
310
+ labListInstruments(status: $status, search: $search, pagination: $pagination) {
311
+ items { ${INSTRUMENT_FIELDS} }
312
+ ${PAGE_INFO}
307
313
  }
308
314
  }
309
315
  `;
@@ -384,6 +390,13 @@ var LIST_NOTEBOOK_ENTRIES = `
384
390
  }
385
391
  }
386
392
  `;
393
+ var GET_NOTEBOOK_ENTRY = `
394
+ query LabsofscienceGetNotebookEntry($id: ID!) {
395
+ labNotebookEntry(id: $id) {
396
+ ${NOTEBOOK_FIELDS}
397
+ }
398
+ }
399
+ `;
387
400
  var CREATE_NOTEBOOK_ENTRY = `
388
401
  mutation LabsofscienceCreateNotebookEntry($input: LabCreateNotebookEntryInput!) {
389
402
  labCreateNotebookEntry(input: $input) {
@@ -398,6 +411,13 @@ var SIGN_NOTEBOOK_ENTRY = `
398
411
  }
399
412
  }
400
413
  `;
414
+ var WITNESS_NOTEBOOK_ENTRY = `
415
+ mutation LabsofscienceWitnessNotebookEntry($id: ID!) {
416
+ labWitnessNotebookEntry(id: $id) {
417
+ ${NOTEBOOK_FIELDS}
418
+ }
419
+ }
420
+ `;
401
421
  var HEALTH = `
402
422
  query LabsofscienceHealth {
403
423
  labHealth
@@ -582,7 +602,11 @@ function registerProjectTools(server, ctx, track) {
582
602
  {
583
603
  title: "Archive Research Project",
584
604
  description: "Archive a research project. Returns true on success.",
585
- inputSchema: { id: z.string().describe("Project ID") }
605
+ inputSchema: { id: z.string().describe("Project ID") },
606
+ // BOFF-3611: irreversible via this tool surface — see datasets.ts's
607
+ // datasets_archive for why this is an explicit per-tool override
608
+ // rather than a fleet-wide tool-name.ts regex change.
609
+ annotations: { destructiveHint: true }
586
610
  },
587
611
  async ({ id }) => {
588
612
  try {
@@ -1010,7 +1034,12 @@ function registerProtocolTools(server, ctx, track) {
1010
1034
  {
1011
1035
  title: "Publish Protocol",
1012
1036
  description: "Publish a draft protocol, freezing its version for reproducibility. Returns the published protocol with its steps.",
1013
- inputSchema: { id: z.string().describe("Protocol ID") }
1037
+ inputSchema: { id: z.string().describe("Protocol ID") },
1038
+ // BOFF-3611: irreversible via this tool surface — publishing freezes
1039
+ // the version; there is no "unpublish" mutation. Explicit override —
1040
+ // see datasets.ts's datasets_archive for why per-tool, not a
1041
+ // fleet-wide tool-name.ts regex change.
1042
+ annotations: { destructiveHint: true }
1014
1043
  },
1015
1044
  async ({ id }) => {
1016
1045
  try {
@@ -1144,7 +1173,12 @@ function registerDatasetTools(server, ctx, track) {
1144
1173
  {
1145
1174
  title: "Archive Dataset",
1146
1175
  description: "Archive a dataset. Returns true on success.",
1147
- inputSchema: { id: z.string().describe("Dataset ID") }
1176
+ inputSchema: { id: z.string().describe("Dataset ID") },
1177
+ // BOFF-3611: irreversible via this tool surface — the backend has no
1178
+ // "unarchive" mutation. Explicit override because tool-name.ts's
1179
+ // fleet-wide inference only matches "delete"; declaring it here (not
1180
+ // by widening that shared regex) keeps the blast radius to this tool.
1181
+ annotations: { destructiveHint: true }
1148
1182
  },
1149
1183
  async ({ id }) => {
1150
1184
  try {
@@ -1167,12 +1201,18 @@ function registerPipelineTools(server, ctx, track) {
1167
1201
  track(mcpToolName(ctx.productName, "pipelines_list")),
1168
1202
  {
1169
1203
  title: "List Analysis Pipelines",
1170
- description: "List all analysis pipelines in the bound workspace.",
1171
- inputSchema: {}
1204
+ description: "List analysis pipelines in the bound workspace, with pagination. Returns a connection ({ items, pageInfo }).",
1205
+ inputSchema: {
1206
+ limit: z.number().int().optional().describe("Page size (default 50)"),
1207
+ offset: z.number().int().optional().describe("Pagination offset (default 0)"),
1208
+ cursor: z.string().optional().describe("Opaque pagination cursor")
1209
+ }
1172
1210
  },
1173
- async () => {
1211
+ async ({ limit, offset, cursor }) => {
1174
1212
  try {
1175
- const data = await ctx.gql.workspace.request(LIST_PIPELINES, {});
1213
+ const data = await ctx.gql.workspace.request(LIST_PIPELINES, {
1214
+ pagination: { limit: limit ?? 50, offset: offset ?? 0, cursor }
1215
+ });
1176
1216
  return jsonResult(data.labListPipelines);
1177
1217
  } catch (error) {
1178
1218
  ctx.logger.error("labsofscience pipelines_list failed", { error: String(error) });
@@ -1322,14 +1362,22 @@ function registerInstrumentTools(server, ctx, track) {
1322
1362
  track(mcpToolName(ctx.productName, "instruments_list")),
1323
1363
  {
1324
1364
  title: "List Instruments",
1325
- description: "List lab instruments, optionally filtered by availability status.",
1365
+ description: "List lab instruments, optionally filtered by availability status and/or a name/model search term, with pagination. Returns a connection ({ items, pageInfo }).",
1326
1366
  inputSchema: {
1327
- status: InstrumentStatusEnum.optional().describe("Filter by instrument status")
1367
+ status: InstrumentStatusEnum.optional().describe("Filter by instrument status"),
1368
+ search: z.string().optional().describe("Search by name or model"),
1369
+ limit: z.number().int().optional().describe("Page size (default 50)"),
1370
+ offset: z.number().int().optional().describe("Pagination offset (default 0)"),
1371
+ cursor: z.string().optional().describe("Opaque pagination cursor")
1328
1372
  }
1329
1373
  },
1330
- async ({ status }) => {
1374
+ async ({ status, search, limit, offset, cursor }) => {
1331
1375
  try {
1332
- const data = await ctx.gql.workspace.request(LIST_INSTRUMENTS, { status });
1376
+ const data = await ctx.gql.workspace.request(LIST_INSTRUMENTS, {
1377
+ status,
1378
+ search,
1379
+ pagination: { limit: limit ?? 50, offset: offset ?? 0, cursor }
1380
+ });
1333
1381
  return jsonResult(data.labListInstruments);
1334
1382
  } catch (error) {
1335
1383
  ctx.logger.error("labsofscience instruments_list failed", { error: String(error) });
@@ -1556,7 +1604,11 @@ function registerInventoryTools(server, ctx, track) {
1556
1604
  {
1557
1605
  title: "Archive Inventory Item",
1558
1606
  description: "Archive an inventory item. Returns true on success.",
1559
- inputSchema: { id: z.string().describe("Inventory item ID") }
1607
+ inputSchema: { id: z.string().describe("Inventory item ID") },
1608
+ // BOFF-3611: irreversible via this tool surface — see datasets.ts's
1609
+ // datasets_archive for why this is an explicit per-tool override
1610
+ // rather than a fleet-wide tool-name.ts regex change.
1611
+ annotations: { destructiveHint: true }
1560
1612
  },
1561
1613
  async ({ id }) => {
1562
1614
  try {
@@ -1606,6 +1658,26 @@ function registerNotebookTools(server, ctx, track) {
1606
1658
  }
1607
1659
  }
1608
1660
  );
1661
+ server.registerTool(
1662
+ track(mcpToolName(ctx.productName, "notebook_get")),
1663
+ {
1664
+ title: "Get Notebook Entry",
1665
+ description: "Fetch a single ELN entry by ID.",
1666
+ inputSchema: { id: z.string().describe("Notebook entry ID") }
1667
+ },
1668
+ async ({ id }) => {
1669
+ try {
1670
+ const data = await ctx.gql.workspace.request(GET_NOTEBOOK_ENTRY, { id });
1671
+ if (!data.labNotebookEntry) return errorResult(`Notebook entry ${id} not found`);
1672
+ return jsonResult(data.labNotebookEntry);
1673
+ } catch (error) {
1674
+ ctx.logger.error("labsofscience notebook_get failed", { error: String(error) });
1675
+ return errorResult(
1676
+ `Failed to get notebook entry: ${error instanceof Error ? error.message : String(error)}`
1677
+ );
1678
+ }
1679
+ }
1680
+ );
1609
1681
  server.registerTool(
1610
1682
  track(mcpToolName(ctx.productName, "notebook_create")),
1611
1683
  {
@@ -1644,7 +1716,11 @@ function registerNotebookTools(server, ctx, track) {
1644
1716
  {
1645
1717
  title: "Sign Notebook Entry",
1646
1718
  description: "Sign an ELN entry for record integrity (locks editable content). Returns the signed entry.",
1647
- inputSchema: { id: z.string().describe("Notebook entry ID") }
1719
+ inputSchema: { id: z.string().describe("Notebook entry ID") },
1720
+ // BOFF-3611: irreversible — there is no "unsign" mutation. Explicit
1721
+ // override — see datasets.ts's datasets_archive for why per-tool,
1722
+ // not a fleet-wide tool-name.ts regex change.
1723
+ annotations: { destructiveHint: true }
1648
1724
  },
1649
1725
  async ({ id }) => {
1650
1726
  try {
@@ -1658,6 +1734,29 @@ function registerNotebookTools(server, ctx, track) {
1658
1734
  }
1659
1735
  }
1660
1736
  );
1737
+ server.registerTool(
1738
+ track(mcpToolName(ctx.productName, "notebook_witness")),
1739
+ {
1740
+ title: "Witness Notebook Entry",
1741
+ description: "Witness a SIGNED ELN entry as a second party for record integrity. Returns the witnessed entry.",
1742
+ inputSchema: { id: z.string().describe("Notebook entry ID") },
1743
+ // BOFF-3611: irreversible — there is no "unwitness" mutation. Explicit
1744
+ // override — see datasets.ts's datasets_archive for why per-tool,
1745
+ // not a fleet-wide tool-name.ts regex change.
1746
+ annotations: { destructiveHint: true }
1747
+ },
1748
+ async ({ id }) => {
1749
+ try {
1750
+ const data = await ctx.gql.workspace.request(WITNESS_NOTEBOOK_ENTRY, { id });
1751
+ return jsonResult(data.labWitnessNotebookEntry);
1752
+ } catch (error) {
1753
+ ctx.logger.error("labsofscience notebook_witness failed", { error: String(error) });
1754
+ return errorResult(
1755
+ `Failed to witness notebook entry: ${error instanceof Error ? error.message : String(error)}`
1756
+ );
1757
+ }
1758
+ }
1759
+ );
1661
1760
  }
1662
1761
 
1663
1762
  // src/modules/labsofscience/tools/health.ts
@@ -1732,9 +1831,9 @@ Science & discovery platform \u2014 a research LIMS / ELN.
1732
1831
 
1733
1832
  ## Tool groups
1734
1833
  projects, experiments, runs, protocols, datasets, pipelines, instruments, inventory, notebook, health.
1735
- List queries return { items, pageInfo } connections and accept a PaginationInput { limit, offset, cursor };
1736
- instruments_list and pipelines_list return plain arrays. All run against the WORKSPACE gateway
1737
- (workspace-scoped) except the public health probe.
1834
+ Every list query \u2014 including instruments_list and pipelines_list \u2014 returns { items, pageInfo }
1835
+ connections and accepts a PaginationInput { limit, offset, cursor }. All run against the WORKSPACE
1836
+ gateway (workspace-scoped) except the public health probe.
1738
1837
  `;
1739
1838
  return {
1740
1839
  contents: [{ uri: schemaUri, text: schema, mimeType: "text/plain" }]
@@ -1784,7 +1883,7 @@ instruments_list and pipelines_list return plain arrays. All run against the WOR
1784
1883
  instrumentsUri,
1785
1884
  {
1786
1885
  title: "Labsofscience Instrument Catalog",
1787
- description: "All lab instruments in the bound workspace.",
1886
+ description: "The first 50 lab instruments in the bound workspace.",
1788
1887
  mimeType: "application/json"
1789
1888
  },
1790
1889
  async () => {
@@ -1792,7 +1891,7 @@ instruments_list and pipelines_list return plain arrays. All run against the WOR
1792
1891
  try {
1793
1892
  const data = await ctx.gql.workspace.request(
1794
1893
  LIST_INSTRUMENTS,
1795
- {}
1894
+ { pagination: { limit: 50, offset: 0 } }
1796
1895
  );
1797
1896
  return {
1798
1897
  contents: [
@@ -1823,7 +1922,7 @@ instruments_list and pipelines_list return plain arrays. All run against the WOR
1823
1922
  pipelinesUri,
1824
1923
  {
1825
1924
  title: "Labsofscience Analysis Pipelines",
1826
- description: "All analysis pipelines in the bound workspace.",
1925
+ description: "The first 50 analysis pipelines in the bound workspace.",
1827
1926
  mimeType: "application/json"
1828
1927
  },
1829
1928
  async () => {
@@ -1831,7 +1930,7 @@ instruments_list and pipelines_list return plain arrays. All run against the WOR
1831
1930
  try {
1832
1931
  const data = await ctx.gql.workspace.request(
1833
1932
  LIST_PIPELINES,
1834
- {}
1933
+ { pagination: { limit: 50, offset: 0 } }
1835
1934
  );
1836
1935
  return {
1837
1936
  contents: [