@pipeshub-ai/mcp 2.1.0 → 2.3.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 +92 -2
- package/bin/mcp-server.js +976 -169
- package/bin/mcp-server.js.map +28 -15
- package/bin/pipeshub.js +946 -0
- package/bin/pipeshub.js.map +14 -0
- package/esm/cli/client.d.ts +54 -0
- package/esm/cli/client.d.ts.map +1 -0
- package/esm/cli/client.js +230 -0
- package/esm/cli/client.js.map +1 -0
- package/esm/cli/commands.d.ts +30 -0
- package/esm/cli/commands.d.ts.map +1 -0
- package/esm/cli/commands.js +321 -0
- package/esm/cli/commands.js.map +1 -0
- package/esm/cli/config.d.ts +73 -0
- package/esm/cli/config.d.ts.map +1 -0
- package/esm/cli/config.js +218 -0
- package/esm/cli/config.js.map +1 -0
- package/esm/cli/init-qm.d.ts +11 -0
- package/esm/cli/init-qm.d.ts.map +1 -0
- package/esm/cli/init-qm.js +170 -0
- package/esm/cli/init-qm.js.map +1 -0
- package/esm/cli/pipeshub.d.ts +2 -0
- package/esm/cli/pipeshub.d.ts.map +1 -0
- package/esm/cli/pipeshub.js +296 -0
- package/esm/cli/pipeshub.js.map +1 -0
- 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/connectorLookupRecordByIdentifier.d.ts +68 -0
- package/esm/funcs/connectorLookupRecordByIdentifier.d.ts.map +1 -0
- package/esm/funcs/connectorLookupRecordByIdentifier.js +138 -0
- package/esm/funcs/connectorLookupRecordByIdentifier.js.map +1 -0
- package/esm/funcs/connectorNavigateKnowledgeGraph.d.ts +75 -0
- package/esm/funcs/connectorNavigateKnowledgeGraph.d.ts.map +1 -0
- package/esm/funcs/connectorNavigateKnowledgeGraph.js +152 -0
- package/esm/funcs/connectorNavigateKnowledgeGraph.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/lib/base64.d.ts +1 -1
- package/esm/lib/base64.d.ts.map +1 -1
- package/esm/lib/base64.js +6 -3
- package/esm/lib/base64.js.map +1 -1
- package/esm/lib/bytes-to-base64.d.ts +16 -0
- package/esm/lib/bytes-to-base64.d.ts.map +1 -0
- package/esm/lib/bytes-to-base64.js +23 -0
- package/esm/lib/bytes-to-base64.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 +62 -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/shared.d.ts +2 -2
- package/esm/mcp-server/shared.d.ts.map +1 -1
- package/esm/mcp-server/shared.js +7 -3
- package/esm/mcp-server/shared.js.map +1 -1
- package/esm/mcp-server/tools/_agui.d.ts +34 -0
- package/esm/mcp-server/tools/_agui.d.ts.map +1 -0
- package/esm/mcp-server/tools/_agui.js +91 -0
- package/esm/mcp-server/tools/_agui.js.map +1 -0
- package/esm/mcp-server/tools/_helpers.d.ts.map +1 -1
- package/esm/mcp-server/tools/_helpers.js +13 -6
- package/esm/mcp-server/tools/_helpers.js.map +1 -1
- package/esm/mcp-server/tools/pipeshubChat.d.ts +0 -3
- package/esm/mcp-server/tools/pipeshubChat.d.ts.map +1 -1
- package/esm/mcp-server/tools/pipeshubChat.js +75 -92
- package/esm/mcp-server/tools/pipeshubChat.js.map +1 -1
- package/esm/mcp-server/tools/pipeshubGetRecordContent.d.ts +125 -0
- package/esm/mcp-server/tools/pipeshubGetRecordContent.d.ts.map +1 -0
- package/esm/mcp-server/tools/pipeshubGetRecordContent.js +214 -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 +22 -5
- 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/availablemodelsresponse.d.ts +1 -1
- package/esm/models/conversation.d.ts +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/models/lookuprecordbyidentifierop.d.ts +7 -0
- package/esm/models/lookuprecordbyidentifierop.d.ts.map +1 -0
- package/esm/models/lookuprecordbyidentifierop.js +14 -0
- package/esm/models/lookuprecordbyidentifierop.js.map +1 -0
- package/esm/models/navigateknowledgegraphop.d.ts +14 -0
- package/esm/models/navigateknowledgegraphop.d.ts.map +1 -0
- package/esm/models/navigateknowledgegraphop.js +25 -0
- package/esm/models/navigateknowledgegraphop.js.map +1 -0
- package/esm/models/userteamsresponse.d.ts +1 -1
- 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 +4 -2
- package/qm/README.md +247 -0
- package/qm/SECURITY.md +134 -0
- package/qm/TROUBLESHOOTING.md +218 -0
- package/qm/qm.config.fragment.jsonc +42 -0
- package/qm/sandbox/Dockerfile +22 -0
- package/qm/sandbox/skills/pipeshub/SKILL.md +69 -0
- package/qm/sandbox/tools/pipeshub/tool.json +36 -0
- package/src/cli/client.ts +272 -0
- package/src/cli/commands.ts +403 -0
- package/src/cli/config.ts +229 -0
- package/src/cli/init-qm.ts +215 -0
- package/src/cli/pipeshub.ts +333 -0
- package/src/funcs/connectorGetRecordContent.ts +179 -0
- package/src/funcs/connectorLookupRecordByIdentifier.ts +200 -0
- package/src/funcs/connectorNavigateKnowledgeGraph.ts +215 -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/lib/base64.ts +6 -3
- package/src/lib/bytes-to-base64.ts +24 -0
- package/src/mcp-server/instructions.ts +62 -9
- package/src/mcp-server/server.ts +11 -3
- package/src/mcp-server/shared.ts +7 -3
- package/src/mcp-server/tools/_agui.ts +138 -0
- package/src/mcp-server/tools/_helpers.ts +13 -6
- package/src/mcp-server/tools/pipeshubChat.ts +78 -91
- package/src/mcp-server/tools/pipeshubGetRecordContent.ts +251 -0
- package/src/mcp-server/tools/pipeshubSearch.ts +22 -5
- package/src/mcp-server/tools.ts +14 -1
- package/src/models/getrecordcontentop.ts +11 -0
- package/src/models/lookuprecordbyidentifierop.ts +26 -0
- package/src/models/navigateknowledgegraphop.ts +56 -0
- package/src/tool-names.ts +6 -2
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Adapted from Speakeasy-generated connectorGetRecordContent.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { PipeshubCore } from "../core.js";
|
|
6
|
+
import { encodeSimple } from "../lib/encodings.js";
|
|
7
|
+
import { compactMap } from "../lib/primitives.js";
|
|
8
|
+
import { safeParse } from "../lib/schemas.js";
|
|
9
|
+
import { RequestOptions } from "../lib/sdks.js";
|
|
10
|
+
import { extractSecurity, resolveGlobalSecurity } from "../lib/security.js";
|
|
11
|
+
import { pathToFunc } from "../lib/url.js";
|
|
12
|
+
import { APIError } from "../models/errors/apierror.js";
|
|
13
|
+
import {
|
|
14
|
+
ConnectionError,
|
|
15
|
+
InvalidRequestError,
|
|
16
|
+
RequestAbortedError,
|
|
17
|
+
RequestTimeoutError,
|
|
18
|
+
UnexpectedClientError,
|
|
19
|
+
} from "../models/errors/httpclienterrors.js";
|
|
20
|
+
import { SDKValidationError } from "../models/errors/sdkvalidationerror.js";
|
|
21
|
+
import {
|
|
22
|
+
GetRecordContentRequest,
|
|
23
|
+
GetRecordContentRequest$zodSchema,
|
|
24
|
+
} from "../models/getrecordcontentop.js";
|
|
25
|
+
import { APICall, APIPromise } from "../types/async.js";
|
|
26
|
+
import { Result } from "../types/fp.js";
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Get a record's full parsed content and metadata
|
|
30
|
+
*
|
|
31
|
+
* @remarks
|
|
32
|
+
* Retrieve the full parsed content and metadata of a single record —
|
|
33
|
+
* the same content PipesHub's own RAG/chat pipeline uses to answer
|
|
34
|
+
* questions, returned directly instead of via chat.
|
|
35
|
+
*
|
|
36
|
+
* **When to use this vs. the other record endpoints:**
|
|
37
|
+
* - `GET /knowledgeBase/record/{recordId}` returns metadata only
|
|
38
|
+
* (name, type, indexing status, size) — no content.
|
|
39
|
+
* - `GET /knowledgeBase/stream/record/{recordId}` returns the original,
|
|
40
|
+
* unparsed file bytes — use it to download/open the source file.
|
|
41
|
+
* - **This endpoint** returns the record's full parsed content as a
|
|
42
|
+
* single plain-text `content` string (a metadata header, then the
|
|
43
|
+
* block/table text in reading order) — use it when you need the
|
|
44
|
+
* record's actual textual/tabular content without downloading and
|
|
45
|
+
* re-parsing the original file yourself.
|
|
46
|
+
*
|
|
47
|
+
* **Typical flow:** obtain a `recordId` from a `pipeshub_search` hit or
|
|
48
|
+
* a chat citation's `recordId`, then call this endpoint to read the
|
|
49
|
+
* full content when the search snippet or citation excerpt isn't
|
|
50
|
+
* enough to answer the question.
|
|
51
|
+
*
|
|
52
|
+
* **Permission scoping:**
|
|
53
|
+
*
|
|
54
|
+
* The requesting user/token must have access to the record; access is
|
|
55
|
+
* verified via the knowledge graph before content is returned — a
|
|
56
|
+
* caller with a valid scope but no access to this specific record gets
|
|
57
|
+
* a `403`.
|
|
58
|
+
*
|
|
59
|
+
* If set, this operation will use either {@link Security.bearerAuth} or {@link Security.oauth2} from the global security.
|
|
60
|
+
*/
|
|
61
|
+
export function connectorGetRecordContent(
|
|
62
|
+
client$: PipeshubCore,
|
|
63
|
+
request: GetRecordContentRequest,
|
|
64
|
+
options?: RequestOptions,
|
|
65
|
+
): APIPromise<
|
|
66
|
+
Result<
|
|
67
|
+
Response,
|
|
68
|
+
| APIError
|
|
69
|
+
| SDKValidationError
|
|
70
|
+
| UnexpectedClientError
|
|
71
|
+
| InvalidRequestError
|
|
72
|
+
| RequestAbortedError
|
|
73
|
+
| RequestTimeoutError
|
|
74
|
+
| ConnectionError
|
|
75
|
+
>
|
|
76
|
+
> {
|
|
77
|
+
return new APIPromise($do(
|
|
78
|
+
client$,
|
|
79
|
+
request,
|
|
80
|
+
options,
|
|
81
|
+
));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function $do(
|
|
85
|
+
client$: PipeshubCore,
|
|
86
|
+
request: GetRecordContentRequest,
|
|
87
|
+
options?: RequestOptions,
|
|
88
|
+
): Promise<
|
|
89
|
+
[
|
|
90
|
+
Result<
|
|
91
|
+
Response,
|
|
92
|
+
| APIError
|
|
93
|
+
| SDKValidationError
|
|
94
|
+
| UnexpectedClientError
|
|
95
|
+
| InvalidRequestError
|
|
96
|
+
| RequestAbortedError
|
|
97
|
+
| RequestTimeoutError
|
|
98
|
+
| ConnectionError
|
|
99
|
+
>,
|
|
100
|
+
APICall,
|
|
101
|
+
]
|
|
102
|
+
> {
|
|
103
|
+
const parsed$ = safeParse(
|
|
104
|
+
request,
|
|
105
|
+
(value$) => GetRecordContentRequest$zodSchema.parse(value$),
|
|
106
|
+
"Input validation failed",
|
|
107
|
+
);
|
|
108
|
+
if (!parsed$.ok) {
|
|
109
|
+
return [parsed$, { status: "invalid" }];
|
|
110
|
+
}
|
|
111
|
+
const payload$ = parsed$.value;
|
|
112
|
+
const body$ = null;
|
|
113
|
+
|
|
114
|
+
const pathParams$ = {
|
|
115
|
+
recordId: encodeSimple("recordId", payload$.recordId, {
|
|
116
|
+
explode: false,
|
|
117
|
+
charEncoding: "percent",
|
|
118
|
+
}),
|
|
119
|
+
};
|
|
120
|
+
const path$ = pathToFunc("/connectors/record/{recordId}/content")(
|
|
121
|
+
pathParams$,
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
const headers$ = new Headers(compactMap({
|
|
125
|
+
Accept: "application/json",
|
|
126
|
+
}));
|
|
127
|
+
const securityInput = await extractSecurity(client$._options.security);
|
|
128
|
+
const requestSecurity = resolveGlobalSecurity(securityInput);
|
|
129
|
+
|
|
130
|
+
const context = {
|
|
131
|
+
options: client$._options,
|
|
132
|
+
baseURL: options?.serverURL ?? client$._baseURL ?? "",
|
|
133
|
+
operationID: "getRecordContent",
|
|
134
|
+
oAuth2Scopes: ["connector:read"],
|
|
135
|
+
resolvedSecurity: requestSecurity,
|
|
136
|
+
securitySource: client$._options.security,
|
|
137
|
+
retryConfig: options?.retries
|
|
138
|
+
|| client$._options.retryConfig
|
|
139
|
+
|| { strategy: "none" },
|
|
140
|
+
retryCodes: options?.retryCodes || [
|
|
141
|
+
"429",
|
|
142
|
+
"500",
|
|
143
|
+
"502",
|
|
144
|
+
"503",
|
|
145
|
+
"504",
|
|
146
|
+
],
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
const requestRes = client$._createRequest(context, {
|
|
150
|
+
security: requestSecurity,
|
|
151
|
+
method: "GET",
|
|
152
|
+
baseURL: options?.serverURL,
|
|
153
|
+
path: path$,
|
|
154
|
+
headers: headers$,
|
|
155
|
+
body: body$,
|
|
156
|
+
userAgent: client$._options.userAgent,
|
|
157
|
+
timeoutMs: options?.timeoutMs || client$._options.timeoutMs
|
|
158
|
+
|| -1,
|
|
159
|
+
}, options);
|
|
160
|
+
if (!requestRes.ok) {
|
|
161
|
+
return [requestRes, { status: "invalid" }];
|
|
162
|
+
}
|
|
163
|
+
const req$ = requestRes.value;
|
|
164
|
+
|
|
165
|
+
const doResult = await client$._do(req$, {
|
|
166
|
+
context,
|
|
167
|
+
errorCodes: [],
|
|
168
|
+
retryConfig: context.retryConfig,
|
|
169
|
+
retryCodes: context.retryCodes,
|
|
170
|
+
});
|
|
171
|
+
if (!doResult.ok) {
|
|
172
|
+
return [doResult, { status: "request-error", request: req$ }];
|
|
173
|
+
}
|
|
174
|
+
return [doResult, {
|
|
175
|
+
status: "complete",
|
|
176
|
+
"request": req$,
|
|
177
|
+
response: doResult.value,
|
|
178
|
+
}];
|
|
179
|
+
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Adapted from Speakeasy-generated connectorLookupRecordByIdentifier.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { PipeshubCore } from "../core.js";
|
|
6
|
+
import { encodeFormQuery } from "../lib/encodings.js";
|
|
7
|
+
import { compactMap } from "../lib/primitives.js";
|
|
8
|
+
import { safeParse } from "../lib/schemas.js";
|
|
9
|
+
import { RequestOptions } from "../lib/sdks.js";
|
|
10
|
+
import { extractSecurity, resolveGlobalSecurity } from "../lib/security.js";
|
|
11
|
+
import { pathToFunc } from "../lib/url.js";
|
|
12
|
+
import { APIError } from "../models/errors/apierror.js";
|
|
13
|
+
import {
|
|
14
|
+
ConnectionError,
|
|
15
|
+
InvalidRequestError,
|
|
16
|
+
RequestAbortedError,
|
|
17
|
+
RequestTimeoutError,
|
|
18
|
+
UnexpectedClientError,
|
|
19
|
+
} from "../models/errors/httpclienterrors.js";
|
|
20
|
+
import { SDKValidationError } from "../models/errors/sdkvalidationerror.js";
|
|
21
|
+
import {
|
|
22
|
+
LookupRecordByIdentifierRequest,
|
|
23
|
+
LookupRecordByIdentifierRequest$zodSchema,
|
|
24
|
+
} from "../models/lookuprecordbyidentifierop.js";
|
|
25
|
+
import { APICall, APIPromise } from "../types/async.js";
|
|
26
|
+
import { Result } from "../types/fp.js";
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Resolve a URL, issue key or external ID to a Record ID
|
|
30
|
+
*
|
|
31
|
+
* @remarks
|
|
32
|
+
* Turn an external reference into the matching PipesHub record. Accepts a
|
|
33
|
+
* pasted link, a Jira-style issue key, or a bare external system ID from
|
|
34
|
+
* any connected source. Repeat `identifiers` to batch-resolve up to ten
|
|
35
|
+
* in one call.
|
|
36
|
+
*
|
|
37
|
+
* The response carries a rendered `text` view — each match's metadata
|
|
38
|
+
* block followed by a `Next:` line naming a follow-up call. The structured
|
|
39
|
+
* fields carry the same information for programmatic use.
|
|
40
|
+
*
|
|
41
|
+
* **Accepted identifiers**
|
|
42
|
+
* - Jira issue URL — `https://acme.atlassian.net/browse/PA-1787`
|
|
43
|
+
* - Jira issue key — `PA-1787`
|
|
44
|
+
* - Confluence page URL —
|
|
45
|
+
* `https://acme.atlassian.net/wiki/spaces/SD/pages/450625553/Agent+Loop`
|
|
46
|
+
* - Google Drive / Docs URL — `https://docs.google.com/document/d/1AbC.../edit`
|
|
47
|
+
* - Slack message link — `https://acme.slack.com/archives/C0123/p1720000000000100`
|
|
48
|
+
* - Bare external system ID — `450625553`
|
|
49
|
+
*
|
|
50
|
+
* **When to use this vs. the other record endpoints:**
|
|
51
|
+
* - **This endpoint** converts an *external* reference into an internal
|
|
52
|
+
* Record ID. Reach for it whenever you meet a link or ticket key and
|
|
53
|
+
* need the record behind it.
|
|
54
|
+
* - `GET /connectors/record/{recordId}/content` reads a record you have
|
|
55
|
+
* already identified. It needs an internal Record ID, which is exactly
|
|
56
|
+
* what this endpoint returns.
|
|
57
|
+
* - `GET /connectors/navigate` browses the hierarchy when you have a
|
|
58
|
+
* position in the tree rather than a specific identifier.
|
|
59
|
+
*
|
|
60
|
+
* **Typical flow:** call this with the reference, take a match's `id`
|
|
61
|
+
* from the response, then call
|
|
62
|
+
* `GET /connectors/record/{recordId}/content` to read the record.
|
|
63
|
+
*
|
|
64
|
+
* **Multiple matches:** one identifier can legitimately match more than
|
|
65
|
+
* one record — the same external ID may exist in several connected
|
|
66
|
+
* instances. In that case the response sets `ambiguous: true` and
|
|
67
|
+
* `matches` holds every candidate. Present the choice rather than taking
|
|
68
|
+
* the first; `connectorName` narrows a retry.
|
|
69
|
+
*
|
|
70
|
+
* **Misses are not errors.**
|
|
71
|
+
*
|
|
72
|
+
* Only records the caller can see are returned, and a miss is a `200`
|
|
73
|
+
* with an empty `matches` array and the input echoed in
|
|
74
|
+
* `not_found_identifiers` — not a `404`. The identifier resolved to
|
|
75
|
+
* nothing *or* to something the caller may not access; the two are
|
|
76
|
+
* deliberately indistinguishable, because an identifier is
|
|
77
|
+
* caller-supplied and guessable, and confirming existence would leak
|
|
78
|
+
* records across organizations. `searched_connectors` names what was
|
|
79
|
+
* covered, so a retry with `connectorName` is often the right next move.
|
|
80
|
+
*
|
|
81
|
+
* **Scope:** resolution searches every connector the caller can access,
|
|
82
|
+
* regardless of any source filter used elsewhere.
|
|
83
|
+
*
|
|
84
|
+
* If set, this operation will use either {@link Security.bearerAuth} or {@link Security.oauth2} from the global security.
|
|
85
|
+
*/
|
|
86
|
+
export function connectorLookupRecordByIdentifier(
|
|
87
|
+
client$: PipeshubCore,
|
|
88
|
+
request: LookupRecordByIdentifierRequest,
|
|
89
|
+
options?: RequestOptions,
|
|
90
|
+
): APIPromise<
|
|
91
|
+
Result<
|
|
92
|
+
Response,
|
|
93
|
+
| APIError
|
|
94
|
+
| SDKValidationError
|
|
95
|
+
| UnexpectedClientError
|
|
96
|
+
| InvalidRequestError
|
|
97
|
+
| RequestAbortedError
|
|
98
|
+
| RequestTimeoutError
|
|
99
|
+
| ConnectionError
|
|
100
|
+
>
|
|
101
|
+
> {
|
|
102
|
+
return new APIPromise($do(
|
|
103
|
+
client$,
|
|
104
|
+
request,
|
|
105
|
+
options,
|
|
106
|
+
));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async function $do(
|
|
110
|
+
client$: PipeshubCore,
|
|
111
|
+
request: LookupRecordByIdentifierRequest,
|
|
112
|
+
options?: RequestOptions,
|
|
113
|
+
): Promise<
|
|
114
|
+
[
|
|
115
|
+
Result<
|
|
116
|
+
Response,
|
|
117
|
+
| APIError
|
|
118
|
+
| SDKValidationError
|
|
119
|
+
| UnexpectedClientError
|
|
120
|
+
| InvalidRequestError
|
|
121
|
+
| RequestAbortedError
|
|
122
|
+
| RequestTimeoutError
|
|
123
|
+
| ConnectionError
|
|
124
|
+
>,
|
|
125
|
+
APICall,
|
|
126
|
+
]
|
|
127
|
+
> {
|
|
128
|
+
const parsed$ = safeParse(
|
|
129
|
+
request,
|
|
130
|
+
(value$) => LookupRecordByIdentifierRequest$zodSchema.parse(value$),
|
|
131
|
+
"Input validation failed",
|
|
132
|
+
);
|
|
133
|
+
if (!parsed$.ok) {
|
|
134
|
+
return [parsed$, { status: "invalid" }];
|
|
135
|
+
}
|
|
136
|
+
const payload$ = parsed$.value;
|
|
137
|
+
const body$ = null;
|
|
138
|
+
const path$ = pathToFunc("/connectors/record/lookup")();
|
|
139
|
+
const query$ = encodeFormQuery({
|
|
140
|
+
"connectorName": payload$.connectorName,
|
|
141
|
+
"identifiers": payload$.identifiers,
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
const headers$ = new Headers(compactMap({
|
|
145
|
+
Accept: "application/json",
|
|
146
|
+
}));
|
|
147
|
+
const securityInput = await extractSecurity(client$._options.security);
|
|
148
|
+
const requestSecurity = resolveGlobalSecurity(securityInput);
|
|
149
|
+
|
|
150
|
+
const context = {
|
|
151
|
+
options: client$._options,
|
|
152
|
+
baseURL: options?.serverURL ?? client$._baseURL ?? "",
|
|
153
|
+
operationID: "lookupRecordByIdentifier",
|
|
154
|
+
oAuth2Scopes: ["kb:read", "connector:read"],
|
|
155
|
+
resolvedSecurity: requestSecurity,
|
|
156
|
+
securitySource: client$._options.security,
|
|
157
|
+
retryConfig: options?.retries
|
|
158
|
+
|| client$._options.retryConfig
|
|
159
|
+
|| { strategy: "none" },
|
|
160
|
+
retryCodes: options?.retryCodes || [
|
|
161
|
+
"429",
|
|
162
|
+
"500",
|
|
163
|
+
"502",
|
|
164
|
+
"503",
|
|
165
|
+
"504",
|
|
166
|
+
],
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
const requestRes = client$._createRequest(context, {
|
|
170
|
+
security: requestSecurity,
|
|
171
|
+
method: "GET",
|
|
172
|
+
baseURL: options?.serverURL,
|
|
173
|
+
path: path$,
|
|
174
|
+
headers: headers$,
|
|
175
|
+
query: query$,
|
|
176
|
+
body: body$,
|
|
177
|
+
userAgent: client$._options.userAgent,
|
|
178
|
+
timeoutMs: options?.timeoutMs || client$._options.timeoutMs
|
|
179
|
+
|| -1,
|
|
180
|
+
}, options);
|
|
181
|
+
if (!requestRes.ok) {
|
|
182
|
+
return [requestRes, { status: "invalid" }];
|
|
183
|
+
}
|
|
184
|
+
const req$ = requestRes.value;
|
|
185
|
+
|
|
186
|
+
const doResult = await client$._do(req$, {
|
|
187
|
+
context,
|
|
188
|
+
errorCodes: [],
|
|
189
|
+
retryConfig: context.retryConfig,
|
|
190
|
+
retryCodes: context.retryCodes,
|
|
191
|
+
});
|
|
192
|
+
if (!doResult.ok) {
|
|
193
|
+
return [doResult, { status: "request-error", request: req$ }];
|
|
194
|
+
}
|
|
195
|
+
return [doResult, {
|
|
196
|
+
status: "complete",
|
|
197
|
+
"request": req$,
|
|
198
|
+
response: doResult.value,
|
|
199
|
+
}];
|
|
200
|
+
}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Adapted from Speakeasy-generated connectorNavigateKnowledgeGraph.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { PipeshubCore } from "../core.js";
|
|
6
|
+
import { encodeFormQuery } from "../lib/encodings.js";
|
|
7
|
+
import { compactMap } from "../lib/primitives.js";
|
|
8
|
+
import { safeParse } from "../lib/schemas.js";
|
|
9
|
+
import { RequestOptions } from "../lib/sdks.js";
|
|
10
|
+
import { extractSecurity, resolveGlobalSecurity } from "../lib/security.js";
|
|
11
|
+
import { pathToFunc } from "../lib/url.js";
|
|
12
|
+
import { APIError } from "../models/errors/apierror.js";
|
|
13
|
+
import {
|
|
14
|
+
ConnectionError,
|
|
15
|
+
InvalidRequestError,
|
|
16
|
+
RequestAbortedError,
|
|
17
|
+
RequestTimeoutError,
|
|
18
|
+
UnexpectedClientError,
|
|
19
|
+
} from "../models/errors/httpclienterrors.js";
|
|
20
|
+
import { SDKValidationError } from "../models/errors/sdkvalidationerror.js";
|
|
21
|
+
import {
|
|
22
|
+
NavigateKnowledgeGraphRequest,
|
|
23
|
+
NavigateKnowledgeGraphRequest$zodSchema,
|
|
24
|
+
} from "../models/navigateknowledgegraphop.js";
|
|
25
|
+
import { APICall, APIPromise } from "../types/async.js";
|
|
26
|
+
import { Result } from "../types/fp.js";
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Browse the knowledge graph from a node
|
|
30
|
+
*
|
|
31
|
+
* @remarks
|
|
32
|
+
* Open a node in the knowledge graph and see what is inside it — a file
|
|
33
|
+
* explorer across every connected source.
|
|
34
|
+
*
|
|
35
|
+
* Call it with no `nodeId` for a flat listing of every record group and
|
|
36
|
+
* record the caller can reach, newest first. This is a starting point to
|
|
37
|
+
* pick a node from, not a roster of connected apps — app nodes are never
|
|
38
|
+
* returned in a listing, though an app's `id` is accepted as a `nodeId`
|
|
39
|
+
* and lists that app's record groups. Pass a node's `id` to descend:
|
|
40
|
+
* record groups contain records and folders, and a record contains its
|
|
41
|
+
* own children — comments, attachments, sub-tasks — plus a `related`
|
|
42
|
+
* section of cross-referenced records, such as the Confluence page linked
|
|
43
|
+
* from a Jira ticket. `nodeId` is tolerant: a URL or an issue key such as
|
|
44
|
+
* `PA-1787` is resolved to its record before navigating, so a link can be
|
|
45
|
+
* pasted straight in without a separate lookup call.
|
|
46
|
+
*
|
|
47
|
+
* The response carries a rendered `text` view — breadcrumbs, the current
|
|
48
|
+
* node, the children listing, `Related:`, and a closing `Next:` line
|
|
49
|
+
* naming a follow-up call. The structured fields carry the same
|
|
50
|
+
* information for programmatic use.
|
|
51
|
+
*
|
|
52
|
+
* **When to use this vs. the other record endpoints:**
|
|
53
|
+
* - **This endpoint** is for structural exploration — "what is in this
|
|
54
|
+
* project", "what is attached to this ticket", "what else links to this
|
|
55
|
+
* page". It returns names, types and IDs; it never returns document
|
|
56
|
+
* text.
|
|
57
|
+
* - `GET /connectors/record/{recordId}/content` returns one record's
|
|
58
|
+
* actual parsed text. Use it once navigation has identified the record
|
|
59
|
+
* you want to read.
|
|
60
|
+
* - `GET /connectors/record/lookup` is the way in when you hold a URL or
|
|
61
|
+
* an issue key rather than a position in the tree.
|
|
62
|
+
*
|
|
63
|
+
* **Typical flow:** call with no `nodeId` to see what is reachable → pass
|
|
64
|
+
* a record group's `id` to list its records → take a row whose
|
|
65
|
+
* `is_record` is true and call
|
|
66
|
+
* `GET /connectors/record/{recordId}/content` to read it.
|
|
67
|
+
*
|
|
68
|
+
* **Paging and depth:** results are paginated; `pagination.has_next`
|
|
69
|
+
* tells you whether to request the next `page`. `depth` above 1 returns
|
|
70
|
+
* all descendants down to that level as one flat list, each row carrying
|
|
71
|
+
* its own `level`, instead of only direct children.
|
|
72
|
+
*
|
|
73
|
+
* **Scope:** everything the caller can read, across both connectors and
|
|
74
|
+
* Knowledge Base collections. No connector-level filter is applied — the
|
|
75
|
+
* listing is bounded by per-node permissions alone.
|
|
76
|
+
*
|
|
77
|
+
* **Permission scoping:**
|
|
78
|
+
*
|
|
79
|
+
* `rows` and `related` carry only nodes the caller can see, and the
|
|
80
|
+
* opened node itself is access-checked before any of its details are
|
|
81
|
+
* returned. A node that does not exist and a node the caller cannot
|
|
82
|
+
* access are deliberately indistinguishable — both return an empty view
|
|
83
|
+
* rather than an error.
|
|
84
|
+
*
|
|
85
|
+
* `breadcrumbs` is the exception: the ancestor trail is resolved by id
|
|
86
|
+
* alone, without a permission check. For a record shared directly with
|
|
87
|
+
* the caller, it can therefore name ancestors the caller cannot open.
|
|
88
|
+
* Treat breadcrumb entries as labels, not as nodes guaranteed to be
|
|
89
|
+
* navigable.
|
|
90
|
+
*
|
|
91
|
+
* If set, this operation will use either {@link Security.bearerAuth} or {@link Security.oauth2} from the global security.
|
|
92
|
+
*/
|
|
93
|
+
export function connectorNavigateKnowledgeGraph(
|
|
94
|
+
client$: PipeshubCore,
|
|
95
|
+
request?: NavigateKnowledgeGraphRequest | undefined,
|
|
96
|
+
options?: RequestOptions,
|
|
97
|
+
): APIPromise<
|
|
98
|
+
Result<
|
|
99
|
+
Response,
|
|
100
|
+
| APIError
|
|
101
|
+
| SDKValidationError
|
|
102
|
+
| UnexpectedClientError
|
|
103
|
+
| InvalidRequestError
|
|
104
|
+
| RequestAbortedError
|
|
105
|
+
| RequestTimeoutError
|
|
106
|
+
| ConnectionError
|
|
107
|
+
>
|
|
108
|
+
> {
|
|
109
|
+
return new APIPromise($do(
|
|
110
|
+
client$,
|
|
111
|
+
request,
|
|
112
|
+
options,
|
|
113
|
+
));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async function $do(
|
|
117
|
+
client$: PipeshubCore,
|
|
118
|
+
request?: NavigateKnowledgeGraphRequest | undefined,
|
|
119
|
+
options?: RequestOptions,
|
|
120
|
+
): Promise<
|
|
121
|
+
[
|
|
122
|
+
Result<
|
|
123
|
+
Response,
|
|
124
|
+
| APIError
|
|
125
|
+
| SDKValidationError
|
|
126
|
+
| UnexpectedClientError
|
|
127
|
+
| InvalidRequestError
|
|
128
|
+
| RequestAbortedError
|
|
129
|
+
| RequestTimeoutError
|
|
130
|
+
| ConnectionError
|
|
131
|
+
>,
|
|
132
|
+
APICall,
|
|
133
|
+
]
|
|
134
|
+
> {
|
|
135
|
+
const parsed$ = safeParse(
|
|
136
|
+
request,
|
|
137
|
+
(value$) =>
|
|
138
|
+
NavigateKnowledgeGraphRequest$zodSchema.optional().parse(value$),
|
|
139
|
+
"Input validation failed",
|
|
140
|
+
);
|
|
141
|
+
if (!parsed$.ok) {
|
|
142
|
+
return [parsed$, { status: "invalid" }];
|
|
143
|
+
}
|
|
144
|
+
const payload$ = parsed$.value;
|
|
145
|
+
const body$ = null;
|
|
146
|
+
const path$ = pathToFunc("/connectors/navigate")();
|
|
147
|
+
const query$ = encodeFormQuery({
|
|
148
|
+
"createdAfter": payload$?.createdAfter,
|
|
149
|
+
"createdBefore": payload$?.createdBefore,
|
|
150
|
+
"depth": payload$?.depth,
|
|
151
|
+
"limit": payload$?.limit,
|
|
152
|
+
"modifiedAfter": payload$?.modifiedAfter,
|
|
153
|
+
"modifiedBefore": payload$?.modifiedBefore,
|
|
154
|
+
"nodeId": payload$?.nodeId,
|
|
155
|
+
"nodeTypes": payload$?.nodeTypes,
|
|
156
|
+
"page": payload$?.page,
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
const headers$ = new Headers(compactMap({
|
|
160
|
+
Accept: "application/json",
|
|
161
|
+
}));
|
|
162
|
+
const securityInput = await extractSecurity(client$._options.security);
|
|
163
|
+
const requestSecurity = resolveGlobalSecurity(securityInput);
|
|
164
|
+
|
|
165
|
+
const context = {
|
|
166
|
+
options: client$._options,
|
|
167
|
+
baseURL: options?.serverURL ?? client$._baseURL ?? "",
|
|
168
|
+
operationID: "navigateKnowledgeGraph",
|
|
169
|
+
oAuth2Scopes: ["kb:read", "connector:read"],
|
|
170
|
+
resolvedSecurity: requestSecurity,
|
|
171
|
+
securitySource: client$._options.security,
|
|
172
|
+
retryConfig: options?.retries
|
|
173
|
+
|| client$._options.retryConfig
|
|
174
|
+
|| { strategy: "none" },
|
|
175
|
+
retryCodes: options?.retryCodes || [
|
|
176
|
+
"429",
|
|
177
|
+
"500",
|
|
178
|
+
"502",
|
|
179
|
+
"503",
|
|
180
|
+
"504",
|
|
181
|
+
],
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
const requestRes = client$._createRequest(context, {
|
|
185
|
+
security: requestSecurity,
|
|
186
|
+
method: "GET",
|
|
187
|
+
baseURL: options?.serverURL,
|
|
188
|
+
path: path$,
|
|
189
|
+
headers: headers$,
|
|
190
|
+
query: query$,
|
|
191
|
+
body: body$,
|
|
192
|
+
userAgent: client$._options.userAgent,
|
|
193
|
+
timeoutMs: options?.timeoutMs || client$._options.timeoutMs
|
|
194
|
+
|| -1,
|
|
195
|
+
}, options);
|
|
196
|
+
if (!requestRes.ok) {
|
|
197
|
+
return [requestRes, { status: "invalid" }];
|
|
198
|
+
}
|
|
199
|
+
const req$ = requestRes.value;
|
|
200
|
+
|
|
201
|
+
const doResult = await client$._do(req$, {
|
|
202
|
+
context,
|
|
203
|
+
errorCodes: [],
|
|
204
|
+
retryConfig: context.retryConfig,
|
|
205
|
+
retryCodes: context.retryCodes,
|
|
206
|
+
});
|
|
207
|
+
if (!doResult.ok) {
|
|
208
|
+
return [doResult, { status: "request-error", request: req$ }];
|
|
209
|
+
}
|
|
210
|
+
return [doResult, {
|
|
211
|
+
status: "complete",
|
|
212
|
+
"request": req$,
|
|
213
|
+
response: doResult.value,
|
|
214
|
+
}];
|
|
215
|
+
}
|
|
@@ -87,7 +87,7 @@ async function $do(
|
|
|
87
87
|
options: client$._options,
|
|
88
88
|
baseURL: options?.serverURL ?? client$._baseURL ?? "",
|
|
89
89
|
operationID: "streamConversation",
|
|
90
|
-
oAuth2Scopes: ["conversation:
|
|
90
|
+
oAuth2Scopes: ["conversation:chat"],
|
|
91
91
|
resolvedSecurity: requestSecurity,
|
|
92
92
|
securitySource: client$._options.security,
|
|
93
93
|
retryConfig: options?.retries
|
|
@@ -114,7 +114,7 @@ async function $do(
|
|
|
114
114
|
options: client$._options,
|
|
115
115
|
baseURL: options?.serverURL ?? client$._baseURL ?? "",
|
|
116
116
|
operationID: "getAllUserGroups",
|
|
117
|
-
oAuth2Scopes: [],
|
|
117
|
+
oAuth2Scopes: ["usergroup:read"],
|
|
118
118
|
resolvedSecurity: requestSecurity,
|
|
119
119
|
securitySource: client$._options.security,
|
|
120
120
|
retryConfig: options?.retries
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { RequestIDHook } from "./requestid.js";
|
|
1
2
|
import { Hooks } from "./types.js";
|
|
2
3
|
|
|
3
4
|
/*
|
|
@@ -6,9 +7,9 @@ import { Hooks } from "./types.js";
|
|
|
6
7
|
* in this file or in separate files in the hooks folder.
|
|
7
8
|
*/
|
|
8
9
|
|
|
9
|
-
// @ts-expect-error remove this line when you add your first hook and hooks is used
|
|
10
10
|
export function initHooks(hooks: Hooks) {
|
|
11
11
|
// Add hooks by calling hooks.register{ClientInit/BeforeCreateRequest/BeforeRequest/AfterSuccess/AfterError}Hook
|
|
12
12
|
// with an instance of a hook that implements that specific Hook interface
|
|
13
13
|
// Hooks are registered per SDK instance, and are valid for the lifetime of the SDK instance
|
|
14
|
+
hooks.registerBeforeRequestHook(new RequestIDHook());
|
|
14
15
|
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Ambient per-tool-call request-id context (AsyncLocalStorage). Mirrors the
|
|
3
|
+
* pipeshub-ai backend's own request-context pattern (same header name,
|
|
4
|
+
* `x-request-id`) so a single id traces one MCP tool/resource/prompt
|
|
5
|
+
* invocation across every backend HTTP call it makes.
|
|
6
|
+
*
|
|
7
|
+
* `tools.ts` / `resources.ts` / `prompts.ts` are Speakeasy-generated and
|
|
8
|
+
* regenerated on every build, so there is no `next()`-style wrapper point to
|
|
9
|
+
* bind context around a call. Instead `bindNewRequestId` is called from the
|
|
10
|
+
* hand-written `getClient` closure in `server.ts`, which every generated
|
|
11
|
+
* dispatch site calls synchronously as the first thing before invoking a
|
|
12
|
+
* tool/resource/prompt handler — `enterWith` binds the store for the rest of
|
|
13
|
+
* that handler's execution (sync remainder + everything it awaits).
|
|
14
|
+
*
|
|
15
|
+
* The final id (`mcp-<userId>-<random>` or `mcp-<random>`) can't be composed
|
|
16
|
+
* at bind time — `getClient` only has a `PipeshubCore`, not yet a resolved
|
|
17
|
+
* `Authorization` header, and resolving security may be async (OAuth2)
|
|
18
|
+
* while `getClient` must stay sync (generated call sites pass its return
|
|
19
|
+
* value straight into a handler, never await it). So only the random suffix
|
|
20
|
+
* is fixed here; `requestid.ts`'s hook fills in `finalId` off the first
|
|
21
|
+
* outbound request's `Authorization` header and every later call in the
|
|
22
|
+
* same tool invocation reuses that cached value.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
26
|
+
import { nanoid } from "nanoid";
|
|
27
|
+
|
|
28
|
+
export interface RequestContextStore {
|
|
29
|
+
random: string;
|
|
30
|
+
finalId?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const storage = new AsyncLocalStorage<RequestContextStore>();
|
|
34
|
+
|
|
35
|
+
/** Bind a fresh request-id context for the remainder of the current async chain. */
|
|
36
|
+
export function bindNewRequestId(): void {
|
|
37
|
+
storage.enterWith({ random: nanoid() });
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function getRequestContextStore(): RequestContextStore | undefined {
|
|
41
|
+
return storage.getStore();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** One-off id for the rare case a backend call happens with no bound tool-call context. */
|
|
45
|
+
export function newRequestId(): string {
|
|
46
|
+
return `mcp-${nanoid()}`;
|
|
47
|
+
}
|