@pipeshub-ai/mcp 2.2.0 → 2.3.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.
Files changed (112) hide show
  1. package/README.md +2 -0
  2. package/bin/mcp-server.js +703 -189
  3. package/bin/mcp-server.js.map +19 -13
  4. package/bin/pipeshub.js +946 -0
  5. package/bin/pipeshub.js.map +14 -0
  6. package/esm/cli/client.d.ts +54 -0
  7. package/esm/cli/client.d.ts.map +1 -0
  8. package/esm/cli/client.js +235 -0
  9. package/esm/cli/client.js.map +1 -0
  10. package/esm/cli/commands.d.ts +30 -0
  11. package/esm/cli/commands.d.ts.map +1 -0
  12. package/esm/cli/commands.js +321 -0
  13. package/esm/cli/commands.js.map +1 -0
  14. package/esm/cli/config.d.ts +73 -0
  15. package/esm/cli/config.d.ts.map +1 -0
  16. package/esm/cli/config.js +218 -0
  17. package/esm/cli/config.js.map +1 -0
  18. package/esm/cli/init-qm.d.ts +11 -0
  19. package/esm/cli/init-qm.d.ts.map +1 -0
  20. package/esm/cli/init-qm.js +170 -0
  21. package/esm/cli/init-qm.js.map +1 -0
  22. package/esm/cli/pipeshub.d.ts +2 -0
  23. package/esm/cli/pipeshub.d.ts.map +1 -0
  24. package/esm/cli/pipeshub.js +296 -0
  25. package/esm/cli/pipeshub.js.map +1 -0
  26. package/esm/funcs/connectorLookupRecordByIdentifier.d.ts +68 -0
  27. package/esm/funcs/connectorLookupRecordByIdentifier.d.ts.map +1 -0
  28. package/esm/funcs/connectorLookupRecordByIdentifier.js +138 -0
  29. package/esm/funcs/connectorLookupRecordByIdentifier.js.map +1 -0
  30. package/esm/funcs/connectorNavigateKnowledgeGraph.d.ts +75 -0
  31. package/esm/funcs/connectorNavigateKnowledgeGraph.d.ts.map +1 -0
  32. package/esm/funcs/connectorNavigateKnowledgeGraph.js +152 -0
  33. package/esm/funcs/connectorNavigateKnowledgeGraph.js.map +1 -0
  34. package/esm/lib/base64.d.ts +1 -1
  35. package/esm/lib/base64.d.ts.map +1 -1
  36. package/esm/lib/base64.js +6 -3
  37. package/esm/lib/base64.js.map +1 -1
  38. package/esm/lib/bytes-to-base64.d.ts +16 -0
  39. package/esm/lib/bytes-to-base64.d.ts.map +1 -0
  40. package/esm/lib/bytes-to-base64.js +23 -0
  41. package/esm/lib/bytes-to-base64.js.map +1 -0
  42. package/esm/mcp-server/instructions.d.ts +1 -1
  43. package/esm/mcp-server/instructions.d.ts.map +1 -1
  44. package/esm/mcp-server/instructions.js +30 -0
  45. package/esm/mcp-server/instructions.js.map +1 -1
  46. package/esm/mcp-server/shared.d.ts +2 -2
  47. package/esm/mcp-server/shared.d.ts.map +1 -1
  48. package/esm/mcp-server/shared.js +7 -3
  49. package/esm/mcp-server/shared.js.map +1 -1
  50. package/esm/mcp-server/tools/_agui.d.ts +34 -0
  51. package/esm/mcp-server/tools/_agui.d.ts.map +1 -0
  52. package/esm/mcp-server/tools/_agui.js +91 -0
  53. package/esm/mcp-server/tools/_agui.js.map +1 -0
  54. package/esm/mcp-server/tools/_helpers.d.ts +20 -3
  55. package/esm/mcp-server/tools/_helpers.d.ts.map +1 -1
  56. package/esm/mcp-server/tools/_helpers.js +54 -10
  57. package/esm/mcp-server/tools/_helpers.js.map +1 -1
  58. package/esm/mcp-server/tools/pipeshubChat.d.ts +0 -3
  59. package/esm/mcp-server/tools/pipeshubChat.d.ts.map +1 -1
  60. package/esm/mcp-server/tools/pipeshubChat.js +75 -98
  61. package/esm/mcp-server/tools/pipeshubChat.js.map +1 -1
  62. package/esm/mcp-server/tools/pipeshubDirectory.d.ts.map +1 -1
  63. package/esm/mcp-server/tools/pipeshubDirectory.js +62 -8
  64. package/esm/mcp-server/tools/pipeshubDirectory.js.map +1 -1
  65. package/esm/mcp-server/tools/pipeshubGetRecordContent.d.ts +118 -1
  66. package/esm/mcp-server/tools/pipeshubGetRecordContent.d.ts.map +1 -1
  67. package/esm/mcp-server/tools/pipeshubGetRecordContent.js +180 -29
  68. package/esm/mcp-server/tools/pipeshubGetRecordContent.js.map +1 -1
  69. package/esm/mcp-server/tools/pipeshubSearch.d.ts.map +1 -1
  70. package/esm/mcp-server/tools/pipeshubSearch.js +15 -2
  71. package/esm/mcp-server/tools/pipeshubSearch.js.map +1 -1
  72. package/esm/mcp-server/tools/pipeshubSources.js +2 -2
  73. package/esm/mcp-server/tools/pipeshubSources.js.map +1 -1
  74. package/esm/models/lookuprecordbyidentifierop.d.ts +7 -0
  75. package/esm/models/lookuprecordbyidentifierop.d.ts.map +1 -0
  76. package/esm/models/lookuprecordbyidentifierop.js +14 -0
  77. package/esm/models/lookuprecordbyidentifierop.js.map +1 -0
  78. package/esm/models/navigateknowledgegraphop.d.ts +14 -0
  79. package/esm/models/navigateknowledgegraphop.d.ts.map +1 -0
  80. package/esm/models/navigateknowledgegraphop.js +25 -0
  81. package/esm/models/navigateknowledgegraphop.js.map +1 -0
  82. package/esm/tool-names.js +4 -4
  83. package/esm/tool-names.js.map +1 -1
  84. package/package.json +3 -2
  85. package/qm/README.md +247 -0
  86. package/qm/SECURITY.md +134 -0
  87. package/qm/TROUBLESHOOTING.md +218 -0
  88. package/qm/qm.config.fragment.jsonc +42 -0
  89. package/qm/sandbox/Dockerfile +22 -0
  90. package/qm/sandbox/skills/pipeshub/SKILL.md +69 -0
  91. package/qm/sandbox/tools/pipeshub/tool.json +36 -0
  92. package/src/cli/client.ts +279 -0
  93. package/src/cli/commands.ts +403 -0
  94. package/src/cli/config.ts +229 -0
  95. package/src/cli/init-qm.ts +215 -0
  96. package/src/cli/pipeshub.ts +333 -0
  97. package/src/funcs/connectorLookupRecordByIdentifier.ts +200 -0
  98. package/src/funcs/connectorNavigateKnowledgeGraph.ts +215 -0
  99. package/src/lib/base64.ts +6 -3
  100. package/src/lib/bytes-to-base64.ts +24 -0
  101. package/src/mcp-server/instructions.ts +30 -0
  102. package/src/mcp-server/shared.ts +7 -3
  103. package/src/mcp-server/tools/_agui.ts +138 -0
  104. package/src/mcp-server/tools/_helpers.ts +55 -8
  105. package/src/mcp-server/tools/pipeshubChat.ts +78 -97
  106. package/src/mcp-server/tools/pipeshubDirectory.ts +65 -7
  107. package/src/mcp-server/tools/pipeshubGetRecordContent.ts +213 -29
  108. package/src/mcp-server/tools/pipeshubSearch.ts +15 -2
  109. package/src/mcp-server/tools/pipeshubSources.ts +2 -2
  110. package/src/models/lookuprecordbyidentifierop.ts +26 -0
  111. package/src/models/navigateknowledgegraphop.ts +56 -0
  112. package/src/tool-names.ts +4 -4
package/bin/mcp-server.js CHANGED
@@ -50456,6 +50456,36 @@ the user can verify.
50456
50456
  - \`pipeshub_agents\` — list the org's configured **agents** (specialized
50457
50457
  assistants with their own prompt, tools, and knowledge scope).
50458
50458
 
50459
+ ## Knowledge Sources
50460
+
50461
+ A source (an app connector or a KB) holds record groups (a project, space,
50462
+ drive, folder), which hold records (one ticket, page, file). Records nest —
50463
+ an epic over its stories, a page over its child pages.
50464
+
50465
+ **Every tool returns metadata or content.**
50466
+
50467
+ - \`pipeshub_search\` / \`pipeshub_chat\` — content, but only a ranked sample of
50468
+ blocks: never all blocks of a record, never every record that matches, never
50469
+ a complete list of what exists.
50470
+ - \`mode:"lookup"\` and \`mode:"navigate"\` — metadata: recordIds, fields, and
50471
+ structure. No document text. \`mode:"content"\` — one whole record.
50472
+
50473
+ **Combining them.** Search finds a starting point by wording; navigate on a
50474
+ \`recordId\` reveals what search structurally cannot — siblings, children,
50475
+ linked records, and the group's total count. For a question that must be
50476
+ exhaustive ("all", "how many", "every"), do NOT count search hits or trust a
50477
+ chat answer: navigate the record group, which reports the real total. For a
50478
+ record's siblings, navigate its parent — take the id from the \`Path\` line.
50479
+
50480
+ **A named record** — an issue key, a Confluence / Drive / Slack link, an
50481
+ external ID → \`mode:"lookup"\` FIRST. It returns that record's metadata (for a
50482
+ ticket: status, assignee, priority, dates) plus its \`recordId\`, which often
50483
+ answers the question outright. Never guess a \`recordId\`.
50484
+
50485
+ Pass a source's id from \`pipeshub_sources\` as \`apps: ["<id>"]\` to search or
50486
+ chat, and as \`nodeId\` to navigate. Navigate and lookup end in a \`Next:\` line
50487
+ naming the exact follow-up call — follow it.
50488
+
50459
50489
  ## Agents
50460
50490
 
50461
50491
  Some orgs configure **agents** for specific jobs (e.g. a Slack messenger, a
@@ -50595,10 +50625,17 @@ var init_registration = __esm(() => {
50595
50625
  init_requestid();
50596
50626
  });
50597
50627
 
50598
- // src/lib/base64.ts
50628
+ // src/lib/bytes-to-base64.ts
50599
50629
  function bytesToBase64(u8arr) {
50600
- return btoa(String.fromCodePoint(...u8arr));
50630
+ let binary = "";
50631
+ for (let i = 0;i < u8arr.length; i += BASE64_CHUNK) {
50632
+ binary += String.fromCharCode(...u8arr.subarray(i, i + BASE64_CHUNK));
50633
+ }
50634
+ return btoa(binary);
50601
50635
  }
50636
+ var BASE64_CHUNK = 32768;
50637
+
50638
+ // src/lib/base64.ts
50602
50639
  function bytesFromBase64(encoded) {
50603
50640
  return Uint8Array.from(atob(encoded), (c) => c.charCodeAt(0));
50604
50641
  }
@@ -51919,9 +51956,6 @@ function createRegisterPrompt(logger, server, getSDK, allowedScopes) {
51919
51956
  }
51920
51957
 
51921
51958
  // src/mcp-server/shared.ts
51922
- function bytesToBase642(u8arr) {
51923
- return btoa(String.fromCodePoint(...u8arr));
51924
- }
51925
51959
  async function consumeStream(stream) {
51926
51960
  const reader = stream.getReader();
51927
51961
  const chunks = [];
@@ -51942,13 +51976,13 @@ async function valueToBase64(value) {
51942
51976
  if (value == null) {
51943
51977
  return null;
51944
51978
  } else if (value instanceof Uint8Array) {
51945
- return bytesToBase642(value);
51979
+ return bytesToBase64(value);
51946
51980
  } else if (value instanceof ArrayBuffer) {
51947
- return bytesToBase642(new Uint8Array(value));
51981
+ return bytesToBase64(new Uint8Array(value));
51948
51982
  } else if (value instanceof Response || value instanceof Blob) {
51949
- return bytesToBase642(new Uint8Array(await value.arrayBuffer()));
51983
+ return bytesToBase64(new Uint8Array(await value.arrayBuffer()));
51950
51984
  } else if (value instanceof ReadableStream) {
51951
- return bytesToBase642(await consumeStream(value));
51985
+ return bytesToBase64(await consumeStream(value));
51952
51986
  } else if (typeof value === "string") {
51953
51987
  return base64Schema.parse(value);
51954
51988
  } else {
@@ -52912,6 +52946,77 @@ var init_agentsStreamMessage = __esm(() => {
52912
52946
  init_async();
52913
52947
  });
52914
52948
 
52949
+ // src/mcp-server/tools/_agui.ts
52950
+ function newChatStreamState() {
52951
+ return {
52952
+ conversation: null,
52953
+ recordsUsed: undefined,
52954
+ error: null,
52955
+ conversationId: null,
52956
+ title: null,
52957
+ text: new Map,
52958
+ rootMessageIds: new Set
52959
+ };
52960
+ }
52961
+ function applyAGUIFrame(state, frame) {
52962
+ const data = frame.data;
52963
+ const d = data && typeof data === "object" ? data : {};
52964
+ switch (frame.event) {
52965
+ case AGUIEvent.RUN_FINISHED: {
52966
+ const result = d.result;
52967
+ if (!result || typeof result !== "object")
52968
+ return false;
52969
+ state.conversation = result.conversation ?? null;
52970
+ state.recordsUsed = result.recordsUsed ?? result.meta?.recordsUsed;
52971
+ return true;
52972
+ }
52973
+ case AGUIEvent.RUN_ERROR: {
52974
+ state.error = (typeof data === "string" ? data : null) ?? d.message ?? d.error ?? frame.raw ?? "Stream error";
52975
+ return true;
52976
+ }
52977
+ case AGUIEvent.CUSTOM: {
52978
+ if (d.name === "conversation_created") {
52979
+ const value = d.value ?? {};
52980
+ state.conversationId = value.conversationId ?? state.conversationId;
52981
+ state.title = value.title ?? state.title;
52982
+ }
52983
+ return false;
52984
+ }
52985
+ case AGUIEvent.TEXT_MESSAGE_CONTENT: {
52986
+ if (typeof d.delta !== "string" || d.delta.length === 0)
52987
+ return false;
52988
+ const id = typeof d.messageId === "string" ? d.messageId : "";
52989
+ state.text.set(id, (state.text.get(id) ?? "") + d.delta);
52990
+ if (d.parentRunId == null)
52991
+ state.rootMessageIds.add(id);
52992
+ return false;
52993
+ }
52994
+ default:
52995
+ return false;
52996
+ }
52997
+ }
52998
+ function salvagedText(state) {
52999
+ const pick2 = (ids) => {
53000
+ let best = null;
53001
+ for (const id of ids) {
53002
+ const text = state.text.get(id);
53003
+ if (text && (best === null || text.length > best.length))
53004
+ best = text;
53005
+ }
53006
+ return best;
53007
+ };
53008
+ return pick2(state.rootMessageIds) ?? pick2(state.text.keys());
53009
+ }
53010
+ var AGUIEvent;
53011
+ var init__agui = __esm(() => {
53012
+ AGUIEvent = {
53013
+ RUN_FINISHED: "RUN_FINISHED",
53014
+ RUN_ERROR: "RUN_ERROR",
53015
+ CUSTOM: "CUSTOM",
53016
+ TEXT_MESSAGE_CONTENT: "TEXT_MESSAGE_CONTENT"
53017
+ };
53018
+ });
53019
+
52915
53020
  // src/funcs/agentsListAgents.ts
52916
53021
  function agentsListAgents(client$, request, options) {
52917
53022
  return new APIPromise($do5(client$, request, options));
@@ -52993,7 +53098,10 @@ var init_agentsListAgents = __esm(() => {
52993
53098
  });
52994
53099
 
52995
53100
  // src/mcp-server/tools/_helpers.ts
52996
- async function readJson(response) {
53101
+ async function readJson(response, context = "PipesHub request") {
53102
+ const httpErr = await httpErrorResult(response, context);
53103
+ if (httpErr)
53104
+ return { ok: false, result: httpErr };
52997
53105
  const text = await response.text();
52998
53106
  if (!text) {
52999
53107
  return {
@@ -53022,6 +53130,13 @@ ${text.slice(0, 500)}`
53022
53130
  };
53023
53131
  }
53024
53132
  }
53133
+ function expiredTokenError(exp) {
53134
+ if (typeof exp !== "number" || !Number.isFinite(exp))
53135
+ return null;
53136
+ if (exp * 1000 > Date.now())
53137
+ return null;
53138
+ return errorResult(`The access token expired on ${new Date(exp * 1000).toISOString()}. ` + "Mint a new personal access token in PipesHub under " + "Developer Settings → Personal Access Tokens.");
53139
+ }
53025
53140
  function jsonResult(value) {
53026
53141
  return {
53027
53142
  content: [{ type: "text", text: JSON.stringify(value, null, 2) }]
@@ -53048,11 +53163,15 @@ async function httpErrorResult(response, context) {
53048
53163
  message = candidate || body;
53049
53164
  } catch {}
53050
53165
  }
53051
- const detail = message ? ` ${message.slice(0, 400)}` : "";
53166
+ const reason = message.slice(0, 400).trim();
53167
+ const detail = reason ? ` ${/[.!?]$/.test(reason) ? reason : `${reason}.`}` : "";
53052
53168
  const auth = response.status === 401 || response.status === 403 ? " Check that the bearer token / credentials are valid and not expired." : "";
53053
53169
  return errorResult(`${context} failed (HTTP ${response.status} ${response.statusText}).${detail}${auth}`);
53054
53170
  }
53055
- async function readValidated(response, schema) {
53171
+ async function readValidated(response, schema, context = "PipesHub request") {
53172
+ const httpErr = await httpErrorResult(response, context);
53173
+ if (httpErr)
53174
+ return { ok: false, result: httpErr };
53056
53175
  const text = await response.text();
53057
53176
  if (!text) {
53058
53177
  return { ok: false, result: errorResult("Empty response from server") };
@@ -53121,15 +53240,17 @@ function trimCitations(citations) {
53121
53240
  if (!Array.isArray(citations))
53122
53241
  return [];
53123
53242
  return citations.map((c) => {
53124
- const md = c?.metadata ?? {};
53243
+ const cd = c?.citationData ?? c;
53244
+ const md = cd?.metadata ?? c?.metadata ?? {};
53245
+ const content = typeof cd?.content === "string" ? cd.content : c?.content;
53125
53246
  return {
53126
- recordId: md.recordId ?? c?.recordId,
53127
- recordName: md.recordName ?? c?.recordName,
53128
- snippet: typeof c?.content === "string" ? c.content.slice(0, 280) : md.blockText ?? null,
53247
+ recordId: md.recordId ?? cd?.recordId ?? c?.recordId,
53248
+ recordName: md.recordName ?? cd?.recordName ?? c?.recordName,
53249
+ snippet: typeof content === "string" ? content.slice(0, 280) : md.blockText ?? null,
53129
53250
  mimeType: md.mimeType,
53130
53251
  webUrl: md.webUrl,
53131
53252
  pageNum: md.pageNum,
53132
- chunkIndex: c?.chunkIndex ?? md.chunkIndex
53253
+ chunkIndex: cd?.chunkIndex ?? c?.chunkIndex ?? md.chunkIndex
53133
53254
  };
53134
53255
  });
53135
53256
  }
@@ -53302,9 +53423,10 @@ var init_pipeshubChat = __esm(() => {
53302
53423
  init_conversationsStreamMessage();
53303
53424
  init_agentsStreamConversation();
53304
53425
  init_agentsStreamMessage();
53426
+ init__agui();
53305
53427
  init__helpers();
53306
53428
  FiltersShape = object({
53307
- apps: array(string2()).optional().describe("Source-scoping ids. Mix connector instance UUIDs with the synthetic " + "`knowledgeBase_<orgId>` id (use pipeshub_sources to discover them). " + "Empty / omitted means no app-side restriction."),
53429
+ apps: array(string2()).optional().describe("Source-scoping ids from `pipeshub_sources` — connector instance and / " + "or knowledge base ids, mixed freely. The legacy org-wide " + "`knowledgeBase_<orgId>` id is still accepted on deployments that " + "predate per-KB sources. Empty / omitted means no app-side " + "restriction."),
53308
53430
  kb: array(string2()).optional().describe("Legacy / unused. Leave empty.")
53309
53431
  }).optional();
53310
53432
  args = {
@@ -53318,40 +53440,45 @@ var init_pipeshubChat = __esm(() => {
53318
53440
  chatMode: _enum([
53319
53441
  "internal_search",
53320
53442
  "web_search",
53321
- "auto",
53322
- "quick",
53323
- "verification",
53324
- "deep"
53443
+ "quick"
53325
53444
  ]).optional().describe("Response strategy. The valid values depend on whether `agentId` is set:\n" + "- WITHOUT `agentId` (plain chat): `internal_search` — answer from the " + "org's indexed knowledge (default) — or `web_search` — answer from the " + `live web.
53326
- ` + "- WITH `agentId` (agent chat): `auto` (let the agent decide; default), " + "`quick`, `verification`, or `deep`.")
53445
+ ` + "- WITH `agentId` (agent chat): `quick` is the only supported mode and " + "is sent automatically, so this argument can be omitted.")
53327
53446
  };
53328
53447
  tool$pipeshubChat = {
53329
53448
  name: "pipeshub_chat",
53330
- description: `**Primary chat tool handles both internal knowledge queries and web search.**
53331
-
53332
- **Internal search** (default, \`chatMode: "internal_search"\`): Use whenever
53333
- the user asks about their documents, files, knowledge base, company policies,
53334
- or anything that could plausibly be answered by content in their PipesHub-indexed
53335
- sources (Drive, Box, Confluence, Slack, Gmail, Jira, the org's KB, ...).
53336
- Grounds the answer in the user's actual data and returns citations.
53337
- Answers come from a few retrieved passages, not whole documents — for
53338
- any task needing a document's full content, use
53339
- \`pipeshub_get_record_content\` instead.
53340
-
53341
- **Web search** (\`chatMode: "web_search"\`): Use when the user asks about
53342
- current events, public information, or anything unlikely to be in the org's
53343
- internal knowledge base. Pass \`chatMode: "web_search"\` and this tool will
53344
- search the public web instead.
53345
-
53346
- **When to pick this over other tools:**
53347
- - "Summarize <doc>" / "key points of <doc>" / "what does <document> say
53348
- about X?" → NOT this tool. Use \`pipeshub_search\` →
53349
- \`pipeshub_get_record_content\`: answering for a specific document
53350
- requires its full content, and chat only sees a few retrieved
53351
- passages, never the whole document.
53449
+ description: `Ask a question, get an answer grounded in the org's indexed data with
53450
+ citations. It reads a few retrieved passages — never a whole document,
53451
+ never a complete list.
53452
+
53453
+ **Three questions this tool gets WRONG. Check them first:**
53454
+ - **Structure** "what's under this epic?", "which pages are in this
53455
+ space?", "what links to this ticket?", "what's in this folder?"
53456
+ \`pipeshub_get_record_content\` \`mode:"navigate"\`. Ranking cannot see how
53457
+ records relate.
53458
+ - **Exhaustive** — "how many X?", "list ALL the Y", "every Z" →
53459
+ \`mode:"navigate"\`, which reports the group's real total. This tool
53460
+ undercounts and will not say so.
53461
+ - **One named document** — summarize it, extract from it, what does it say
53462
+ about X \`pipeshub_search\` for the \`recordId\`, then \`mode:"content"\`.
53463
+
53464
+ Everything else about the org's knowledge belongs here: policies,
53465
+ processes, decisions, history, "what do we know about X", and any question
53466
+ spanning several documents.
53467
+
53468
+ **Internal search** (default, \`chatMode: "internal_search"\`): the user's
53469
+ documents, files, knowledge base, company policies anything in their
53470
+ PipesHub-indexed sources (Drive, Box, Confluence, Slack, Gmail, Jira, the
53471
+ org's KB, ...).
53472
+
53473
+ **Web search** (\`chatMode: "web_search"\`): current events or public
53474
+ information unlikely to be in the org's knowledge base.
53475
+
53476
+ Both are plain-chat modes. **Agent chat** — pass an \`agentId\` from
53477
+ \`pipeshub_agents\` — runs against that agent's own prompt, tools and knowledge;
53478
+ \`quick\` is its only mode, requires the \`agentId\`, and is sent automatically.
53479
+
53352
53480
  - "What's our policy on Y?" → \`pipeshub_chat\` (internal_search)
53353
53481
  - "What's in the news about Z?" → \`pipeshub_chat\` (web_search)
53354
- - "What is the latest version of <library>?" → \`pipeshub_chat\` (web_search)
53355
53482
  - "Find / locate the file named X" → \`pipeshub_search\` (then
53356
53483
  \`pipeshub_download_record\` if the user wants the bytes).
53357
53484
 
@@ -53381,9 +53508,10 @@ cited document, take \`citations[*].recordId\` and call
53381
53508
  args,
53382
53509
  tool: async (client, args2, ctx) => {
53383
53510
  const fetchOptions = { signal: ctx.signal };
53511
+ const plainChatMode = args2.chatMode === "web_search" ? "web_search" : "internal_search";
53384
53512
  let response;
53385
53513
  if (args2.agentId) {
53386
- const agentChatMode = args2.chatMode ?? "auto";
53514
+ const agentChatMode = "quick";
53387
53515
  if (args2.conversationId) {
53388
53516
  const [result] = await agentsStreamMessage(client, {
53389
53517
  agentKey: args2.agentId,
@@ -53423,7 +53551,7 @@ cited document, take \`citations[*].recordId\` and call
53423
53551
  modelKey: args2.modelKey,
53424
53552
  modelName: args2.modelName,
53425
53553
  modelFriendlyName: args2.modelFriendlyName,
53426
- chatMode: args2.chatMode ?? "internal_search"
53554
+ chatMode: plainChatMode
53427
53555
  }
53428
53556
  }, { fetchOptions }).$inspect();
53429
53557
  if (!result.ok)
@@ -53436,7 +53564,7 @@ cited document, take \`citations[*].recordId\` and call
53436
53564
  modelKey: args2.modelKey,
53437
53565
  modelName: args2.modelName,
53438
53566
  modelFriendlyName: args2.modelFriendlyName,
53439
- chatMode: args2.chatMode ?? "internal_search"
53567
+ chatMode: plainChatMode
53440
53568
  }, { fetchOptions }).$inspect();
53441
53569
  if (!result.ok)
53442
53570
  return errorResult(result.error.message);
@@ -53445,62 +53573,36 @@ cited document, take \`citations[*].recordId\` and call
53445
53573
  const httpErr = await httpErrorResult(response, "PipesHub chat request");
53446
53574
  if (httpErr)
53447
53575
  return httpErr;
53448
- let finalConversation = null;
53449
- let recordsUsed;
53450
- let lastAccumulated = null;
53451
- let errorMessage = null;
53576
+ const state = newChatStreamState();
53452
53577
  try {
53453
53578
  for await (const frame of iterateSSE(response)) {
53454
- switch (frame.event) {
53455
- case "complete": {
53456
- const d = frame.data ?? {};
53457
- finalConversation = d.conversation ?? null;
53458
- recordsUsed = d.recordsUsed ?? d.meta?.recordsUsed;
53459
- break;
53460
- }
53461
- case "error": {
53462
- const d = frame.data ?? {};
53463
- errorMessage = typeof d === "string" ? d : d.error ?? d.message ?? frame.raw ?? "Stream error";
53464
- break;
53465
- }
53466
- case "answer_chunk": {
53467
- const d = frame.data ?? {};
53468
- if (typeof d.accumulated === "string") {
53469
- lastAccumulated = d.accumulated;
53470
- } else if (typeof d.content === "string") {
53471
- lastAccumulated = (lastAccumulated ?? "") + d.content;
53472
- }
53473
- break;
53474
- }
53475
- default:
53476
- break;
53477
- }
53478
- if (finalConversation || errorMessage)
53579
+ if (applyAGUIFrame(state, frame))
53479
53580
  break;
53480
53581
  }
53481
53582
  } catch (e) {
53482
53583
  return errorResult(`SSE stream failed: ${e.message}`);
53483
53584
  }
53484
- if (errorMessage)
53485
- return errorResult(errorMessage);
53486
- if (finalConversation) {
53585
+ if (state.error)
53586
+ return errorResult(state.error);
53587
+ if (state.conversation) {
53487
53588
  return jsonResult({
53488
- ...trimConversation(finalConversation),
53489
- recordsUsed
53589
+ ...trimConversation(state.conversation),
53590
+ recordsUsed: state.recordsUsed
53490
53591
  });
53491
53592
  }
53492
- if (lastAccumulated) {
53593
+ const salvaged = salvagedText(state);
53594
+ if (salvaged) {
53493
53595
  return jsonResult({
53494
- conversationId: null,
53495
- title: null,
53596
+ conversationId: state.conversationId,
53597
+ title: state.title,
53496
53598
  status: "Inprogress",
53497
- answer: lastAccumulated,
53599
+ answer: salvaged,
53498
53600
  confidence: null,
53499
53601
  citations: [],
53500
53602
  followUpQuestions: [],
53501
53603
  messageCount: 0,
53502
- recordsUsed,
53503
- warning: "Stream ended without a `complete` frame; answer is " + "the last accumulated chunk and citations are unavailable."
53604
+ recordsUsed: state.recordsUsed,
53605
+ warning: "Stream ended without a terminal RUN_FINISHED; answer is the " + "accumulated TEXT_MESSAGE_CONTENT and citations are unavailable."
53504
53606
  });
53505
53607
  }
53506
53608
  return errorResult("Stream ended without any usable frames.");
@@ -53669,10 +53771,23 @@ Typical uses:
53669
53771
  - Show the user a ranked list of matching files when they ask "find /
53670
53772
  search for X".
53671
53773
 
53774
+ Not for structural questions — what is under this epic, which pages are
53775
+ in this space, what links to this ticket. Ranking by content cannot show
53776
+ how records relate; use \`pipeshub_get_record_content\` \`mode:"navigate"\`.
53777
+
53778
+ **A ranked sample, never a complete list.** Hits are the top-scoring
53779
+ blocks from the best-matching records — not all blocks of any record, and
53780
+ not every record that matches. Never count them to answer "how many" /
53781
+ "all" / "every"; navigate the record group instead, which reports its
53782
+ real total.
53783
+
53672
53784
  The response is trimmed to one row per hit:
53673
53785
  \`{ recordId, recordName, score, snippet, mimeType, webUrl, ... }\`.
53674
53786
  Highest \`score\` first; multiple hits may share the same \`recordId\`
53675
- (different chunks of the same record).`,
53787
+ (different blocks of the same record).
53788
+
53789
+ When presenting results to the user, link each record using its
53790
+ \`webUrl\` (when present).`,
53676
53791
  scopes: ["read"],
53677
53792
  annotations: {
53678
53793
  title: "Semantic search",
@@ -53690,7 +53805,7 @@ Highest \`score\` first; multiple hits may share the same \`recordId\`
53690
53805
  }, { fetchOptions: { signal: ctx.signal } }).$inspect();
53691
53806
  if (!result.ok)
53692
53807
  return errorResult(result.error.message);
53693
- const parsed = await readJson(result.value);
53808
+ const parsed = await readJson(result.value, "PipesHub search");
53694
53809
  if (!parsed.ok)
53695
53810
  return parsed.result;
53696
53811
  const sr = parsed.value.searchResponse ?? {};
@@ -53966,44 +54081,306 @@ var init_connectorGetRecordContent = __esm(() => {
53966
54081
  init_async();
53967
54082
  });
53968
54083
 
54084
+ // src/models/lookuprecordbyidentifierop.ts
54085
+ var LookupRecordByIdentifierRequest$zodSchema;
54086
+ var init_lookuprecordbyidentifierop = __esm(() => {
54087
+ init_zod();
54088
+ LookupRecordByIdentifierRequest$zodSchema = object({
54089
+ connectorName: string2().describe("Connector hint to prioritise resolution order, e.g. `JIRA`, `CONFLUENCE`, `GOOGLE_DRIVE`, `SLACK`. Cannot widen beyond the connectors the caller can access.").optional(),
54090
+ identifiers: array(string2().min(1).max(2048)).min(1).max(10).describe("The references to resolve: URLs, issue keys such as `PA-1787`, or bare external system IDs. Paste each exactly as you found it — tracking parameters and fragments are handled. Up to 10 per call.")
54091
+ });
54092
+ });
54093
+
54094
+ // src/funcs/connectorLookupRecordByIdentifier.ts
54095
+ function connectorLookupRecordByIdentifier(client$, request, options) {
54096
+ return new APIPromise($do9(client$, request, options));
54097
+ }
54098
+ async function $do9(client$, request, options) {
54099
+ const parsed$ = safeParse4(request, (value$) => LookupRecordByIdentifierRequest$zodSchema.parse(value$), "Input validation failed");
54100
+ if (!parsed$.ok) {
54101
+ return [parsed$, { status: "invalid" }];
54102
+ }
54103
+ const payload$ = parsed$.value;
54104
+ const body$ = null;
54105
+ const path$ = pathToFunc("/connectors/record/lookup")();
54106
+ const query$ = encodeFormQuery({
54107
+ connectorName: payload$.connectorName,
54108
+ identifiers: payload$.identifiers
54109
+ });
54110
+ const headers$ = new Headers(compactMap({
54111
+ Accept: "application/json"
54112
+ }));
54113
+ const securityInput = await extractSecurity(client$._options.security);
54114
+ const requestSecurity = resolveGlobalSecurity(securityInput);
54115
+ const context = {
54116
+ options: client$._options,
54117
+ baseURL: options?.serverURL ?? client$._baseURL ?? "",
54118
+ operationID: "lookupRecordByIdentifier",
54119
+ oAuth2Scopes: ["kb:read", "connector:read"],
54120
+ resolvedSecurity: requestSecurity,
54121
+ securitySource: client$._options.security,
54122
+ retryConfig: options?.retries || client$._options.retryConfig || { strategy: "none" },
54123
+ retryCodes: options?.retryCodes || [
54124
+ "429",
54125
+ "500",
54126
+ "502",
54127
+ "503",
54128
+ "504"
54129
+ ]
54130
+ };
54131
+ const requestRes = client$._createRequest(context, {
54132
+ security: requestSecurity,
54133
+ method: "GET",
54134
+ baseURL: options?.serverURL,
54135
+ path: path$,
54136
+ headers: headers$,
54137
+ query: query$,
54138
+ body: body$,
54139
+ userAgent: client$._options.userAgent,
54140
+ timeoutMs: options?.timeoutMs || client$._options.timeoutMs || -1
54141
+ }, options);
54142
+ if (!requestRes.ok) {
54143
+ return [requestRes, { status: "invalid" }];
54144
+ }
54145
+ const req$ = requestRes.value;
54146
+ const doResult = await client$._do(req$, {
54147
+ context,
54148
+ errorCodes: [],
54149
+ retryConfig: context.retryConfig,
54150
+ retryCodes: context.retryCodes
54151
+ });
54152
+ if (!doResult.ok) {
54153
+ return [doResult, { status: "request-error", request: req$ }];
54154
+ }
54155
+ return [doResult, {
54156
+ status: "complete",
54157
+ request: req$,
54158
+ response: doResult.value
54159
+ }];
54160
+ }
54161
+ var init_connectorLookupRecordByIdentifier = __esm(() => {
54162
+ init_encodings();
54163
+ init_primitives();
54164
+ init_schemas4();
54165
+ init_security2();
54166
+ init_url();
54167
+ init_lookuprecordbyidentifierop();
54168
+ init_async();
54169
+ });
54170
+
54171
+ // src/models/navigateknowledgegraphop.ts
54172
+ var NavigateKnowledgeGraphRequest$zodSchema;
54173
+ var init_navigateknowledgegraphop = __esm(() => {
54174
+ init_zod();
54175
+ NavigateKnowledgeGraphRequest$zodSchema = object({
54176
+ createdAfter: string2().describe("Filter children by source creation time. ISO 8601 `YYYY-MM-DD`, or a full datetime that MUST carry a timezone offset — a naive datetime is rejected rather than assumed to be UTC.").optional(),
54177
+ createdBefore: string2().describe("Filter children by source creation time. `YYYY-MM-DD` is inclusive of the whole day.").optional(),
54178
+ depth: int().min(1).max(3).describe("Levels of descendants to return in one call. Above 1, `rows` is a flat list of all descendants down to that level rather than only direct children, and each row carries its own `level`.").optional(),
54179
+ limit: int().min(50).max(200).describe("Children per page. The minimum is 50 — smaller values are rejected rather than silently raised.").optional(),
54180
+ modifiedAfter: string2().describe("Filter children by source modification time.").optional(),
54181
+ modifiedBefore: string2().describe("Filter children by source modification time.").optional(),
54182
+ nodeId: string2().min(1).max(2048).describe("The node to open. Take it from an `id` in a previous navigate or lookup response. Omit it entirely for the flat listing of everything reachable — the usual starting point. A URL or an issue key such as `PA-1787` also works: it is resolved to its record automatically.").optional(),
54183
+ nodeTypes: array(string2()).describe("Restrict children to these node types. Repeat the parameter for multiple types: `?nodeTypes=record&nodeTypes=folder`.").optional(),
54184
+ page: int().min(1).describe("Page number, 1-indexed.").optional()
54185
+ });
54186
+ });
54187
+
54188
+ // src/funcs/connectorNavigateKnowledgeGraph.ts
54189
+ function connectorNavigateKnowledgeGraph(client$, request, options) {
54190
+ return new APIPromise($do10(client$, request, options));
54191
+ }
54192
+ async function $do10(client$, request, options) {
54193
+ const parsed$ = safeParse4(request, (value$) => NavigateKnowledgeGraphRequest$zodSchema.optional().parse(value$), "Input validation failed");
54194
+ if (!parsed$.ok) {
54195
+ return [parsed$, { status: "invalid" }];
54196
+ }
54197
+ const payload$ = parsed$.value;
54198
+ const body$ = null;
54199
+ const path$ = pathToFunc("/connectors/navigate")();
54200
+ const query$ = encodeFormQuery({
54201
+ createdAfter: payload$?.createdAfter,
54202
+ createdBefore: payload$?.createdBefore,
54203
+ depth: payload$?.depth,
54204
+ limit: payload$?.limit,
54205
+ modifiedAfter: payload$?.modifiedAfter,
54206
+ modifiedBefore: payload$?.modifiedBefore,
54207
+ nodeId: payload$?.nodeId,
54208
+ nodeTypes: payload$?.nodeTypes,
54209
+ page: payload$?.page
54210
+ });
54211
+ const headers$ = new Headers(compactMap({
54212
+ Accept: "application/json"
54213
+ }));
54214
+ const securityInput = await extractSecurity(client$._options.security);
54215
+ const requestSecurity = resolveGlobalSecurity(securityInput);
54216
+ const context = {
54217
+ options: client$._options,
54218
+ baseURL: options?.serverURL ?? client$._baseURL ?? "",
54219
+ operationID: "navigateKnowledgeGraph",
54220
+ oAuth2Scopes: ["kb:read", "connector:read"],
54221
+ resolvedSecurity: requestSecurity,
54222
+ securitySource: client$._options.security,
54223
+ retryConfig: options?.retries || client$._options.retryConfig || { strategy: "none" },
54224
+ retryCodes: options?.retryCodes || [
54225
+ "429",
54226
+ "500",
54227
+ "502",
54228
+ "503",
54229
+ "504"
54230
+ ]
54231
+ };
54232
+ const requestRes = client$._createRequest(context, {
54233
+ security: requestSecurity,
54234
+ method: "GET",
54235
+ baseURL: options?.serverURL,
54236
+ path: path$,
54237
+ headers: headers$,
54238
+ query: query$,
54239
+ body: body$,
54240
+ userAgent: client$._options.userAgent,
54241
+ timeoutMs: options?.timeoutMs || client$._options.timeoutMs || -1
54242
+ }, options);
54243
+ if (!requestRes.ok) {
54244
+ return [requestRes, { status: "invalid" }];
54245
+ }
54246
+ const req$ = requestRes.value;
54247
+ const doResult = await client$._do(req$, {
54248
+ context,
54249
+ errorCodes: [],
54250
+ retryConfig: context.retryConfig,
54251
+ retryCodes: context.retryCodes
54252
+ });
54253
+ if (!doResult.ok) {
54254
+ return [doResult, { status: "request-error", request: req$ }];
54255
+ }
54256
+ return [doResult, {
54257
+ status: "complete",
54258
+ request: req$,
54259
+ response: doResult.value
54260
+ }];
54261
+ }
54262
+ var init_connectorNavigateKnowledgeGraph = __esm(() => {
54263
+ init_encodings();
54264
+ init_primitives();
54265
+ init_schemas4();
54266
+ init_security2();
54267
+ init_url();
54268
+ init_navigateknowledgegraphop();
54269
+ init_async();
54270
+ });
54271
+
53969
54272
  // src/mcp-server/tools/pipeshubGetRecordContent.ts
54273
+ async function renderedResult(response) {
54274
+ const parsed = await readJson(response);
54275
+ if (!parsed.ok)
54276
+ return parsed.result;
54277
+ const text = parsed.value?.text;
54278
+ return typeof text === "string" && text.trim().length > 0 ? { content: [{ type: "text", text }] } : jsonResult(parsed.value);
54279
+ }
53970
54280
  var args4, tool$pipeshubGetRecordContent;
53971
54281
  var init_pipeshubGetRecordContent = __esm(() => {
53972
54282
  init_zod();
53973
54283
  init_connectorGetRecordContent();
54284
+ init_connectorLookupRecordByIdentifier();
54285
+ init_connectorNavigateKnowledgeGraph();
53974
54286
  init__helpers();
53975
54287
  args4 = {
53976
- recordId: string2().min(1).describe("Record identifier usually a UUID for connector-sourced records or " + "a 24-character ObjectId for uploaded records. Get it from a chat " + "citation (`citations[*].recordId`) or from a `pipeshub_search` hit.")
54288
+ mode: _enum(["content", "navigate", "lookup"]).default("content").describe("`content` (default) reads a record's full text by `recordId`. " + "`lookup` resolves a URL / issue key / external ID to a recordId. " + "`navigate` browses the knowledge graph tree."),
54289
+ recordId: string2().min(1).optional().describe("Record identifier — usually a UUID for connector-sourced records or " + "a 24-character ObjectId for uploaded records. Get it from a chat " + "citation (`citations[*].recordId`) or from a `pipeshub_search` hit. " + "Required when `mode` is `content`."),
54290
+ nodeId: string2().min(1).max(2048).optional().describe("The node to open. Take it from a `record_id=` or `node_id=` shown in a " + "previous navigate or lookup response, from a search hit's `recordId`, " + "or from a `pipeshub_sources` id — a KB or connector id opens that " + "source directly. Omit it entirely for the flat listing of everything " + "reachable, newest first — the usual starting point. A URL or an issue " + "key such as `PA-1787` also works: it is resolved to its record " + "automatically, so no separate lookup is needed."),
54291
+ page: int().min(1).default(1).describe("Page number, 1-indexed."),
54292
+ limit: int().min(50).max(200).default(50).describe("Children per page. The minimum is 50 — smaller values are rejected " + "rather than silently raised."),
54293
+ depth: int().min(1).max(3).default(1).describe("Levels of descendants to return in one call. Above 1, the listing is a " + "flat list of all descendants down to that level rather than only " + "direct children, and each row carries its own `level`."),
54294
+ nodeTypes: array(string2()).optional().describe('Restrict children to these node types, e.g. `["record", "folder"]`.'),
54295
+ createdAfter: string2().optional().describe("Filter children by source creation time. ISO 8601 `YYYY-MM-DD`, or a " + "full datetime that MUST carry a timezone offset — a naive datetime is " + "rejected rather than assumed to be UTC."),
54296
+ createdBefore: string2().optional().describe("Filter children by source creation time. `YYYY-MM-DD` is inclusive of " + "the whole day."),
54297
+ modifiedAfter: string2().optional().describe("Filter children by source modification time. Same formats as " + "`createdAfter`."),
54298
+ modifiedBefore: string2().optional().describe("Filter children by source modification time. Same formats as " + "`createdBefore`."),
54299
+ identifiers: union([
54300
+ string2().min(1).max(2048),
54301
+ array(string2().min(1).max(2048)).min(1).max(10)
54302
+ ]).optional().describe("The reference(s) to resolve: a URL, an issue key such as `PA-1787`, or " + "a bare external system ID. Paste each exactly as you found it — " + "tracking parameters and fragments are handled. Pass a single string, " + "or an array of up to 10 to resolve them in one call. Required when " + "`mode` is `lookup`."),
54303
+ connectorName: string2().optional().describe("Optional hint that prioritises resolution order, e.g. `JIRA`, " + "`CONFLUENCE`, `GOOGLE_DRIVE`, `SLACK`. It cannot widen the search beyond the " + "connectors you can already access. Useful on a retry when a lookup " + "came back empty.")
53977
54304
  };
53978
54305
  tool$pipeshubGetRecordContent = {
53979
54306
  name: "pipeshub_get_record_content",
53980
- description: `Read a record's full parsed content by \`recordId\` the only way to
53981
- see a document's COMPLETE text.
53982
-
53983
- Use it whenever the answer depends on a document's full content — any
53984
- task where missing a part could make the answer wrong: summarize / key
53985
- points / action items; extract or list ALL of something; check whether
53986
- or where the doc mentions X; translate, rewrite, outline, or review the
53987
- doc; compare named docs (fetch each); any question scoped to one named
53988
- document. \`pipeshub_chat\` cannot do these it only sees a few
53989
- retrieved passages, never the whole document. Get the \`recordId\` from
53990
- a \`pipeshub_search\` top hit or a chat citation.
53991
-
53992
- Judge by the user's INTENT, not their keywords: they need not say
53993
- "summarize", "key points", or "extract". Reason about what a good
53994
- answer requires if it would need the whole document (e.g. "what's
53995
- this doc about?", "walk me through the report", "anything in here
53996
- about Y?"), that is a full-content task, so call this tool.
53997
-
53998
- Returns a single \`content\` string: a short metadata header (title,
53999
- source, key fields, and a pre-generated summary) followed by the
54000
- record's full parsed text paragraphs, tables, and lists in reading
54001
- order. For a record with no extractable content, \`content\` is the
54002
- literal \`No record found\`. Use \`pipeshub_download_record\` only when
54003
- you need the original file bytes.`,
54307
+ description: `Three operations on the org's records. Pick by what you hold:
54308
+
54309
+ \`mode:"lookup"\` — a URL, issue key (PA-1787), or external ID
54310
+ its recordId plus the record's metadata
54311
+ \`mode:"navigate"\` a question about structure: what is under X,
54312
+ what links to Y browses the hierarchy
54313
+ \`mode:"content"\` — a recordId, and you need the document's COMPLETE text
54314
+
54315
+ **\`mode:"content"\` (default)**the only way to see a document's complete
54316
+ text. Use it whenever missing part of the document could make the answer
54317
+ wrong: summarize, extract or list ALL of something, check whether or where
54318
+ a doc mentions X, review, or compare named docs. \`pipeshub_chat\` cannot do
54319
+ these it never sees a whole document.
54320
+
54321
+ Judge by the user's INTENT, not their keywords: "what's this doc about?",
54322
+ "walk me through the report", "anything in here about Y?" are all
54323
+ full-content tasks. Get the \`recordId\` from a \`pipeshub_search\` top hit, a
54324
+ chat citation, or \`mode:"lookup"\`.
54325
+
54326
+ Returns one \`content\` string: a metadata header (title, source, key fields,
54327
+ pre-generated summary) then the full parsed text. A record with no
54328
+ extractable content returns the literal \`No record found\`. Use
54329
+ \`pipeshub_download_record\` only for the original file bytes.
54330
+
54331
+ **\`mode:"navigate"\`** — browse the hierarchy: RecordGroup (project /
54332
+ space / drive / folder) → Record (epic / story / page / file) → children,
54333
+ with breadcrumbs, related links and record IDs.
54334
+
54335
+ Use it when the question depends on structure rather than wording: what is
54336
+ under this epic, which pages sit in this space, what is linked to this
54337
+ ticket, what is in this folder — and every "how many" / "all of" / "every"
54338
+ question. Search ranks by content; only this shows how records relate, and
54339
+ only this gives a count you can trust.
54340
+
54341
+ Omit \`nodeId\` for a flat listing of everything reachable, most recently
54342
+ updated first — the usual starting point. A URL, an issue key, or a
54343
+ \`pipeshub_sources\` id also works and resolves automatically.
54344
+
54345
+ Pass \`depth:2\` or \`depth:3\` to see several levels in ONE call — an epic's
54346
+ stories AND their subtasks, a space's pages AND their children — instead of
54347
+ one call per level. Use it whenever the question needs an overview of a
54348
+ hierarchy rather than a single node.
54349
+
54350
+ Opening a record also prints that record's own metadata — for a ticket,
54351
+ status, assignee, priority and dates — so a question about one record is
54352
+ often answered by this call alone. It returns no document text; for that,
54353
+ re-call with \`mode:"content"\`.
54354
+
54355
+ Returns \`Path\` breadcrumbs, the current node's metadata, a children listing
54356
+ carrying \`record_id=\` or \`node_id=\` per row plus the group's total
54357
+ (\`Children 1-50 of 61\`), \`Related\` cross-references, and a \`Next:\` line.
54358
+ One page is usually every child, so only pass \`page:2\` when that \`Next:\`
54359
+ line says more exist.
54360
+
54361
+ **\`mode:"lookup"\`** — turn an external reference into a recordId, the first
54362
+ step whenever the question names one. Returns that record's metadata (for a
54363
+ ticket: status, assignee, priority, dates) plus its recordId, which
54364
+ \`mode:"navigate"\` takes to list what is under it and \`mode:"content"\` takes
54365
+ to read it.
54366
+
54367
+ Handles Jira keys and URLs, Confluence, Drive, Slack permalinks, Linear,
54368
+ Notion, ServiceNow sys_id, SharePoint, Gmail/Outlook, and any connector
54369
+ whose records index a web URL. Resolution searches ALL connectors you can
54370
+ access, regardless of any source filter you used elsewhere.
54371
+
54372
+ A miss is a 200 with empty \`matches\` and the input echoed in
54373
+ \`not_found_identifiers\` — that may mean no-access, not non-existence. Use
54374
+ \`mode:"navigate"\` to confirm the record exists before telling the user it
54375
+ does not. If \`ambiguous\` is true, pick from \`matches\` rather than taking
54376
+ the first.
54377
+
54378
+ Navigate and lookup return a rendered text view whose closing \`Next:\` line
54379
+ names the exact follow-up call — follow it. When presenting a record, link
54380
+ it using the \`Web URL\` from its metadata header (when present).`,
54004
54381
  scopes: ["read"],
54005
54382
  annotations: {
54006
- title: "Get a record's full parsed content",
54383
+ title: "Read, browse, or resolve records",
54007
54384
  destructiveHint: false,
54008
54385
  idempotentHint: true,
54009
54386
  openWorldHint: false,
@@ -54011,9 +54388,48 @@ you need the original file bytes.`,
54011
54388
  },
54012
54389
  args: args4,
54013
54390
  tool: async (client, args5, ctx) => {
54391
+ const fetchOptions = { fetchOptions: { signal: ctx.signal } };
54392
+ if (args5.mode === "navigate") {
54393
+ const [r] = await connectorNavigateKnowledgeGraph(client, {
54394
+ nodeId: args5.nodeId,
54395
+ page: args5.page,
54396
+ limit: args5.limit,
54397
+ depth: args5.depth,
54398
+ nodeTypes: args5.nodeTypes,
54399
+ createdAfter: args5.createdAfter,
54400
+ createdBefore: args5.createdBefore,
54401
+ modifiedAfter: args5.modifiedAfter,
54402
+ modifiedBefore: args5.modifiedBefore
54403
+ }, fetchOptions).$inspect();
54404
+ if (!r.ok)
54405
+ return errorResult(r.error.message);
54406
+ const httpErr2 = await httpErrorResult(r.value, "Navigate knowledge graph");
54407
+ if (httpErr2)
54408
+ return httpErr2;
54409
+ return renderedResult(r.value);
54410
+ }
54411
+ if (args5.mode === "lookup") {
54412
+ const identifiers = typeof args5.identifiers === "string" ? [args5.identifiers] : args5.identifiers ?? [];
54413
+ if (identifiers.length === 0) {
54414
+ return errorResult('`identifiers` is required when mode is "lookup".');
54415
+ }
54416
+ const [r] = await connectorLookupRecordByIdentifier(client, {
54417
+ identifiers,
54418
+ connectorName: args5.connectorName
54419
+ }, fetchOptions).$inspect();
54420
+ if (!r.ok)
54421
+ return errorResult(r.error.message);
54422
+ const httpErr2 = await httpErrorResult(r.value, "Record lookup");
54423
+ if (httpErr2)
54424
+ return httpErr2;
54425
+ return renderedResult(r.value);
54426
+ }
54427
+ if (!args5.recordId) {
54428
+ return errorResult('`recordId` is required when mode is "content".');
54429
+ }
54014
54430
  const [result] = await connectorGetRecordContent(client, {
54015
54431
  recordId: args5.recordId
54016
- }, { fetchOptions: { signal: ctx.signal } }).$inspect();
54432
+ }, fetchOptions).$inspect();
54017
54433
  if (!result.ok)
54018
54434
  return errorResult(result.error.message);
54019
54435
  const httpErr = await httpErrorResult(result.value, "Get record content");
@@ -54040,9 +54456,9 @@ var init_getallusersop = __esm(() => {
54040
54456
 
54041
54457
  // src/funcs/usersGetAllUsers.ts
54042
54458
  function usersGetAllUsers(client$, request, options) {
54043
- return new APIPromise($do9(client$, request, options));
54459
+ return new APIPromise($do11(client$, request, options));
54044
54460
  }
54045
- async function $do9(client$, request, options) {
54461
+ async function $do11(client$, request, options) {
54046
54462
  const parsed$ = safeParse4(request, (value$) => GetAllUsersRequest$zodSchema.optional().parse(value$), "Input validation failed");
54047
54463
  if (!parsed$.ok) {
54048
54464
  return [parsed$, { status: "invalid" }];
@@ -54127,9 +54543,9 @@ var init_getuserbyidop = __esm(() => {
54127
54543
 
54128
54544
  // src/funcs/usersGetUserById.ts
54129
54545
  function usersGetUserById(client$, request, options) {
54130
- return new APIPromise($do10(client$, request, options));
54546
+ return new APIPromise($do12(client$, request, options));
54131
54547
  }
54132
- async function $do10(client$, request, options) {
54548
+ async function $do12(client$, request, options) {
54133
54549
  const parsed$ = safeParse4(request, (value$) => GetUserByIdRequest$zodSchema.parse(value$), "Input validation failed");
54134
54550
  if (!parsed$.ok) {
54135
54551
  return [parsed$, { status: "invalid" }];
@@ -54216,9 +54632,9 @@ var init_getallusergroupsop = __esm(() => {
54216
54632
 
54217
54633
  // src/funcs/userGroupsGetAllUserGroups.ts
54218
54634
  function userGroupsGetAllUserGroups(client$, request, options) {
54219
- return new APIPromise($do11(client$, request, options));
54635
+ return new APIPromise($do13(client$, request, options));
54220
54636
  }
54221
- async function $do11(client$, request, options) {
54637
+ async function $do13(client$, request, options) {
54222
54638
  const parsed$ = safeParse4(request, (value$) => GetAllUserGroupsRequest$zodSchema.optional().parse(value$), "Input validation failed");
54223
54639
  if (!parsed$.ok) {
54224
54640
  return [parsed$, { status: "invalid" }];
@@ -54308,9 +54724,9 @@ var init_getuserteamsop = __esm(() => {
54308
54724
 
54309
54725
  // src/funcs/teamsGetUserTeams.ts
54310
54726
  function teamsGetUserTeams(client$, request, options) {
54311
- return new APIPromise($do12(client$, request, options));
54727
+ return new APIPromise($do14(client$, request, options));
54312
54728
  }
54313
- async function $do12(client$, request, options) {
54729
+ async function $do14(client$, request, options) {
54314
54730
  const parsed$ = safeParse4(request, (value$) => GetUserTeamsRequest$zodSchema.optional().parse(value$), "Input validation failed");
54315
54731
  if (!parsed$.ok) {
54316
54732
  return [parsed$, { status: "invalid" }];
@@ -54404,7 +54820,7 @@ var init_pipeshubDirectory = __esm(() => {
54404
54820
  "list_groups",
54405
54821
  "list_my_teams"
54406
54822
  ]).describe(`What to do:
54407
- ` + "- `whoami` — return the authenticated user's identity (decoded from " + `the bearer JWT). No other args needed.
54823
+ ` + "- `whoami` — return the authenticated user's identity, confirmed " + `against the server. No other args needed.
54408
54824
  ` + "- `list_users` — paginated list of org users. Optional `page`, " + "`limit`, `search` (substring match against name or email).\n" + "- `get_user` — full profile for one user. Required `userId`. " + "Use `whoami` to find your own id first if needed.\n" + "- `list_groups` — paginated list of user groups (with `userCount`).\n" + "- `list_my_teams` — teams the authenticated user belongs to, with " + "capability flags."),
54409
54825
  userId: string2().optional().describe("Required when `action` is `get_user`. 24-character ObjectId."),
54410
54826
  page: number2().int().min(1).optional().describe("Pagination — 1-based page number. Used by list_* actions."),
@@ -54418,7 +54834,8 @@ actions — pick the right \`action\`:
54418
54834
 
54419
54835
  - \`whoami\` — who is the caller? Use this whenever you need the
54420
54836
  authenticated user's own id, email, or full name (e.g. before
54421
- \`get_user\` on themselves).
54837
+ \`get_user\` on themselves). Errors if the credential is expired
54838
+ or revoked.
54422
54839
  - \`list_users\` — search / page through org users.
54423
54840
  - \`get_user\` — full \`User\` document for one user (requires \`userId\`).
54424
54841
  - \`list_groups\` — list user groups with \`userCount\`.
@@ -54443,13 +54860,39 @@ Output shape varies by action; see each action's docs above.`,
54443
54860
  if (!claims) {
54444
54861
  return errorResult("No bearer token configured on the SDK client, so `whoami` " + "cannot resolve the caller's identity. Ask the user for " + "their email and use `list_users` with `search`.");
54445
54862
  }
54863
+ const exp = claims["exp"];
54864
+ const expired = expiredTokenError(exp);
54865
+ if (expired)
54866
+ return expired;
54867
+ const tokenExpiresAt = typeof exp === "number" ? new Date(exp * 1000).toISOString() : undefined;
54868
+ const userId = claims["userId"];
54869
+ let verified = "unchecked";
54870
+ let unverifiedReason = "No userId claim in the token, so the identity could not be " + "confirmed with the server.";
54871
+ if (typeof userId === "string" && userId) {
54872
+ const [probe] = await usersGetUserById(client, { id: userId }, {
54873
+ fetchOptions
54874
+ }).$inspect();
54875
+ if (!probe.ok) {
54876
+ unverifiedReason = `Could not reach PipesHub to confirm the ` + `identity (${probe.error.message}). The details below come ` + `from the token itself.`;
54877
+ } else if (probe.value.status === 401) {
54878
+ return errorResult("PipesHub rejected this access token (HTTP 401 Unauthorized), " + "so the identity in it is no longer valid — it has most " + "likely been revoked. Mint a new personal access token " + "under Developer Settings → Personal Access Tokens.");
54879
+ } else if (probe.value.ok) {
54880
+ verified = true;
54881
+ unverifiedReason = undefined;
54882
+ } else {
54883
+ unverifiedReason = `PipesHub returned HTTP ${probe.value.status} ` + `when confirming the identity, so it could not be checked. ` + `The details below come from the token itself.`;
54884
+ }
54885
+ }
54446
54886
  return jsonResult({
54447
54887
  userId: claims["userId"],
54448
54888
  orgId: claims["orgId"],
54449
54889
  email: claims["email"],
54450
54890
  fullName: claims["fullName"],
54451
54891
  mobile: claims["mobile"],
54452
- userSlug: claims["userSlug"]
54892
+ userSlug: claims["userSlug"],
54893
+ tokenExpiresAt,
54894
+ identityVerified: verified,
54895
+ note: unverifiedReason
54453
54896
  });
54454
54897
  }
54455
54898
  case "list_users": {
@@ -54460,7 +54903,7 @@ Output shape varies by action; see each action's docs above.`,
54460
54903
  }, { fetchOptions }).$inspect();
54461
54904
  if (!result.ok)
54462
54905
  return errorResult(result.error.message);
54463
- const parsed = await readJson(result.value);
54906
+ const parsed = await readJson(result.value, "User listing");
54464
54907
  if (!parsed.ok)
54465
54908
  return parsed.result;
54466
54909
  return jsonResult(parsed.value);
@@ -54474,7 +54917,7 @@ Output shape varies by action; see each action's docs above.`,
54474
54917
  }, { fetchOptions }).$inspect();
54475
54918
  if (!result.ok)
54476
54919
  return errorResult(result.error.message);
54477
- const parsed = await readJson(result.value);
54920
+ const parsed = await readJson(result.value, "User lookup");
54478
54921
  if (!parsed.ok)
54479
54922
  return parsed.result;
54480
54923
  return jsonResult(parsed.value);
@@ -54487,7 +54930,7 @@ Output shape varies by action; see each action's docs above.`,
54487
54930
  }, { fetchOptions }).$inspect();
54488
54931
  if (!result.ok)
54489
54932
  return errorResult(result.error.message);
54490
- const parsed = await readJson(result.value);
54933
+ const parsed = await readJson(result.value, "Group listing");
54491
54934
  if (!parsed.ok)
54492
54935
  return parsed.result;
54493
54936
  return jsonResult(parsed.value);
@@ -54500,7 +54943,7 @@ Output shape varies by action; see each action's docs above.`,
54500
54943
  }, { fetchOptions }).$inspect();
54501
54944
  if (!result.ok)
54502
54945
  return errorResult(result.error.message);
54503
- const parsed = await readJson(result.value);
54946
+ const parsed = await readJson(result.value, "Team listing");
54504
54947
  if (!parsed.ok)
54505
54948
  return parsed.result;
54506
54949
  return jsonResult(parsed.value);
@@ -54540,9 +54983,9 @@ var init_getknowledgehubrootnodesop = __esm(() => {
54540
54983
 
54541
54984
  // src/funcs/knowledgeHubGetKnowledgeHubRootNodes.ts
54542
54985
  function knowledgeHubGetKnowledgeHubRootNodes(client$, request, options) {
54543
- return new APIPromise($do13(client$, request, options));
54986
+ return new APIPromise($do15(client$, request, options));
54544
54987
  }
54545
- async function $do13(client$, request, options) {
54988
+ async function $do15(client$, request, options) {
54546
54989
  const parsed$ = safeParse4(request, (value$) => GetKnowledgeHubRootNodesRequest$zodSchema.optional().parse(value$), "Input validation failed");
54547
54990
  if (!parsed$.ok) {
54548
54991
  return [parsed$, { status: "invalid" }];
@@ -54658,9 +55101,9 @@ var init_getavailablemodelsbytypeop = __esm(() => {
54658
55101
 
54659
55102
  // src/funcs/aiModelsProvidersGetAvailableModelsByType.ts
54660
55103
  function aiModelsProvidersGetAvailableModelsByType(client$, request, options) {
54661
- return new APIPromise($do14(client$, request, options));
55104
+ return new APIPromise($do16(client$, request, options));
54662
55105
  }
54663
- async function $do14(client$, request, options) {
55106
+ async function $do16(client$, request, options) {
54664
55107
  const parsed$ = safeParse4(request, (value$) => GetAvailableModelsByTypeRequest$zodSchema.parse(value$), "Input validation failed");
54665
55108
  if (!parsed$.ok) {
54666
55109
  return [parsed$, { status: "invalid" }];
@@ -54785,7 +55228,7 @@ are returned by default; pass \`include\` to override.`,
54785
55228
  }, { fetchOptions }).$inspect();
54786
55229
  if (!r.ok)
54787
55230
  return errorResult(`sources: ${r.error.message}`);
54788
- const parsed = await readJson(r.value);
55231
+ const parsed = await readJson(r.value, "Knowledge base listing");
54789
55232
  if (!parsed.ok)
54790
55233
  return parsed.result;
54791
55234
  result["sources"] = (parsed.value.items ?? []).map((n) => ({
@@ -54807,7 +55250,7 @@ are returned by default; pass \`include\` to override.`,
54807
55250
  }, { fetchOptions }).$inspect();
54808
55251
  if (!r.ok)
54809
55252
  return errorResult(`${key}: ${r.error.message}`);
54810
- const parsed = await readJson(r.value);
55253
+ const parsed = await readJson(r.value, "Model listing");
54811
55254
  if (!parsed.ok)
54812
55255
  return parsed.result;
54813
55256
  result[key] = (parsed.value.models ?? []).map((m) => ({
@@ -54986,31 +55429,39 @@ var init_tool_names = __esm(() => {
54986
55429
  },
54987
55430
  {
54988
55431
  name: "pipeshub_chat",
54989
- description: `**Primary chat tool handles both internal knowledge queries and web search.**
54990
-
54991
- **Internal search** (default, \`chatMode: "internal_search"\`): Use whenever
54992
- the user asks about their documents, files, knowledge base, company policies,
54993
- or anything that could plausibly be answered by content in their PipesHub-indexed
54994
- sources (Drive, Box, Confluence, Slack, Gmail, Jira, the org's KB, ...).
54995
- Grounds the answer in the user's actual data and returns citations.
54996
- Answers come from a few retrieved passages, not whole documents — for
54997
- any task needing a document's full content, use
54998
- \`pipeshub_get_record_content\` instead.
54999
-
55000
- **Web search** (\`chatMode: "web_search"\`): Use when the user asks about
55001
- current events, public information, or anything unlikely to be in the org's
55002
- internal knowledge base. Pass \`chatMode: "web_search"\` and this tool will
55003
- search the public web instead.
55004
-
55005
- **When to pick this over other tools:**
55006
- - "Summarize <doc>" / "key points of <doc>" / "what does <document> say
55007
- about X?" → NOT this tool. Use \`pipeshub_search\` →
55008
- \`pipeshub_get_record_content\`: answering for a specific document
55009
- requires its full content, and chat only sees a few retrieved
55010
- passages, never the whole document.
55432
+ description: `Ask a question, get an answer grounded in the org's indexed data with
55433
+ citations. It reads a few retrieved passages — never a whole document,
55434
+ never a complete list.
55435
+
55436
+ **Three questions this tool gets WRONG. Check them first:**
55437
+ - **Structure** "what's under this epic?", "which pages are in this
55438
+ space?", "what links to this ticket?", "what's in this folder?"
55439
+ \`pipeshub_get_record_content\` \`mode:"navigate"\`. Ranking cannot see how
55440
+ records relate.
55441
+ - **Exhaustive** — "how many X?", "list ALL the Y", "every Z" →
55442
+ \`mode:"navigate"\`, which reports the group's real total. This tool
55443
+ undercounts and will not say so.
55444
+ - **One named document** — summarize it, extract from it, what does it say
55445
+ about X \`pipeshub_search\` for the \`recordId\`, then \`mode:"content"\`.
55446
+
55447
+ Everything else about the org's knowledge belongs here: policies,
55448
+ processes, decisions, history, "what do we know about X", and any question
55449
+ spanning several documents.
55450
+
55451
+ **Internal search** (default, \`chatMode: "internal_search"\`): the user's
55452
+ documents, files, knowledge base, company policies anything in their
55453
+ PipesHub-indexed sources (Drive, Box, Confluence, Slack, Gmail, Jira, the
55454
+ org's KB, ...).
55455
+
55456
+ **Web search** (\`chatMode: "web_search"\`): current events or public
55457
+ information unlikely to be in the org's knowledge base.
55458
+
55459
+ Both are plain-chat modes. **Agent chat** — pass an \`agentId\` from
55460
+ \`pipeshub_agents\` — runs against that agent's own prompt, tools and knowledge;
55461
+ \`quick\` is its only mode, requires the \`agentId\`, and is sent automatically.
55462
+
55011
55463
  - "What's our policy on Y?" → \`pipeshub_chat\` (internal_search)
55012
55464
  - "What's in the news about Z?" → \`pipeshub_chat\` (web_search)
55013
- - "What is the latest version of <library>?" → \`pipeshub_chat\` (web_search)
55014
55465
  - "Find / locate the file named X" → \`pipeshub_search\` (then
55015
55466
  \`pipeshub_download_record\` if the user wants the bytes).
55016
55467
 
@@ -55049,10 +55500,23 @@ Typical uses:
55049
55500
  - Show the user a ranked list of matching files when they ask "find /
55050
55501
  search for X".
55051
55502
 
55503
+ Not for structural questions — what is under this epic, which pages are
55504
+ in this space, what links to this ticket. Ranking by content cannot show
55505
+ how records relate; use \`pipeshub_get_record_content\` \`mode:"navigate"\`.
55506
+
55507
+ **A ranked sample, never a complete list.** Hits are the top-scoring
55508
+ blocks from the best-matching records — not all blocks of any record, and
55509
+ not every record that matches. Never count them to answer "how many" /
55510
+ "all" / "every"; navigate the record group instead, which reports its
55511
+ real total.
55512
+
55052
55513
  The response is trimmed to one row per hit:
55053
55514
  \`{ recordId, recordName, score, snippet, mimeType, webUrl, ... }\`.
55054
55515
  Highest \`score\` first; multiple hits may share the same \`recordId\`
55055
- (different chunks of the same record).`
55516
+ (different blocks of the same record).
55517
+
55518
+ When presenting results to the user, link each record using its
55519
+ \`webUrl\` (when present).`
55056
55520
  },
55057
55521
  {
55058
55522
  name: "pipeshub_download_record",
@@ -55060,34 +55524,84 @@ Highest \`score\` first; multiple hits may share the same \`recordId\`
55060
55524
  },
55061
55525
  {
55062
55526
  name: "pipeshub_get_record_content",
55063
- description: `Read a record's full parsed content by \`recordId\` the only way to
55064
- see a document's COMPLETE text.
55065
-
55066
- Use it whenever the answer depends on a document's full content — any
55067
- task where missing a part could make the answer wrong: summarize / key
55068
- points / action items; extract or list ALL of something; check whether
55069
- or where the doc mentions X; translate, rewrite, outline, or review the
55070
- doc; compare named docs (fetch each); any question scoped to one named
55071
- document. \`pipeshub_chat\` cannot do these it only sees a few
55072
- retrieved passages, never the whole document. Get the \`recordId\` from
55073
- a \`pipeshub_search\` top hit or a chat citation.
55074
-
55075
- Judge by the user's INTENT, not their keywords: they need not say
55076
- "summarize", "key points", or "extract". Reason about what a good
55077
- answer requires if it would need the whole document (e.g. "what's
55078
- this doc about?", "walk me through the report", "anything in here
55079
- about Y?"), that is a full-content task, so call this tool.
55080
-
55081
- Returns a single \`content\` string: a short metadata header (title,
55082
- source, key fields, and a pre-generated summary) followed by the
55083
- record's full parsed text paragraphs, tables, and lists in reading
55084
- order. For a record with no extractable content, \`content\` is the
55085
- literal \`No record found\`. Use \`pipeshub_download_record\` only when
55086
- you need the original file bytes.`
55527
+ description: `Three operations on the org's records. Pick by what you hold:
55528
+
55529
+ \`mode:"lookup"\` — a URL, issue key (PA-1787), or external ID
55530
+ its recordId plus the record's metadata
55531
+ \`mode:"navigate"\` a question about structure: what is under X,
55532
+ what links to Y browses the hierarchy
55533
+ \`mode:"content"\` — a recordId, and you need the document's COMPLETE text
55534
+
55535
+ **\`mode:"content"\` (default)**the only way to see a document's complete
55536
+ text. Use it whenever missing part of the document could make the answer
55537
+ wrong: summarize, extract or list ALL of something, check whether or where
55538
+ a doc mentions X, review, or compare named docs. \`pipeshub_chat\` cannot do
55539
+ these it never sees a whole document.
55540
+
55541
+ Judge by the user's INTENT, not their keywords: "what's this doc about?",
55542
+ "walk me through the report", "anything in here about Y?" are all
55543
+ full-content tasks. Get the \`recordId\` from a \`pipeshub_search\` top hit, a
55544
+ chat citation, or \`mode:"lookup"\`.
55545
+
55546
+ Returns one \`content\` string: a metadata header (title, source, key fields,
55547
+ pre-generated summary) then the full parsed text. A record with no
55548
+ extractable content returns the literal \`No record found\`. Use
55549
+ \`pipeshub_download_record\` only for the original file bytes.
55550
+
55551
+ **\`mode:"navigate"\`** — browse the hierarchy: RecordGroup (project /
55552
+ space / drive / folder) → Record (epic / story / page / file) → children,
55553
+ with breadcrumbs, related links and record IDs.
55554
+
55555
+ Use it when the question depends on structure rather than wording: what is
55556
+ under this epic, which pages sit in this space, what is linked to this
55557
+ ticket, what is in this folder — and every "how many" / "all of" / "every"
55558
+ question. Search ranks by content; only this shows how records relate, and
55559
+ only this gives a count you can trust.
55560
+
55561
+ Omit \`nodeId\` for a flat listing of everything reachable, most recently
55562
+ updated first — the usual starting point. A URL, an issue key, or a
55563
+ \`pipeshub_sources\` id also works and resolves automatically.
55564
+
55565
+ Pass \`depth:2\` or \`depth:3\` to see several levels in ONE call — an epic's
55566
+ stories AND their subtasks, a space's pages AND their children — instead of
55567
+ one call per level. Use it whenever the question needs an overview of a
55568
+ hierarchy rather than a single node.
55569
+
55570
+ Opening a record also prints that record's own metadata — for a ticket,
55571
+ status, assignee, priority and dates — so a question about one record is
55572
+ often answered by this call alone. It returns no document text; for that,
55573
+ re-call with \`mode:"content"\`.
55574
+
55575
+ Returns \`Path\` breadcrumbs, the current node's metadata, a children listing
55576
+ carrying \`record_id=\` or \`node_id=\` per row plus the group's total
55577
+ (\`Children 1-50 of 61\`), \`Related\` cross-references, and a \`Next:\` line.
55578
+ One page is usually every child, so only pass \`page:2\` when that \`Next:\`
55579
+ line says more exist.
55580
+
55581
+ **\`mode:"lookup"\`** — turn an external reference into a recordId, the first
55582
+ step whenever the question names one. Returns that record's metadata (for a
55583
+ ticket: status, assignee, priority, dates) plus its recordId, which
55584
+ \`mode:"navigate"\` takes to list what is under it and \`mode:"content"\` takes
55585
+ to read it.
55586
+
55587
+ Handles Jira keys and URLs, Confluence, Drive, Slack permalinks, Linear,
55588
+ Notion, ServiceNow sys_id, SharePoint, Gmail/Outlook, and any connector
55589
+ whose records index a web URL. Resolution searches ALL connectors you can
55590
+ access, regardless of any source filter you used elsewhere.
55591
+
55592
+ A miss is a 200 with empty \`matches\` and the input echoed in
55593
+ \`not_found_identifiers\` — that may mean no-access, not non-existence. Use
55594
+ \`mode:"navigate"\` to confirm the record exists before telling the user it
55595
+ does not. If \`ambiguous\` is true, pick from \`matches\` rather than taking
55596
+ the first.
55597
+
55598
+ Navigate and lookup return a rendered text view whose closing \`Next:\` line
55599
+ names the exact follow-up call — follow it. When presenting a record, link
55600
+ it using the \`Web URL\` from its metadata header (when present).`
55087
55601
  },
55088
55602
  {
55089
55603
  name: "pipeshub_directory",
55090
- description: "Look up people, groups, and teams in PipesHub. One tool with five\nactions — pick the right `action`:\n\n- `whoami` — who is the caller? Use this whenever you need the\n authenticated user's own id, email, or full name (e.g. before\n `get_user` on themselves).\n- `list_users` — search / page through org users.\n- `get_user` — full `User` document for one user (requires `userId`).\n- `list_groups` — list user groups with `userCount`.\n- `list_my_teams` — teams the caller belongs to, with capability flags\n (`canEdit` / `canDelete` / `canManageMembers`).\n\nOutput shape varies by action; see each action's docs above."
55604
+ description: "Look up people, groups, and teams in PipesHub. One tool with five\nactions — pick the right `action`:\n\n- `whoami` — who is the caller? Use this whenever you need the\n authenticated user's own id, email, or full name (e.g. before\n `get_user` on themselves). Errors if the credential is expired\n or revoked.\n- `list_users` — search / page through org users.\n- `get_user` — full `User` document for one user (requires `userId`).\n- `list_groups` — list user groups with `userCount`.\n- `list_my_teams` — teams the caller belongs to, with capability flags\n (`canEdit` / `canDelete` / `canManageMembers`).\n\nOutput shape varies by action; see each action's docs above."
55091
55605
  },
55092
55606
  {
55093
55607
  name: "pipeshub_agents",
@@ -58720,5 +59234,5 @@ export {
58720
59234
  app
58721
59235
  };
58722
59236
 
58723
- //# debugId=59975E6F38134B6C64756E2164756E21
59237
+ //# debugId=B3EADAB5212D24DD64756E2164756E21
58724
59238
  //# sourceMappingURL=mcp-server.js.map