@pipeshub-ai/mcp 2.1.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +90 -2
- package/bin/mcp-server.js +408 -74
- package/bin/mcp-server.js.map +19 -12
- package/esm/funcs/connectorGetRecordContent.d.ts +43 -0
- package/esm/funcs/connectorGetRecordContent.d.ts.map +1 -0
- package/esm/funcs/connectorGetRecordContent.js +114 -0
- package/esm/funcs/connectorGetRecordContent.js.map +1 -0
- package/esm/funcs/conversationsStreamConversation.js +1 -1
- package/esm/funcs/conversationsStreamConversation.js.map +1 -1
- package/esm/funcs/userGroupsGetAllUserGroups.js +1 -1
- package/esm/funcs/userGroupsGetAllUserGroups.js.map +1 -1
- package/esm/hooks/registration.d.ts.map +1 -1
- package/esm/hooks/registration.js +2 -1
- package/esm/hooks/registration.js.map +1 -1
- package/esm/hooks/request-context.d.ts +10 -0
- package/esm/hooks/request-context.d.ts.map +1 -0
- package/esm/hooks/request-context.js +38 -0
- package/esm/hooks/request-context.js.map +1 -0
- package/esm/hooks/requestid.d.ts +5 -0
- package/esm/hooks/requestid.d.ts.map +1 -0
- package/esm/hooks/requestid.js +58 -0
- package/esm/hooks/requestid.js.map +1 -0
- package/esm/mcp-server/instructions.d.ts +1 -1
- package/esm/mcp-server/instructions.d.ts.map +1 -1
- package/esm/mcp-server/instructions.js +32 -9
- package/esm/mcp-server/instructions.js.map +1 -1
- package/esm/mcp-server/server.d.ts.map +1 -1
- package/esm/mcp-server/server.js +10 -3
- package/esm/mcp-server/server.js.map +1 -1
- package/esm/mcp-server/tools/pipeshubChat.d.ts.map +1 -1
- package/esm/mcp-server/tools/pipeshubChat.js +8 -2
- package/esm/mcp-server/tools/pipeshubChat.js.map +1 -1
- package/esm/mcp-server/tools/pipeshubGetRecordContent.d.ts +8 -0
- package/esm/mcp-server/tools/pipeshubGetRecordContent.d.ts.map +1 -0
- package/esm/mcp-server/tools/pipeshubGetRecordContent.js +63 -0
- package/esm/mcp-server/tools/pipeshubGetRecordContent.js.map +1 -0
- package/esm/mcp-server/tools/pipeshubSearch.d.ts.map +1 -1
- package/esm/mcp-server/tools/pipeshubSearch.js +8 -4
- package/esm/mcp-server/tools/pipeshubSearch.js.map +1 -1
- package/esm/mcp-server/tools.d.ts.map +1 -1
- package/esm/mcp-server/tools.js +13 -1
- package/esm/mcp-server/tools.js.map +1 -1
- package/esm/models/getrecordcontentop.d.ts +6 -0
- package/esm/models/getrecordcontentop.d.ts.map +1 -0
- package/esm/models/getrecordcontentop.js +5 -0
- package/esm/models/getrecordcontentop.js.map +1 -0
- package/esm/tool-names.d.ts.map +1 -1
- package/esm/tool-names.js +6 -2
- package/esm/tool-names.js.map +1 -1
- package/package.json +2 -1
- package/src/funcs/connectorGetRecordContent.ts +179 -0
- package/src/funcs/conversationsStreamConversation.ts +1 -1
- package/src/funcs/userGroupsGetAllUserGroups.ts +1 -1
- package/src/hooks/registration.ts +2 -1
- package/src/hooks/request-context.ts +47 -0
- package/src/hooks/requestid.ts +62 -0
- package/src/mcp-server/instructions.ts +32 -9
- package/src/mcp-server/server.ts +11 -3
- package/src/mcp-server/tools/pipeshubChat.ts +8 -2
- package/src/mcp-server/tools/pipeshubGetRecordContent.ts +67 -0
- package/src/mcp-server/tools/pipeshubSearch.ts +8 -4
- package/src/mcp-server/tools.ts +14 -1
- package/src/models/getrecordcontentop.ts +11 -0
- package/src/tool-names.ts +6 -2
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Stamp an x-request-id trace header onto every outbound SDK request, reusing
|
|
3
|
+
* the id bound for the current MCP tool/resource/prompt call (see
|
|
4
|
+
* request-context.ts) so all backend calls from one invocation share it.
|
|
5
|
+
*
|
|
6
|
+
* Format mirrors the TypeScript SDK's own RequestIDHook: `mcp-<userId>-<random>`
|
|
7
|
+
* when the bearer JWT carries a userId claim, else `mcp-<random>`. The userId
|
|
8
|
+
* is only knowable once a request actually carries a resolved Authorization
|
|
9
|
+
* header, so it's decoded off the first outbound request of the tool call and
|
|
10
|
+
* cached on the bound context for every subsequent call to reuse verbatim.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { getRequestContextStore, newRequestId } from "./request-context.js";
|
|
14
|
+
import { BeforeRequestContext, BeforeRequestHook } from "./types.js";
|
|
15
|
+
|
|
16
|
+
const REQUEST_ID_HEADER = "x-request-id";
|
|
17
|
+
|
|
18
|
+
/** Decodes the JWT payload without verifying the signature. */
|
|
19
|
+
function decodeJwtPayload(token: string): Record<string, unknown> | null {
|
|
20
|
+
try {
|
|
21
|
+
const base64Url = token.split(".")[1];
|
|
22
|
+
if (!base64Url) return null;
|
|
23
|
+
const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/");
|
|
24
|
+
const padded = base64 + "=".repeat((4 - (base64.length % 4)) % 4);
|
|
25
|
+
const claims = JSON.parse(Buffer.from(padded, "base64").toString("utf8"));
|
|
26
|
+
return typeof claims === "object" && claims !== null ? claims : null;
|
|
27
|
+
} catch {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function userIdFromRequest(request: Request): string | null {
|
|
33
|
+
const auth = request.headers.get("authorization") ?? "";
|
|
34
|
+
if (!auth.toLowerCase().startsWith("bearer ")) return null;
|
|
35
|
+
const token = auth.slice(7).trim();
|
|
36
|
+
const claims = decodeJwtPayload(token);
|
|
37
|
+
return (claims?.["userId"] as string) || null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export class RequestIDHook implements BeforeRequestHook {
|
|
41
|
+
beforeRequest(_hookCtx: BeforeRequestContext, request: Request): Request {
|
|
42
|
+
if (request.headers.get(REQUEST_ID_HEADER)) {
|
|
43
|
+
return request;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const store = getRequestContextStore();
|
|
47
|
+
if (!store) {
|
|
48
|
+
// No bound tool-call context (shouldn't happen in practice).
|
|
49
|
+
request.headers.set(REQUEST_ID_HEADER, newRequestId());
|
|
50
|
+
return request;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (!store.finalId) {
|
|
54
|
+
const userId = userIdFromRequest(request);
|
|
55
|
+
store.finalId = userId
|
|
56
|
+
? `mcp-${userId}-${store.random}`
|
|
57
|
+
: `mcp-${store.random}`;
|
|
58
|
+
}
|
|
59
|
+
request.headers.set(REQUEST_ID_HEADER, store.finalId);
|
|
60
|
+
return request;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -9,14 +9,35 @@ PipesHub is the user's workplace AI platform. It indexes their documents,
|
|
|
9
9
|
knowledge base content, and connector sources (Drive, Box, Confluence,
|
|
10
10
|
Slack, Jira, Gmail, ...). When in doubt, the answer is in PipesHub.
|
|
11
11
|
|
|
12
|
+
## Full-document tasks: \`pipeshub_search\` → \`pipeshub_get_record_content\`
|
|
13
|
+
|
|
14
|
+
\`pipeshub_chat\` answers from a handful of retrieved passages — it never
|
|
15
|
+
reads a whole document. Whenever the task depends on a document's
|
|
16
|
+
COMPLETE content, fetch the document itself:
|
|
17
|
+
|
|
18
|
+
1. \`pipeshub_search\` with the document's name / topic.
|
|
19
|
+
2. Take the top hit's \`recordId\`.
|
|
20
|
+
3. \`pipeshub_get_record_content\`, and answer from the returned content.
|
|
21
|
+
|
|
22
|
+
Tasks that need this path — anything where missing part of the document
|
|
23
|
+
could make the answer wrong:
|
|
24
|
+
|
|
25
|
+
- Summarize / TL;DR / key points / takeaways / action items of a doc.
|
|
26
|
+
- Extract or list ALL of something (dates, owners, requirements, ...).
|
|
27
|
+
- Check whether / where a doc mentions something.
|
|
28
|
+
- Translate, rewrite, outline, review, or reformat a doc.
|
|
29
|
+
- Compare named docs (fetch each \`recordId\`).
|
|
30
|
+
- Any question explicitly scoped to ONE named document — chat retrieval
|
|
31
|
+
cannot be restricted to a single record.
|
|
32
|
+
|
|
12
33
|
## Default tool: \`pipeshub_chat\`
|
|
13
34
|
|
|
14
35
|
**Use \`pipeshub_chat\` for any question that could plausibly be answered
|
|
15
|
-
by the user's PipesHub-indexed data
|
|
36
|
+
by the user's PipesHub-indexed data** and is not a full-document task:
|
|
16
37
|
|
|
17
|
-
-
|
|
18
|
-
|
|
19
|
-
|
|
38
|
+
- A question that may span several documents, or where you don't yet
|
|
39
|
+
know which record holds the answer (e.g. "what did Aashil say about
|
|
40
|
+
onboarding?").
|
|
20
41
|
- Anything about company / org policies, processes, decisions, or
|
|
21
42
|
history (e.g. "what's our vacation policy?", "who owns the auth
|
|
22
43
|
service?").
|
|
@@ -32,15 +53,17 @@ the user can verify.
|
|
|
32
53
|
|
|
33
54
|
## When to use the other tools
|
|
34
55
|
|
|
35
|
-
- \`pipeshub_search\` —
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
the doc say?" use \`pipeshub_chat\` instead — it does the retrieval
|
|
39
|
-
internally.
|
|
56
|
+
- \`pipeshub_search\` — locate a document by name or topic and resolve it
|
|
57
|
+
to a \`recordId\`. To read or summarize one specific document, search,
|
|
58
|
+
then pass the top hit's \`recordId\` to \`pipeshub_get_record_content\`.
|
|
40
59
|
- \`pipeshub_download_record\` — when the user wants the actual file
|
|
41
60
|
bytes (download, attach, open). Get the \`recordId\` either from
|
|
42
61
|
citations on a prior \`pipeshub_chat\` response or from
|
|
43
62
|
\`pipeshub_search\`.
|
|
63
|
+
- \`pipeshub_get_record_content\` — when you need a record's full parsed
|
|
64
|
+
content (returned as a single \`content\` string: metadata header plus
|
|
65
|
+
the document's text) without downloading the original file. Prefer this
|
|
66
|
+
over download when the question is about what the record says.
|
|
44
67
|
- \`pipeshub_directory\` — people, groups, teams, and \`whoami\` lookups.
|
|
45
68
|
Not for documents.
|
|
46
69
|
- \`pipeshub_sources\` — call once at the start of a session to discover
|
package/src/mcp-server/server.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
2
2
|
|
|
3
3
|
import { PIPESHUB_INSTRUCTIONS } from "./instructions.js";
|
|
4
4
|
import { PipeshubCore } from "../core.js";
|
|
5
|
+
import { bindNewRequestId } from "../hooks/request-context.js";
|
|
5
6
|
import { SDKOptions } from "../lib/config.js";
|
|
6
7
|
import type { ConsoleLogger } from "./console-logger.js";
|
|
7
8
|
import { createRegisterPrompt } from "./prompts.js";
|
|
@@ -14,6 +15,7 @@ import { createRegisterTool, registerDynamicTools } from "./tools.js";
|
|
|
14
15
|
import { tool$pipeshubChat } from "./tools/pipeshubChat.js";
|
|
15
16
|
import { tool$pipeshubSearch } from "./tools/pipeshubSearch.js";
|
|
16
17
|
import { tool$pipeshubDownloadRecord } from "./tools/pipeshubDownloadRecord.js";
|
|
18
|
+
import { tool$pipeshubGetRecordContent } from "./tools/pipeshubGetRecordContent.js";
|
|
17
19
|
import { tool$pipeshubDirectory } from "./tools/pipeshubDirectory.js";
|
|
18
20
|
import { tool$pipeshubSources } from "./tools/pipeshubSources.js";
|
|
19
21
|
import { tool$pipeshubAgents } from "./tools/pipeshubAgents.js";
|
|
@@ -40,7 +42,7 @@ export function createMCPServer(deps: {
|
|
|
40
42
|
},
|
|
41
43
|
);
|
|
42
44
|
|
|
43
|
-
const
|
|
45
|
+
const resolveClient = deps.getSDK || (() =>
|
|
44
46
|
new PipeshubCore({
|
|
45
47
|
security: deps.security,
|
|
46
48
|
serverURL: deps.serverURL,
|
|
@@ -55,6 +57,11 @@ export function createMCPServer(deps: {
|
|
|
55
57
|
: undefined,
|
|
56
58
|
}));
|
|
57
59
|
|
|
60
|
+
const getClient = () => {
|
|
61
|
+
bindNewRequestId();
|
|
62
|
+
return resolveClient();
|
|
63
|
+
};
|
|
64
|
+
|
|
58
65
|
const scopes = new Set(deps.scopes);
|
|
59
66
|
|
|
60
67
|
const allowedTools = deps.allowedTools && new Set(deps.allowedTools);
|
|
@@ -87,8 +94,9 @@ export function createMCPServer(deps: {
|
|
|
87
94
|
tool(tool$pipeshubChat); // 2. ask questions (start + continue)
|
|
88
95
|
tool(tool$pipeshubSearch); // 3. resolve filename → recordId
|
|
89
96
|
tool(tool$pipeshubDownloadRecord); // 4. fetch a document by id
|
|
90
|
-
tool(tool$
|
|
91
|
-
tool(tool$
|
|
97
|
+
tool(tool$pipeshubGetRecordContent); // 5. fetch a record's parsed content
|
|
98
|
+
tool(tool$pipeshubDirectory); // 6. people / groups / teams / whoami
|
|
99
|
+
tool(tool$pipeshubAgents); // 7. discover org agents
|
|
92
100
|
|
|
93
101
|
// Curated prompt: user-invokable tool-routing guidance.
|
|
94
102
|
prompt(prompt$pipeshubAssistant);
|
|
@@ -100,6 +100,9 @@ the user asks about their documents, files, knowledge base, company policies,
|
|
|
100
100
|
or anything that could plausibly be answered by content in their PipesHub-indexed
|
|
101
101
|
sources (Drive, Box, Confluence, Slack, Gmail, Jira, the org's KB, ...).
|
|
102
102
|
Grounds the answer in the user's actual data and returns citations.
|
|
103
|
+
Answers come from a few retrieved passages, not whole documents — for
|
|
104
|
+
any task needing a document's full content, use
|
|
105
|
+
\`pipeshub_get_record_content\` instead.
|
|
103
106
|
|
|
104
107
|
**Web search** (\`chatMode: "web_search"\`): Use when the user asks about
|
|
105
108
|
current events, public information, or anything unlikely to be in the org's
|
|
@@ -107,8 +110,11 @@ internal knowledge base. Pass \`chatMode: "web_search"\` and this tool will
|
|
|
107
110
|
search the public web instead.
|
|
108
111
|
|
|
109
112
|
**When to pick this over other tools:**
|
|
110
|
-
- "
|
|
111
|
-
|
|
113
|
+
- "Summarize <doc>" / "key points of <doc>" / "what does <document> say
|
|
114
|
+
about X?" → NOT this tool. Use \`pipeshub_search\` →
|
|
115
|
+
\`pipeshub_get_record_content\`: answering for a specific document
|
|
116
|
+
requires its full content, and chat only sees a few retrieved
|
|
117
|
+
passages, never the whole document.
|
|
112
118
|
- "What's our policy on Y?" → \`pipeshub_chat\` (internal_search)
|
|
113
119
|
- "What's in the news about Z?" → \`pipeshub_chat\` (web_search)
|
|
114
120
|
- "What is the latest version of <library>?" → \`pipeshub_chat\` (web_search)
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
import { connectorGetRecordContent } from "../../funcs/connectorGetRecordContent.js";
|
|
3
|
+
import { ToolDefinition } from "../tools.js";
|
|
4
|
+
import { errorResult, httpErrorResult, readJson } from "./_helpers.js";
|
|
5
|
+
|
|
6
|
+
const args = {
|
|
7
|
+
recordId: z.string().min(1).describe(
|
|
8
|
+
"Record identifier — usually a UUID for connector-sourced records or "
|
|
9
|
+
+ "a 24-character ObjectId for uploaded records. Get it from a chat "
|
|
10
|
+
+ "citation (`citations[*].recordId`) or from a `pipeshub_search` hit.",
|
|
11
|
+
),
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const tool$pipeshubGetRecordContent: ToolDefinition<typeof args> = {
|
|
15
|
+
name: "pipeshub_get_record_content",
|
|
16
|
+
description:
|
|
17
|
+
`Read a record's full parsed content by \`recordId\` — the only way to
|
|
18
|
+
see a document's COMPLETE text.
|
|
19
|
+
|
|
20
|
+
Use it whenever the answer depends on a document's full content — any
|
|
21
|
+
task where missing a part could make the answer wrong: summarize / key
|
|
22
|
+
points / action items; extract or list ALL of something; check whether
|
|
23
|
+
or where the doc mentions X; translate, rewrite, outline, or review the
|
|
24
|
+
doc; compare named docs (fetch each); any question scoped to one named
|
|
25
|
+
document. \`pipeshub_chat\` cannot do these — it only sees a few
|
|
26
|
+
retrieved passages, never the whole document. Get the \`recordId\` from
|
|
27
|
+
a \`pipeshub_search\` top hit or a chat citation.
|
|
28
|
+
|
|
29
|
+
Judge by the user's INTENT, not their keywords: they need not say
|
|
30
|
+
"summarize", "key points", or "extract". Reason about what a good
|
|
31
|
+
answer requires — if it would need the whole document (e.g. "what's
|
|
32
|
+
this doc about?", "walk me through the report", "anything in here
|
|
33
|
+
about Y?"), that is a full-content task, so call this tool.
|
|
34
|
+
|
|
35
|
+
Returns a single \`content\` string: a short metadata header (title,
|
|
36
|
+
source, key fields, and a pre-generated summary) followed by the
|
|
37
|
+
record's full parsed text — paragraphs, tables, and lists in reading
|
|
38
|
+
order. For a record with no extractable content, \`content\` is the
|
|
39
|
+
literal \`No record found\`. Use \`pipeshub_download_record\` only when
|
|
40
|
+
you need the original file bytes.`,
|
|
41
|
+
scopes: ["read"],
|
|
42
|
+
annotations: {
|
|
43
|
+
title: "Get a record's full parsed content",
|
|
44
|
+
destructiveHint: false,
|
|
45
|
+
idempotentHint: true,
|
|
46
|
+
openWorldHint: false,
|
|
47
|
+
readOnlyHint: true,
|
|
48
|
+
},
|
|
49
|
+
args,
|
|
50
|
+
tool: async (client, args, ctx) => {
|
|
51
|
+
const [result] = await connectorGetRecordContent(client, {
|
|
52
|
+
recordId: args.recordId,
|
|
53
|
+
}, { fetchOptions: { signal: ctx.signal } }).$inspect();
|
|
54
|
+
if (!result.ok) return errorResult(result.error.message);
|
|
55
|
+
|
|
56
|
+
// The SDK func uses errorCodes:[], so any non-2xx comes back as an
|
|
57
|
+
// ok=false Response — surface it as an error rather than parsing it.
|
|
58
|
+
const httpErr = await httpErrorResult(result.value, "Get record content");
|
|
59
|
+
if (httpErr) return httpErr;
|
|
60
|
+
|
|
61
|
+
// Success: the endpoint returns { content: <string> }. Hand the LLM the
|
|
62
|
+
// plain text (real newlines), not the JSON wrapper.
|
|
63
|
+
const parsed = await readJson<{ content?: string }>(result.value);
|
|
64
|
+
if (!parsed.ok) return parsed.result;
|
|
65
|
+
return { content: [{ type: "text", text: parsed.value.content ?? "" }] };
|
|
66
|
+
},
|
|
67
|
+
};
|
|
@@ -23,12 +23,16 @@ export const tool$pipeshubSearch: ToolDefinition<typeof args> = {
|
|
|
23
23
|
description:
|
|
24
24
|
`Vector / semantic search across the org's indexed documents.
|
|
25
25
|
|
|
26
|
-
**Use this
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
does the retrieval internally and grounds the answer in
|
|
26
|
+
**Use this when the user wants to LOCATE a document** — by name, topic,
|
|
27
|
+
or a phrase to grep for — and to resolve it to a \`recordId\`. For
|
|
28
|
+
open-ended questions across many documents, use \`pipeshub_chat\`
|
|
29
|
+
instead, which does the retrieval internally and grounds the answer in
|
|
30
|
+
citations.
|
|
30
31
|
|
|
31
32
|
Typical uses:
|
|
33
|
+
- Resolve a doc name / topic into a \`recordId\` for
|
|
34
|
+
\`pipeshub_get_record_content\` — step 1 of any full-document task
|
|
35
|
+
(summarize, extract, review, "what does the doc say?").
|
|
32
36
|
- Resolve a filename / phrase into a \`recordId\` for
|
|
33
37
|
\`pipeshub_download_record\`.
|
|
34
38
|
- Show the user a ranked list of matching files when they ask "find /
|
package/src/mcp-server/tools.ts
CHANGED
|
@@ -75,9 +75,22 @@ export async function formatResult(
|
|
|
75
75
|
content = data == null
|
|
76
76
|
? []
|
|
77
77
|
: [{ type: "audio", data, mimeType: contentType }];
|
|
78
|
-
} else
|
|
78
|
+
} else if (
|
|
79
|
+
contentType.startsWith("text/")
|
|
80
|
+
|| contentType.includes("json")
|
|
81
|
+
|| contentType.includes("xml")
|
|
82
|
+
|| contentType.includes("yaml")
|
|
83
|
+
) {
|
|
79
84
|
const text = await response.text();
|
|
80
85
|
content = [{ type: "text", text }];
|
|
86
|
+
} else {
|
|
87
|
+
const blob = await valueToBase64(await response.arrayBuffer());
|
|
88
|
+
content = blob == null
|
|
89
|
+
? []
|
|
90
|
+
: [{
|
|
91
|
+
type: "resource",
|
|
92
|
+
resource: { uri: response.url, mimeType: contentType, blob },
|
|
93
|
+
}];
|
|
81
94
|
}
|
|
82
95
|
|
|
83
96
|
return response.ok ? { content } : { content, isError: true };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
|
|
3
|
+
export type GetRecordContentRequest = { recordId: string };
|
|
4
|
+
|
|
5
|
+
export const GetRecordContentRequest$zodSchema: z.ZodType<
|
|
6
|
+
GetRecordContentRequest
|
|
7
|
+
> = z.object({
|
|
8
|
+
recordId: z.string().describe(
|
|
9
|
+
"Record ID to fetch. Obtain it from a `pipeshub_search` result (`hits[*].recordId`) or a chat citation (`citations[*].recordId`).",
|
|
10
|
+
),
|
|
11
|
+
});
|
package/src/tool-names.ts
CHANGED
|
@@ -6,16 +6,20 @@ export const toolNames: Array<{ name: string; description: string }>= [
|
|
|
6
6
|
},
|
|
7
7
|
{
|
|
8
8
|
"name": "pipeshub_chat",
|
|
9
|
-
"description": "**Primary chat tool — handles both internal knowledge queries and web search.**\n\n**Internal search** (default, `chatMode: \"internal_search\"`): Use whenever\nthe user asks about their documents, files, knowledge base, company policies,\nor anything that could plausibly be answered by content in their PipesHub-indexed\nsources (Drive, Box, Confluence, Slack, Gmail, Jira, the org's KB, ...).\nGrounds the answer in the user's actual data and returns citations.\n\n**Web search** (`chatMode: \"web_search\"`): Use when the user asks about\ncurrent events, public information, or anything unlikely to be in the org's\ninternal knowledge base. Pass `chatMode: \"web_search\"` and this tool will\nsearch the public web instead.\n\n**When to pick this over other tools:**\n- \"
|
|
9
|
+
"description": "**Primary chat tool — handles both internal knowledge queries and web search.**\n\n**Internal search** (default, `chatMode: \"internal_search\"`): Use whenever\nthe user asks about their documents, files, knowledge base, company policies,\nor anything that could plausibly be answered by content in their PipesHub-indexed\nsources (Drive, Box, Confluence, Slack, Gmail, Jira, the org's KB, ...).\nGrounds the answer in the user's actual data and returns citations.\nAnswers come from a few retrieved passages, not whole documents — for\nany task needing a document's full content, use\n`pipeshub_get_record_content` instead.\n\n**Web search** (`chatMode: \"web_search\"`): Use when the user asks about\ncurrent events, public information, or anything unlikely to be in the org's\ninternal knowledge base. Pass `chatMode: \"web_search\"` and this tool will\nsearch the public web instead.\n\n**When to pick this over other tools:**\n- \"Summarize <doc>\" / \"key points of <doc>\" / \"what does <document> say\n about X?\" → NOT this tool. Use `pipeshub_search` →\n `pipeshub_get_record_content`: answering for a specific document\n requires its full content, and chat only sees a few retrieved\n passages, never the whole document.\n- \"What's our policy on Y?\" → `pipeshub_chat` (internal_search)\n- \"What's in the news about Z?\" → `pipeshub_chat` (web_search)\n- \"What is the latest version of <library>?\" → `pipeshub_chat` (web_search)\n- \"Find / locate the file named X\" → `pipeshub_search` (then\n `pipeshub_download_record` if the user wants the bytes).\n\n**Conversation lifecycle** — one tool, both start and continue:\n\n- **First turn**: omit `conversationId`. The server creates a new\n conversation; capture `conversationId` from the response.\n- **Follow-up turn**: pass the `conversationId` from the previous\n response. Server-side context is preserved — do NOT replay earlier\n messages, and `filters` is ignored on follow-ups (set once at\n creation).\n\nOnly re-omit `conversationId` (start a fresh conversation) when the\nuser explicitly asks to start over / clear context.\n\nThe response contains the AI's `answer` plus `citations`. To download a\ncited document, take `citations[*].recordId` and call\n`pipeshub_download_record`."
|
|
10
10
|
},
|
|
11
11
|
{
|
|
12
12
|
"name": "pipeshub_search",
|
|
13
|
-
"description": "Vector / semantic search across the org's indexed documents.\n\n**Use this
|
|
13
|
+
"description": "Vector / semantic search across the org's indexed documents.\n\n**Use this when the user wants to LOCATE a document** — by name, topic,\nor a phrase to grep for — and to resolve it to a `recordId`. For\nopen-ended questions across many documents, use `pipeshub_chat`\ninstead, which does the retrieval internally and grounds the answer in\ncitations.\n\nTypical uses:\n- Resolve a doc name / topic into a `recordId` for\n `pipeshub_get_record_content` — step 1 of any full-document task\n (summarize, extract, review, \"what does the doc say?\").\n- Resolve a filename / phrase into a `recordId` for\n `pipeshub_download_record`.\n- Show the user a ranked list of matching files when they ask \"find /\n search for X\".\n\nThe response is trimmed to one row per hit:\n`{ recordId, recordName, score, snippet, mimeType, webUrl, ... }`.\nHighest `score` first; multiple hits may share the same `recordId`\n(different chunks of the same record)."
|
|
14
14
|
},
|
|
15
15
|
{
|
|
16
16
|
"name": "pipeshub_download_record",
|
|
17
17
|
"description": "Stream the binary content of a single record from PipesHub.\n\nTypical sources for the `recordId`:\n- A chat citation:\n `pipeshub_chat` response → `citations[*].recordId`.\n- A search result:\n `pipeshub_search` response → `hits[*].recordId` /\n `uniqueRecords[*].recordId`.\n\nResponse `Content-Type` is forwarded from the upstream service —\n`application/pdf`, `application/octet-stream`, etc. Binary content is\nreturned base64-encoded; text content is returned inline."
|
|
18
18
|
},
|
|
19
|
+
{
|
|
20
|
+
"name": "pipeshub_get_record_content",
|
|
21
|
+
"description": "Read a record's full parsed content by `recordId` — the only way to\nsee a document's COMPLETE text.\n\nUse it whenever the answer depends on a document's full content — any\ntask where missing a part could make the answer wrong: summarize / key\npoints / action items; extract or list ALL of something; check whether\nor where the doc mentions X; translate, rewrite, outline, or review the\ndoc; compare named docs (fetch each); any question scoped to one named\ndocument. `pipeshub_chat` cannot do these — it only sees a few\nretrieved passages, never the whole document. Get the `recordId` from\na `pipeshub_search` top hit or a chat citation.\n\nJudge by the user's INTENT, not their keywords: they need not say\n\"summarize\", \"key points\", or \"extract\". Reason about what a good\nanswer requires — if it would need the whole document (e.g. \"what's\nthis doc about?\", \"walk me through the report\", \"anything in here\nabout Y?\"), that is a full-content task, so call this tool.\n\nReturns a single `content` string: a short metadata header (title,\nsource, key fields, and a pre-generated summary) followed by the\nrecord's full parsed text — paragraphs, tables, and lists in reading\norder. For a record with no extractable content, `content` is the\nliteral `No record found`. Use `pipeshub_download_record` only when\nyou need the original file bytes."
|
|
22
|
+
},
|
|
19
23
|
{
|
|
20
24
|
"name": "pipeshub_directory",
|
|
21
25
|
"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."
|