@sentry/junior 0.203.0 → 0.205.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/dist/agent-D4nu8MQV.js +17521 -0
- package/dist/agent-hooks-DXHpTJ4m.js +4042 -0
- package/dist/agent-runner-C04colea.js +678 -0
- package/dist/api/schema.d.ts +2736 -2639
- package/dist/api/schema.js +4 -169
- package/dist/api.d.ts +23 -17
- package/dist/api.js +3881 -5326
- package/dist/app-Cb776lL8.d.ts +247 -0
- package/dist/app.d.ts +4 -520
- package/dist/app.js +9524 -11875
- package/dist/auth-pause-state-C2P3guS1.js +168 -0
- package/dist/catalog-runtime-CXw3asJD.js +7 -0
- package/dist/checkpoint-CrViJ8Ps.js +2450 -0
- package/dist/cli/chat.d.ts +20 -0
- package/dist/cli/chat.js +229 -336
- package/dist/cli/check.d.ts +8 -0
- package/dist/cli/check.js +513 -806
- package/dist/cli/env.d.ts +13 -0
- package/dist/cli/env.js +63 -12
- package/dist/cli/init.d.ts +3 -0
- package/dist/cli/init.js +90 -159
- package/dist/cli/main.d.ts +6 -0
- package/dist/cli/main.js +43 -64
- package/dist/cli/plugins.d.ts +13 -0
- package/dist/cli/plugins.js +148 -227
- package/dist/cli/run.d.ts +16 -0
- package/dist/cli/run.js +52 -8
- package/dist/cli/snapshot-create.d.ts +4 -0
- package/dist/cli/snapshot-create.js +71 -125
- package/dist/cli/upgrade.d.ts +15 -0
- package/dist/cli/upgrade.js +232 -335
- package/dist/config-BOtJQ7Pc.js +941 -0
- package/dist/context-B45_WqVX.js +298 -0
- package/dist/conversation-privacy-BZRA6Pes.js +182 -0
- package/dist/conversation-work-C6y3T5Hx.js +51 -0
- package/dist/credential-sync-Bep34IB_.js +2661 -0
- package/dist/db-BeyXKJOI.d.ts +5 -0
- package/dist/destinations-BeBiJeEM.js +29 -0
- package/dist/execution-stats-B6lgWxCK.js +373 -0
- package/dist/executor-BlUhFL7_.js +721 -0
- package/dist/experimental-CIXf7Uye.js +46 -0
- package/dist/history-ClE4toxe.d.ts +32 -0
- package/dist/history-CqTuSAdI.js +441 -0
- package/dist/instrumentation.d.ts +3 -3
- package/dist/instrumentation.js +130 -176
- package/dist/job-queue-DLMqIK74.js +63 -0
- package/dist/logging-BMJzdDlP.js +1210 -0
- package/dist/model-profile-CZmVoGdX.js +49 -0
- package/dist/nitro.d.ts +31 -36
- package/dist/nitro.js +275 -456
- package/dist/oauth-callback-server-WwG5_Zgm.js +130 -0
- package/dist/oauth-relay-CdCnu80W.js +62 -0
- package/dist/package-discovery-7a9YqLhE.js +455 -0
- package/dist/pending-auth-CUbXq06G.js +185 -0
- package/dist/plugin-module-BKGOXca2.js +81 -0
- package/dist/plugins-BI9A1vKA.d.ts +95 -0
- package/dist/plugins-egKWjgue.js +99 -0
- package/dist/private-trace-result-BLvBjy_e.js +16 -0
- package/dist/profile-T0tXE4zu.js +861 -0
- package/dist/registry-BA0__8ai.js +1419 -0
- package/dist/reporting-schema-DosTN__Q.js +105 -0
- package/dist/resolve-bvD1ORdv.js +546 -0
- package/dist/rolldown-runtime-B4FMCO8f.js +28 -0
- package/dist/runner-BXYxfIUL.js +392 -0
- package/dist/sandbox-egress-signals-ngEPjSEW.js +46 -0
- package/dist/schema-Rn8-XEaN.js +1140 -0
- package/dist/sentry-CD3SdJl-.js +26 -0
- package/dist/skills-DJOlYdBs.js +316 -0
- package/dist/spawn-h0R1bTEo.js +29 -0
- package/dist/stats-3YZn-Swi.js +2648 -0
- package/dist/store-C5qxF2ac.d.ts +208 -0
- package/dist/task-queue-CoXwd2eq.js +136 -0
- package/dist/task-runner-B2_mJZj0.js +639 -0
- package/dist/thread-state-CqZ1m-es.js +431 -0
- package/dist/turn-execution-fI6PZ0iI.js +47 -0
- package/dist/turn-lifecycle-CcnwrOcp.js +54 -0
- package/dist/usage-B5SfMb3H.js +296 -0
- package/dist/usage-schema-CKaDkH_j.js +20 -0
- package/dist/validation-DfWcvhuS.js +40 -0
- package/dist/vercel-queue-DIkVcfK4.js +167 -0
- package/dist/vercel.d.ts +6 -6
- package/dist/vercel.js +14 -6
- package/dist/version-CnXZ614E.d.ts +5 -0
- package/dist/version-oD-oPrbO.js +8 -0
- package/dist/version.d.ts +2 -4
- package/dist/version.js +2 -6
- package/dist/web-authorization-Crbzlmzo.js +76 -0
- package/dist/web-input-BOuq3oKK.js +494 -0
- package/dist/work-B91LQKHr.js +509 -0
- package/dist/worker-BcRRTe6k.js +625 -0
- package/package.json +9 -9
- package/dist/agent-hooks-GB2VPTNA.js +0 -51
- package/dist/catalog-runtime-UBLIWD3O.js +0 -14
- package/dist/chunk-2LYHWMR4.js +0 -46
- package/dist/chunk-352OWAZH.js +0 -320
- package/dist/chunk-3Y47VMHV.js +0 -6
- package/dist/chunk-3YJSKOWF.js +0 -164
- package/dist/chunk-4KSSKTQ5.js +0 -229
- package/dist/chunk-4UO6FK4G.js +0 -64
- package/dist/chunk-5AHX53NQ.js +0 -717
- package/dist/chunk-5J3E5FWA.js +0 -700
- package/dist/chunk-5PDAG6O3.js +0 -206
- package/dist/chunk-6Y5B5PEX.js +0 -80
- package/dist/chunk-7GWQ5S5R.js +0 -2086
- package/dist/chunk-7H2VZTWN.js +0 -103
- package/dist/chunk-7NL7T6VU.js +0 -406
- package/dist/chunk-A5L27LE4.js +0 -387
- package/dist/chunk-ADIAWL34.js +0 -81
- package/dist/chunk-ASTEMBKJ.js +0 -519
- package/dist/chunk-CEMMLV5L.js +0 -98
- package/dist/chunk-CM5HNV2W.js +0 -131
- package/dist/chunk-CRTRBBRI.js +0 -105
- package/dist/chunk-DA2KGZZI.js +0 -13
- package/dist/chunk-DIB4GX3G.js +0 -8
- package/dist/chunk-DKIJ2RP5.js +0 -68
- package/dist/chunk-DWPN6SCH.js +0 -172
- package/dist/chunk-EDKUFGPL.js +0 -64
- package/dist/chunk-ENY6SKIU.js +0 -1228
- package/dist/chunk-F4MGCZOX.js +0 -2943
- package/dist/chunk-FA6POGLH.js +0 -70
- package/dist/chunk-FSUR7GTG.js +0 -20475
- package/dist/chunk-GKEOHJL5.js +0 -1856
- package/dist/chunk-GMCB6W5Y.js +0 -333
- package/dist/chunk-IE2VPG7O.js +0 -1135
- package/dist/chunk-IZ5B5IHQ.js +0 -3656
- package/dist/chunk-JAL3CH4G.js +0 -27
- package/dist/chunk-KIIEUXD3.js +0 -1764
- package/dist/chunk-KXU4NIL7.js +0 -692
- package/dist/chunk-MTM5IFCY.js +0 -1190
- package/dist/chunk-N7TMSOZH.js +0 -235
- package/dist/chunk-NCEBK6PH.js +0 -36
- package/dist/chunk-NHJU2ZAL.js +0 -56
- package/dist/chunk-P6KTRNVC.js +0 -321
- package/dist/chunk-PDO5BLNM.js +0 -22
- package/dist/chunk-PDRNLLFT.js +0 -3295
- package/dist/chunk-PEQXYSSE.js +0 -45
- package/dist/chunk-PKEX2AQX.js +0 -198
- package/dist/chunk-PSJCGIGO.js +0 -1163
- package/dist/chunk-PZUSUL55.js +0 -506
- package/dist/chunk-R45M4TUS.js +0 -37
- package/dist/chunk-RC745GGZ.js +0 -122
- package/dist/chunk-SP3QKZXJ.js +0 -820
- package/dist/chunk-TXKUKMVO.js +0 -417
- package/dist/chunk-UE4GPWSD.js +0 -931
- package/dist/chunk-UIE3R5XU.js +0 -17
- package/dist/chunk-UXPG6ZIN.js +0 -84
- package/dist/chunk-VFUK3X5B.js +0 -50
- package/dist/chunk-VNMJWREK.js +0 -787
- package/dist/chunk-VZ7CSOX2.js +0 -706
- package/dist/chunk-WCNQJKFT.js +0 -10
- package/dist/chunk-WDZR3QQM.js +0 -135
- package/dist/chunk-WLYVJCHV.js +0 -1433
- package/dist/chunk-WS7NAFTM.js +0 -1520
- package/dist/chunk-X6GDDWQH.js +0 -60
- package/dist/chunk-XPDEYNQI.js +0 -102
- package/dist/chunk-XRW44VGM.js +0 -58
- package/dist/chunk-Y2CM7HXH.js +0 -111
- package/dist/chunk-Y7X25LFY.js +0 -15
- package/dist/conversation-work-VIOQ5ZF3.js +0 -54
- package/dist/db-NGDJV7YH.js +0 -26
- package/dist/discovery-DWVDM7UK.js +0 -44
- package/dist/experimental-P5FZM4CU.js +0 -12
- package/dist/oauth-callback-server-YFY7YEGK.js +0 -243
- package/dist/oauth-relay-5XPVRPYB.js +0 -8
- package/dist/plugins-YTYJMMJF.js +0 -14
- package/dist/runner-ORTNSB2Q.js +0 -522
- package/dist/sandbox-egress-signals-FIL7EGPR.js +0 -73
- package/dist/sentry-MB3NL3IR.js +0 -42
- package/dist/skills-H376WQOH.js +0 -27
- package/dist/spawn-T43WHLOS.js +0 -42
- package/dist/task-runner-LBZWF32V.js +0 -36
- package/dist/validation-REJLRKI6.js +0 -17
- package/dist/work-3KJ3RE76.js +0 -51
- package/dist/worker-QFEBU72E.js +0 -24
package/dist/chunk-PSJCGIGO.js
DELETED
|
@@ -1,1163 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
resolveBaseUrl
|
|
3
|
-
} from "./chunk-UE4GPWSD.js";
|
|
4
|
-
import {
|
|
5
|
-
createPluginLogger,
|
|
6
|
-
getPlugins,
|
|
7
|
-
getSlackClient,
|
|
8
|
-
withSlackRetries
|
|
9
|
-
} from "./chunk-IZ5B5IHQ.js";
|
|
10
|
-
import {
|
|
11
|
-
getDb,
|
|
12
|
-
parseDestination
|
|
13
|
-
} from "./chunk-GKEOHJL5.js";
|
|
14
|
-
import {
|
|
15
|
-
sleep
|
|
16
|
-
} from "./chunk-5AHX53NQ.js";
|
|
17
|
-
import {
|
|
18
|
-
acquireActiveLock,
|
|
19
|
-
getStateAdapter
|
|
20
|
-
} from "./chunk-P6KTRNVC.js";
|
|
21
|
-
import {
|
|
22
|
-
pluginCatalogRuntime
|
|
23
|
-
} from "./chunk-WCNQJKFT.js";
|
|
24
|
-
import {
|
|
25
|
-
fetchWithBoundedOAuthErrorBodies
|
|
26
|
-
} from "./chunk-7GWQ5S5R.js";
|
|
27
|
-
import {
|
|
28
|
-
isRecord
|
|
29
|
-
} from "./chunk-WS7NAFTM.js";
|
|
30
|
-
|
|
31
|
-
// src/chat/capabilities/router.ts
|
|
32
|
-
var ProviderCredentialRouter = class {
|
|
33
|
-
brokersByProvider;
|
|
34
|
-
constructor(input) {
|
|
35
|
-
this.brokersByProvider = input.brokersByProvider;
|
|
36
|
-
}
|
|
37
|
-
async issue(input) {
|
|
38
|
-
const broker = this.brokersByProvider[input.provider];
|
|
39
|
-
if (!broker) {
|
|
40
|
-
throw new Error(
|
|
41
|
-
`No credential broker registered for provider: ${input.provider}`
|
|
42
|
-
);
|
|
43
|
-
}
|
|
44
|
-
return await broker.issue({
|
|
45
|
-
context: input.context,
|
|
46
|
-
reason: input.reason
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
// src/chat/credentials/user-token-store.ts
|
|
52
|
-
import {
|
|
53
|
-
pluginStoredTokensSchema
|
|
54
|
-
} from "@sentry/junior-plugin-api";
|
|
55
|
-
var storedTokensSchema = pluginStoredTokensSchema;
|
|
56
|
-
|
|
57
|
-
// src/chat/credentials/state-adapter-token-store.ts
|
|
58
|
-
var KEY_PREFIX = "oauth-token";
|
|
59
|
-
var BUFFER_MS = 24 * 60 * 60 * 1e3;
|
|
60
|
-
var LONG_LIVED_TTL_MS = 365 * 24 * 60 * 60 * 1e3;
|
|
61
|
-
var REFRESH_LOCK_WAIT_MS = 3e4;
|
|
62
|
-
var REFRESH_LOCK_RETRY_MS = 100;
|
|
63
|
-
function tokenKey(userId, provider) {
|
|
64
|
-
return `${KEY_PREFIX}:${userId}:${provider}`;
|
|
65
|
-
}
|
|
66
|
-
function refreshLockKey(userId, provider) {
|
|
67
|
-
return `${tokenKey(userId, provider)}:refresh`;
|
|
68
|
-
}
|
|
69
|
-
var StateAdapterTokenStore = class {
|
|
70
|
-
state;
|
|
71
|
-
constructor(stateAdapter) {
|
|
72
|
-
this.state = stateAdapter;
|
|
73
|
-
}
|
|
74
|
-
async get(userId, provider) {
|
|
75
|
-
const stored = await this.state.get(tokenKey(userId, provider));
|
|
76
|
-
return stored === null || stored === void 0 ? void 0 : storedTokensSchema.parse(stored);
|
|
77
|
-
}
|
|
78
|
-
async set(userId, provider, tokens) {
|
|
79
|
-
const parsed = storedTokensSchema.parse(tokens);
|
|
80
|
-
const expiresAt = parsed.refreshTokenExpiresAt ?? parsed.expiresAt;
|
|
81
|
-
const ttlMs = expiresAt ? Math.max(expiresAt - Date.now() + BUFFER_MS, BUFFER_MS) : LONG_LIVED_TTL_MS;
|
|
82
|
-
await this.state.set(tokenKey(userId, provider), parsed, ttlMs);
|
|
83
|
-
}
|
|
84
|
-
async delete(userId, provider) {
|
|
85
|
-
await this.state.delete(tokenKey(userId, provider));
|
|
86
|
-
}
|
|
87
|
-
/** Wait for the per-slot refresh gate so rotated refresh tokens are used once. */
|
|
88
|
-
async withRefresh(userId, provider, callback) {
|
|
89
|
-
const lockKey = refreshLockKey(userId, provider);
|
|
90
|
-
const deadline = Date.now() + REFRESH_LOCK_WAIT_MS;
|
|
91
|
-
while (true) {
|
|
92
|
-
const lock = await acquireActiveLock(this.state, lockKey);
|
|
93
|
-
if (lock) {
|
|
94
|
-
try {
|
|
95
|
-
return await callback();
|
|
96
|
-
} finally {
|
|
97
|
-
await this.state.releaseLock(lock);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
if (Date.now() >= deadline) {
|
|
101
|
-
throw new Error(`Could not acquire OAuth token refresh lock`);
|
|
102
|
-
}
|
|
103
|
-
await sleep(REFRESH_LOCK_RETRY_MS);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
// src/chat/capabilities/factory.ts
|
|
109
|
-
var sandboxEgressRouters = /* @__PURE__ */ new WeakMap();
|
|
110
|
-
function createUserTokenStore() {
|
|
111
|
-
return new StateAdapterTokenStore(getStateAdapter());
|
|
112
|
-
}
|
|
113
|
-
function createProviderCredentialRouter(userTokenStore) {
|
|
114
|
-
const brokersByProvider = {};
|
|
115
|
-
for (const plugin of pluginCatalogRuntime.getProviders()) {
|
|
116
|
-
const { name } = plugin.manifest;
|
|
117
|
-
if (!plugin.manifest.credentials && !plugin.manifest.apiHeaders) {
|
|
118
|
-
continue;
|
|
119
|
-
}
|
|
120
|
-
brokersByProvider[name] = pluginCatalogRuntime.createBroker(name, {
|
|
121
|
-
userTokenStore
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
return new ProviderCredentialRouter({ brokersByProvider });
|
|
125
|
-
}
|
|
126
|
-
function getSandboxEgressRouter() {
|
|
127
|
-
const stateAdapter = getStateAdapter();
|
|
128
|
-
let router = sandboxEgressRouters.get(stateAdapter);
|
|
129
|
-
if (!router) {
|
|
130
|
-
router = createProviderCredentialRouter(
|
|
131
|
-
new StateAdapterTokenStore(stateAdapter)
|
|
132
|
-
);
|
|
133
|
-
sandboxEgressRouters.set(stateAdapter, router);
|
|
134
|
-
}
|
|
135
|
-
return router;
|
|
136
|
-
}
|
|
137
|
-
async function issueProviderCredentialLease(input) {
|
|
138
|
-
return await getSandboxEgressRouter().issue(input);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// src/chat/mcp/errors.ts
|
|
142
|
-
var McpProviderError = class extends Error {
|
|
143
|
-
phase;
|
|
144
|
-
provider;
|
|
145
|
-
missingSession;
|
|
146
|
-
resourceHost;
|
|
147
|
-
status;
|
|
148
|
-
constructor(details) {
|
|
149
|
-
super(`MCP provider ${details.phase.replace("_", " ")} failed`);
|
|
150
|
-
this.name = "McpProviderError";
|
|
151
|
-
this.phase = details.phase;
|
|
152
|
-
this.provider = details.provider;
|
|
153
|
-
this.missingSession = details.missingSession;
|
|
154
|
-
this.resourceHost = details.resourceHost;
|
|
155
|
-
this.status = details.status;
|
|
156
|
-
}
|
|
157
|
-
};
|
|
158
|
-
function providerStatus(error) {
|
|
159
|
-
if (!error || typeof error !== "object") {
|
|
160
|
-
return void 0;
|
|
161
|
-
}
|
|
162
|
-
const value = error;
|
|
163
|
-
const status = typeof value.status === "number" ? value.status : typeof value.code === "number" ? value.code : void 0;
|
|
164
|
-
return status !== void 0 && status >= 100 && status <= 599 ? status : void 0;
|
|
165
|
-
}
|
|
166
|
-
function toMcpProviderError(error, details) {
|
|
167
|
-
if (error instanceof McpProviderError) {
|
|
168
|
-
return error;
|
|
169
|
-
}
|
|
170
|
-
return new McpProviderError({
|
|
171
|
-
...details,
|
|
172
|
-
status: details.status ?? providerStatus(error)
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
function getMcpProviderErrorAttributes(error) {
|
|
176
|
-
if (!(error instanceof McpProviderError)) {
|
|
177
|
-
return {};
|
|
178
|
-
}
|
|
179
|
-
return {
|
|
180
|
-
"app.credential.provider": error.provider,
|
|
181
|
-
"app.mcp.error.phase": error.phase,
|
|
182
|
-
...error.status !== void 0 ? { "http.response.status_code": error.status } : void 0,
|
|
183
|
-
...error.resourceHost ? { "server.address": error.resourceHost } : void 0
|
|
184
|
-
};
|
|
185
|
-
}
|
|
186
|
-
var McpToolError = class extends Error {
|
|
187
|
-
constructor(message) {
|
|
188
|
-
super(message);
|
|
189
|
-
this.name = "McpToolError";
|
|
190
|
-
}
|
|
191
|
-
};
|
|
192
|
-
function getMcpAwareErrorType(error, fallback) {
|
|
193
|
-
if (error instanceof McpToolError) {
|
|
194
|
-
return "tool_error";
|
|
195
|
-
}
|
|
196
|
-
if (error instanceof McpProviderError) {
|
|
197
|
-
return "mcp_provider_error";
|
|
198
|
-
}
|
|
199
|
-
return error instanceof Error ? error.name : fallback;
|
|
200
|
-
}
|
|
201
|
-
function getMcpAwareErrorMessage(error) {
|
|
202
|
-
return error instanceof Error ? error.message : String(error);
|
|
203
|
-
}
|
|
204
|
-
function getMcpAwareTelemetryMessage(error, _privacy) {
|
|
205
|
-
if (error instanceof McpToolError) {
|
|
206
|
-
return "MCP tool call failed";
|
|
207
|
-
}
|
|
208
|
-
if (error instanceof McpProviderError) {
|
|
209
|
-
return error.message;
|
|
210
|
-
}
|
|
211
|
-
return getMcpAwareErrorMessage(error);
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// src/chat/slack/users.ts
|
|
215
|
-
function normalizeUser(raw) {
|
|
216
|
-
const rawFields = raw.profile?.fields;
|
|
217
|
-
const profileFields = [];
|
|
218
|
-
if (rawFields && typeof rawFields === "object") {
|
|
219
|
-
for (const [id, field] of Object.entries(rawFields)) {
|
|
220
|
-
if (!field) continue;
|
|
221
|
-
profileFields.push({
|
|
222
|
-
id,
|
|
223
|
-
label: field.label || void 0,
|
|
224
|
-
value: field.value || void 0,
|
|
225
|
-
alt: field.alt || void 0
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
return {
|
|
230
|
-
id: raw.id ?? "",
|
|
231
|
-
team_id: raw.team_id || void 0,
|
|
232
|
-
name: raw.name || void 0,
|
|
233
|
-
real_name: raw.real_name || raw.profile?.real_name || void 0,
|
|
234
|
-
display_name: raw.profile?.display_name || void 0,
|
|
235
|
-
title: raw.profile?.title || void 0,
|
|
236
|
-
email: raw.profile?.email || void 0,
|
|
237
|
-
status_text: raw.profile?.status_text ?? void 0,
|
|
238
|
-
status_emoji: raw.profile?.status_emoji ?? void 0,
|
|
239
|
-
is_bot: raw.is_bot ?? false,
|
|
240
|
-
is_deleted: raw.deleted ?? false,
|
|
241
|
-
timezone: raw.tz || void 0,
|
|
242
|
-
...profileFields.length > 0 ? { profile_fields: profileFields } : void 0
|
|
243
|
-
};
|
|
244
|
-
}
|
|
245
|
-
function normalizeNameTokens(value) {
|
|
246
|
-
return value.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter(Boolean);
|
|
247
|
-
}
|
|
248
|
-
function nameFields(user) {
|
|
249
|
-
return [
|
|
250
|
-
user.name ?? "",
|
|
251
|
-
user.profile?.display_name ?? "",
|
|
252
|
-
user.real_name ?? user.profile?.real_name ?? ""
|
|
253
|
-
].map((value) => value.toLowerCase().trim()).filter(Boolean);
|
|
254
|
-
}
|
|
255
|
-
async function lookupSlackUserProfile(userId) {
|
|
256
|
-
const client = getSlackClient();
|
|
257
|
-
const result = await withSlackRetries(
|
|
258
|
-
() => client.users.info({ user: userId }),
|
|
259
|
-
3,
|
|
260
|
-
{ action: "users.info" }
|
|
261
|
-
);
|
|
262
|
-
const user = result.user;
|
|
263
|
-
if (!user) {
|
|
264
|
-
throw new Error(`Slack users.info returned no user for ${userId}`);
|
|
265
|
-
}
|
|
266
|
-
return normalizeUser(user);
|
|
267
|
-
}
|
|
268
|
-
async function lookupSlackUserByEmail(email) {
|
|
269
|
-
const client = getSlackClient();
|
|
270
|
-
let result;
|
|
271
|
-
try {
|
|
272
|
-
result = await withSlackRetries(
|
|
273
|
-
() => client.users.lookupByEmail({ email }),
|
|
274
|
-
3,
|
|
275
|
-
{ action: "users.lookupByEmail" }
|
|
276
|
-
);
|
|
277
|
-
} catch (error) {
|
|
278
|
-
const apiError = error.apiError;
|
|
279
|
-
if (apiError === "users_not_found") {
|
|
280
|
-
return null;
|
|
281
|
-
}
|
|
282
|
-
throw error;
|
|
283
|
-
}
|
|
284
|
-
const user = result.user;
|
|
285
|
-
if (!user) {
|
|
286
|
-
return null;
|
|
287
|
-
}
|
|
288
|
-
return normalizeUser(user);
|
|
289
|
-
}
|
|
290
|
-
function scoreNameMatch(user, query) {
|
|
291
|
-
const queryLower = query.toLowerCase().trim();
|
|
292
|
-
if (!queryLower) return 0;
|
|
293
|
-
const fields = nameFields(user);
|
|
294
|
-
if (fields.length === 0) return 0;
|
|
295
|
-
let best = 0;
|
|
296
|
-
const queryTokens = normalizeNameTokens(queryLower);
|
|
297
|
-
for (const field of fields) {
|
|
298
|
-
if (field === queryLower) {
|
|
299
|
-
best = Math.max(best, 100);
|
|
300
|
-
continue;
|
|
301
|
-
}
|
|
302
|
-
if (field.startsWith(queryLower)) {
|
|
303
|
-
best = Math.max(best, 70);
|
|
304
|
-
} else if (field.includes(queryLower)) {
|
|
305
|
-
best = Math.max(best, 25);
|
|
306
|
-
}
|
|
307
|
-
const tokens = normalizeNameTokens(field);
|
|
308
|
-
for (const token of tokens) {
|
|
309
|
-
if (token === queryLower) {
|
|
310
|
-
best = Math.max(best, 85);
|
|
311
|
-
} else if (token.startsWith(queryLower)) {
|
|
312
|
-
best = Math.max(best, 60);
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
if (queryTokens.length > 1 && queryTokens.every(
|
|
316
|
-
(queryToken) => tokens.some(
|
|
317
|
-
(token) => token === queryToken || token.startsWith(queryToken)
|
|
318
|
-
)
|
|
319
|
-
)) {
|
|
320
|
-
best = Math.max(best, 95);
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
return best;
|
|
324
|
-
}
|
|
325
|
-
function compareNameMatches(left, right) {
|
|
326
|
-
if (right.score !== left.score) return right.score - left.score;
|
|
327
|
-
return (left.user.name ?? "").localeCompare(right.user.name ?? "");
|
|
328
|
-
}
|
|
329
|
-
async function listWorkspaceUsers(options) {
|
|
330
|
-
const client = getSlackClient();
|
|
331
|
-
const matches = [];
|
|
332
|
-
let cursor;
|
|
333
|
-
let pages = 0;
|
|
334
|
-
let totalScanned = 0;
|
|
335
|
-
while (pages < options.maxPages) {
|
|
336
|
-
pages++;
|
|
337
|
-
const result = await withSlackRetries(
|
|
338
|
-
() => client.users.list({
|
|
339
|
-
limit: 200,
|
|
340
|
-
...cursor ? { cursor } : void 0
|
|
341
|
-
}),
|
|
342
|
-
3,
|
|
343
|
-
{ action: "users.list" }
|
|
344
|
-
);
|
|
345
|
-
const members = result.members ?? [];
|
|
346
|
-
totalScanned += members.length;
|
|
347
|
-
for (const member of members) {
|
|
348
|
-
if (!options.includeDeleted && member.deleted) continue;
|
|
349
|
-
if (!options.includeBots && member.is_bot) continue;
|
|
350
|
-
if (member.id === "USLACKBOT") continue;
|
|
351
|
-
const score = options.score(member);
|
|
352
|
-
if (score <= 0) continue;
|
|
353
|
-
matches.push({ user: member, score });
|
|
354
|
-
}
|
|
355
|
-
const nextCursor = result.response_metadata?.next_cursor;
|
|
356
|
-
if (!nextCursor) {
|
|
357
|
-
cursor = void 0;
|
|
358
|
-
break;
|
|
359
|
-
}
|
|
360
|
-
cursor = nextCursor;
|
|
361
|
-
}
|
|
362
|
-
return {
|
|
363
|
-
matches,
|
|
364
|
-
searched_pages: pages,
|
|
365
|
-
searched_user_count: totalScanned,
|
|
366
|
-
// True only when we hit the page cap with more data remaining.
|
|
367
|
-
truncated: pages >= options.maxPages && Boolean(cursor)
|
|
368
|
-
};
|
|
369
|
-
}
|
|
370
|
-
async function searchSlackUsers(options) {
|
|
371
|
-
const {
|
|
372
|
-
query,
|
|
373
|
-
limit = 10,
|
|
374
|
-
maxPages = 3,
|
|
375
|
-
includeDeleted = false,
|
|
376
|
-
includeBots = false
|
|
377
|
-
} = options;
|
|
378
|
-
const queryLower = query.toLowerCase().trim();
|
|
379
|
-
const listed = await listWorkspaceUsers({
|
|
380
|
-
maxPages,
|
|
381
|
-
includeDeleted,
|
|
382
|
-
includeBots,
|
|
383
|
-
score: (member) => scoreNameMatch(member, queryLower)
|
|
384
|
-
});
|
|
385
|
-
listed.matches.sort(compareNameMatches);
|
|
386
|
-
return {
|
|
387
|
-
users: listed.matches.slice(0, limit).map((m) => normalizeUser(m.user)),
|
|
388
|
-
searched_pages: listed.searched_pages,
|
|
389
|
-
searched_user_count: listed.searched_user_count,
|
|
390
|
-
truncated: listed.truncated
|
|
391
|
-
};
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
// src/chat/plugins/credential-hooks.ts
|
|
395
|
-
import {
|
|
396
|
-
pluginAuthorizationSchema,
|
|
397
|
-
pluginCredentialResultSchema,
|
|
398
|
-
pluginGrantSchema,
|
|
399
|
-
pluginProviderAccountSchema
|
|
400
|
-
} from "@sentry/junior-plugin-api";
|
|
401
|
-
function parseSchema(schema, value, message) {
|
|
402
|
-
const result = schema.safeParse(value);
|
|
403
|
-
if (!result.success) {
|
|
404
|
-
throw new Error(message);
|
|
405
|
-
}
|
|
406
|
-
return result.data;
|
|
407
|
-
}
|
|
408
|
-
function parseAuthorization(value, pluginName) {
|
|
409
|
-
if (value === void 0) {
|
|
410
|
-
return void 0;
|
|
411
|
-
}
|
|
412
|
-
const authorization = parseSchema(
|
|
413
|
-
pluginAuthorizationSchema,
|
|
414
|
-
value,
|
|
415
|
-
`Plugin "${pluginName}" grant authorization is invalid`
|
|
416
|
-
);
|
|
417
|
-
if (authorization.provider !== pluginName) {
|
|
418
|
-
throw new Error(
|
|
419
|
-
`Plugin "${pluginName}" grant authorization provider must match the issuing plugin`
|
|
420
|
-
);
|
|
421
|
-
}
|
|
422
|
-
return authorization;
|
|
423
|
-
}
|
|
424
|
-
function parseGrant(value, pluginName) {
|
|
425
|
-
return parseSchema(
|
|
426
|
-
pluginGrantSchema,
|
|
427
|
-
value,
|
|
428
|
-
`Plugin "${pluginName}" grantForEgress returned an invalid grant`
|
|
429
|
-
);
|
|
430
|
-
}
|
|
431
|
-
function pluginFor(provider) {
|
|
432
|
-
return getPlugins().find((candidate) => candidate.manifest.name === provider);
|
|
433
|
-
}
|
|
434
|
-
function basePluginContext(plugin) {
|
|
435
|
-
const pluginName = plugin.manifest.name;
|
|
436
|
-
return {
|
|
437
|
-
plugin: { name: pluginName },
|
|
438
|
-
log: createPluginLogger(pluginName),
|
|
439
|
-
db: getDb()
|
|
440
|
-
};
|
|
441
|
-
}
|
|
442
|
-
function parseCredentialResult(value, pluginName) {
|
|
443
|
-
const result = parseSchema(
|
|
444
|
-
pluginCredentialResultSchema,
|
|
445
|
-
value,
|
|
446
|
-
`Plugin "${pluginName}" issueCredential result is invalid`
|
|
447
|
-
);
|
|
448
|
-
if (result.type === "lease") {
|
|
449
|
-
parseAuthorization(result.lease.authorization, pluginName);
|
|
450
|
-
return result;
|
|
451
|
-
}
|
|
452
|
-
if (result.type === "unavailable") {
|
|
453
|
-
return result;
|
|
454
|
-
}
|
|
455
|
-
parseAuthorization(result.authorization, pluginName);
|
|
456
|
-
return result;
|
|
457
|
-
}
|
|
458
|
-
async function selectPluginGrant(input) {
|
|
459
|
-
const plugin = pluginFor(input.provider);
|
|
460
|
-
const hook = plugin?.hooks?.grantForEgress;
|
|
461
|
-
if (!plugin || !hook) {
|
|
462
|
-
return void 0;
|
|
463
|
-
}
|
|
464
|
-
const result = await hook({
|
|
465
|
-
...basePluginContext(plugin),
|
|
466
|
-
request: {
|
|
467
|
-
...input.bodyText !== void 0 ? { bodyText: input.bodyText } : void 0,
|
|
468
|
-
method: input.method,
|
|
469
|
-
...input.operation ? { operation: input.operation } : void 0,
|
|
470
|
-
url: input.upstreamUrl.toString()
|
|
471
|
-
}
|
|
472
|
-
});
|
|
473
|
-
return result === void 0 ? void 0 : parseGrant(result, plugin.manifest.name);
|
|
474
|
-
}
|
|
475
|
-
async function onPluginEgressResponse(input) {
|
|
476
|
-
const plugin = pluginFor(input.provider);
|
|
477
|
-
const hook = plugin?.hooks?.onEgressResponse;
|
|
478
|
-
if (!plugin || !hook) {
|
|
479
|
-
return {};
|
|
480
|
-
}
|
|
481
|
-
let permissionDenied;
|
|
482
|
-
await hook({
|
|
483
|
-
...basePluginContext(plugin),
|
|
484
|
-
grant: input.grant,
|
|
485
|
-
permissionDenied(message) {
|
|
486
|
-
const trimmed = message.trim();
|
|
487
|
-
if (!trimmed) {
|
|
488
|
-
throw new Error(
|
|
489
|
-
`Plugin "${plugin.manifest.name}" onEgressResponse permissionDenied message is empty`
|
|
490
|
-
);
|
|
491
|
-
}
|
|
492
|
-
permissionDenied = { message: trimmed };
|
|
493
|
-
},
|
|
494
|
-
request: {
|
|
495
|
-
method: input.method,
|
|
496
|
-
...input.operation ? { operation: input.operation } : void 0,
|
|
497
|
-
url: input.upstreamUrl.toString()
|
|
498
|
-
},
|
|
499
|
-
response: input.response
|
|
500
|
-
});
|
|
501
|
-
return permissionDenied ? { permissionDenied } : {};
|
|
502
|
-
}
|
|
503
|
-
function hasEgressCredentialHooks(provider) {
|
|
504
|
-
const hooks = pluginFor(provider)?.hooks;
|
|
505
|
-
return Boolean(hooks?.grantForEgress || hooks?.issueCredential);
|
|
506
|
-
}
|
|
507
|
-
function getOAuthAccountProviders() {
|
|
508
|
-
return getPlugins().filter((plugin) => Boolean(plugin.hooks?.resolveOAuthAccount)).map((plugin) => plugin.manifest.name);
|
|
509
|
-
}
|
|
510
|
-
async function resolvePluginOAuthAccount(input) {
|
|
511
|
-
const plugin = pluginFor(input.provider);
|
|
512
|
-
const hook = plugin?.hooks?.resolveOAuthAccount;
|
|
513
|
-
if (!plugin || !hook) {
|
|
514
|
-
return void 0;
|
|
515
|
-
}
|
|
516
|
-
const account = await hook({
|
|
517
|
-
...basePluginContext(plugin),
|
|
518
|
-
tokens: input.tokens
|
|
519
|
-
});
|
|
520
|
-
return account === void 0 ? void 0 : parseSchema(
|
|
521
|
-
pluginProviderAccountSchema,
|
|
522
|
-
account,
|
|
523
|
-
`Plugin "${plugin.manifest.name}" resolveOAuthAccount returned an invalid account`
|
|
524
|
-
);
|
|
525
|
-
}
|
|
526
|
-
async function issuePluginCredential(input) {
|
|
527
|
-
const plugin = pluginFor(input.provider);
|
|
528
|
-
const hook = plugin?.hooks?.issueCredential;
|
|
529
|
-
if (!plugin || !hook) {
|
|
530
|
-
throw new Error(`Plugin "${input.provider}" has no issueCredential hook`);
|
|
531
|
-
}
|
|
532
|
-
const currentUserId = "type" in input.actor ? input.actor.userId : void 0;
|
|
533
|
-
const credentialSubjectUserId = input.credentialSubject?.userId;
|
|
534
|
-
const result = await hook({
|
|
535
|
-
...basePluginContext(plugin),
|
|
536
|
-
actor: input.actor,
|
|
537
|
-
grant: input.grant,
|
|
538
|
-
...input.credentialSubject ? { credentialSubject: input.credentialSubject } : void 0,
|
|
539
|
-
tokens: {
|
|
540
|
-
...currentUserId ? {
|
|
541
|
-
currentUser: {
|
|
542
|
-
userId: currentUserId,
|
|
543
|
-
get: async () => await input.userTokenStore.get(
|
|
544
|
-
currentUserId,
|
|
545
|
-
plugin.manifest.name
|
|
546
|
-
),
|
|
547
|
-
set: async (tokens) => {
|
|
548
|
-
await input.userTokenStore.set(
|
|
549
|
-
currentUserId,
|
|
550
|
-
plugin.manifest.name,
|
|
551
|
-
tokens
|
|
552
|
-
);
|
|
553
|
-
},
|
|
554
|
-
withRefresh: async (callback) => await input.userTokenStore.withRefresh(
|
|
555
|
-
currentUserId,
|
|
556
|
-
plugin.manifest.name,
|
|
557
|
-
callback
|
|
558
|
-
)
|
|
559
|
-
}
|
|
560
|
-
} : void 0,
|
|
561
|
-
...credentialSubjectUserId ? {
|
|
562
|
-
credentialSubject: {
|
|
563
|
-
userId: credentialSubjectUserId,
|
|
564
|
-
get: async () => await input.userTokenStore.get(
|
|
565
|
-
credentialSubjectUserId,
|
|
566
|
-
plugin.manifest.name
|
|
567
|
-
),
|
|
568
|
-
set: async (tokens) => {
|
|
569
|
-
await input.userTokenStore.set(
|
|
570
|
-
credentialSubjectUserId,
|
|
571
|
-
plugin.manifest.name,
|
|
572
|
-
tokens
|
|
573
|
-
);
|
|
574
|
-
},
|
|
575
|
-
withRefresh: async (callback) => await input.userTokenStore.withRefresh(
|
|
576
|
-
credentialSubjectUserId,
|
|
577
|
-
plugin.manifest.name,
|
|
578
|
-
callback
|
|
579
|
-
)
|
|
580
|
-
}
|
|
581
|
-
} : void 0
|
|
582
|
-
}
|
|
583
|
-
});
|
|
584
|
-
return parseCredentialResult(result, plugin.manifest.name);
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
// src/chat/mcp/auth-store.ts
|
|
588
|
-
import {
|
|
589
|
-
sourceSchema
|
|
590
|
-
} from "@sentry/junior-plugin-api";
|
|
591
|
-
var MCP_AUTH_SESSION_PREFIX = "junior:mcp_oauth_attempt:v2";
|
|
592
|
-
var MCP_AUTH_CREDENTIALS_PREFIX = "junior:mcp_auth_credentials";
|
|
593
|
-
var MCP_AUTH_SESSION_INDEX_PREFIX = "junior:mcp_oauth_attempt_index:v2";
|
|
594
|
-
var MCP_SERVER_SESSION_PREFIX = "junior:mcp_server_session";
|
|
595
|
-
var MCP_AUTH_SESSION_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
596
|
-
var MCP_AUTH_CREDENTIALS_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
597
|
-
var MCP_SERVER_SESSION_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
598
|
-
function sessionKey(authSessionId) {
|
|
599
|
-
return `${MCP_AUTH_SESSION_PREFIX}:${authSessionId}`;
|
|
600
|
-
}
|
|
601
|
-
function credentialsKey(userId, provider) {
|
|
602
|
-
return `${MCP_AUTH_CREDENTIALS_PREFIX}:${userId}:${provider}`;
|
|
603
|
-
}
|
|
604
|
-
function sessionIndexKey(userId, provider) {
|
|
605
|
-
return `${MCP_AUTH_SESSION_INDEX_PREFIX}:${userId}:${provider}`;
|
|
606
|
-
}
|
|
607
|
-
function serverSessionKey(userId, provider) {
|
|
608
|
-
return `${MCP_SERVER_SESSION_PREFIX}:${userId}:${provider}`;
|
|
609
|
-
}
|
|
610
|
-
function parseSessionIndex(value) {
|
|
611
|
-
if (typeof value !== "string") {
|
|
612
|
-
return [];
|
|
613
|
-
}
|
|
614
|
-
try {
|
|
615
|
-
const parsed = JSON.parse(value);
|
|
616
|
-
if (!Array.isArray(parsed)) {
|
|
617
|
-
return [];
|
|
618
|
-
}
|
|
619
|
-
return [
|
|
620
|
-
...new Set(parsed.filter((id) => typeof id === "string"))
|
|
621
|
-
];
|
|
622
|
-
} catch {
|
|
623
|
-
return [];
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
function parseMcpAuthSession(value) {
|
|
627
|
-
if (typeof value !== "string") {
|
|
628
|
-
return void 0;
|
|
629
|
-
}
|
|
630
|
-
try {
|
|
631
|
-
const parsed = JSON.parse(value);
|
|
632
|
-
if (!isRecord(parsed)) {
|
|
633
|
-
return void 0;
|
|
634
|
-
}
|
|
635
|
-
if (parsed.schemaVersion !== 2 || typeof parsed.authSessionId !== "string" || typeof parsed.provider !== "string" || typeof parsed.userId !== "string" || typeof parsed.conversationId !== "string" || typeof parsed.sessionId !== "string" || typeof parsed.userMessage !== "string" || typeof parsed.createdAtMs !== "number" || typeof parsed.updatedAtMs !== "number") {
|
|
636
|
-
return void 0;
|
|
637
|
-
}
|
|
638
|
-
const destination = parsed.destination === void 0 ? void 0 : parseDestination(parsed.destination);
|
|
639
|
-
if (parsed.destination !== void 0 && !destination) {
|
|
640
|
-
return void 0;
|
|
641
|
-
}
|
|
642
|
-
const source = parsed.source === void 0 ? void 0 : sourceSchema.safeParse(parsed.source);
|
|
643
|
-
if (parsed.source !== void 0 && (!source || !source.success)) {
|
|
644
|
-
return void 0;
|
|
645
|
-
}
|
|
646
|
-
return {
|
|
647
|
-
schemaVersion: 2,
|
|
648
|
-
authSessionId: parsed.authSessionId,
|
|
649
|
-
provider: parsed.provider,
|
|
650
|
-
userId: parsed.userId,
|
|
651
|
-
conversationId: parsed.conversationId,
|
|
652
|
-
...destination ? { destination } : void 0,
|
|
653
|
-
...source?.success ? { source: source.data } : void 0,
|
|
654
|
-
sessionId: parsed.sessionId,
|
|
655
|
-
userMessage: parsed.userMessage,
|
|
656
|
-
createdAtMs: parsed.createdAtMs,
|
|
657
|
-
updatedAtMs: parsed.updatedAtMs,
|
|
658
|
-
...typeof parsed.channelId === "string" ? { channelId: parsed.channelId } : void 0,
|
|
659
|
-
...typeof parsed.threadTs === "string" ? { threadTs: parsed.threadTs } : void 0,
|
|
660
|
-
...typeof parsed.toolChannelId === "string" ? { toolChannelId: parsed.toolChannelId } : void 0,
|
|
661
|
-
...isRecord(parsed.configuration) ? { configuration: parsed.configuration } : void 0,
|
|
662
|
-
...typeof parsed.authorizationUrl === "string" ? { authorizationUrl: parsed.authorizationUrl } : void 0,
|
|
663
|
-
...typeof parsed.codeVerifier === "string" ? { codeVerifier: parsed.codeVerifier } : void 0
|
|
664
|
-
};
|
|
665
|
-
} catch {
|
|
666
|
-
return void 0;
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
function parseServerSession(value) {
|
|
670
|
-
if (typeof value !== "string") {
|
|
671
|
-
return void 0;
|
|
672
|
-
}
|
|
673
|
-
try {
|
|
674
|
-
const parsed = JSON.parse(value);
|
|
675
|
-
if (!isRecord(parsed) || typeof parsed.sessionId !== "string" || typeof parsed.updatedAtMs !== "number") {
|
|
676
|
-
return void 0;
|
|
677
|
-
}
|
|
678
|
-
return {
|
|
679
|
-
sessionId: parsed.sessionId,
|
|
680
|
-
updatedAtMs: parsed.updatedAtMs
|
|
681
|
-
};
|
|
682
|
-
} catch {
|
|
683
|
-
return void 0;
|
|
684
|
-
}
|
|
685
|
-
}
|
|
686
|
-
function parseStoredCredentials(value) {
|
|
687
|
-
if (typeof value !== "string") {
|
|
688
|
-
return void 0;
|
|
689
|
-
}
|
|
690
|
-
try {
|
|
691
|
-
const parsed = JSON.parse(value);
|
|
692
|
-
if (!isRecord(parsed)) {
|
|
693
|
-
return void 0;
|
|
694
|
-
}
|
|
695
|
-
return {
|
|
696
|
-
...isRecord(parsed.clientInformation) ? {
|
|
697
|
-
clientInformation: parsed.clientInformation
|
|
698
|
-
} : void 0,
|
|
699
|
-
...isRecord(parsed.discoveryState) ? {
|
|
700
|
-
// @ts-expect-error non-overlapping boundary cast; rule forbids as-unknown-as chains
|
|
701
|
-
discoveryState: parsed.discoveryState
|
|
702
|
-
} : void 0,
|
|
703
|
-
...isRecord(parsed.tokens) ? { tokens: parsed.tokens } : void 0
|
|
704
|
-
};
|
|
705
|
-
} catch {
|
|
706
|
-
return void 0;
|
|
707
|
-
}
|
|
708
|
-
}
|
|
709
|
-
async function getConnectedStateAdapter() {
|
|
710
|
-
const stateAdapter = getStateAdapter();
|
|
711
|
-
await stateAdapter.connect();
|
|
712
|
-
return stateAdapter;
|
|
713
|
-
}
|
|
714
|
-
async function getMcpAuthSession(authSessionId) {
|
|
715
|
-
const stateAdapter = await getConnectedStateAdapter();
|
|
716
|
-
return parseMcpAuthSession(await stateAdapter.get(sessionKey(authSessionId)));
|
|
717
|
-
}
|
|
718
|
-
async function putMcpAuthSession(session, ttlMs = MCP_AUTH_SESSION_TTL_MS) {
|
|
719
|
-
const stateAdapter = await getConnectedStateAdapter();
|
|
720
|
-
await stateAdapter.set(
|
|
721
|
-
sessionKey(session.authSessionId),
|
|
722
|
-
JSON.stringify(session),
|
|
723
|
-
ttlMs
|
|
724
|
-
);
|
|
725
|
-
const nextIndex = parseSessionIndex(
|
|
726
|
-
await stateAdapter.get(sessionIndexKey(session.userId, session.provider))
|
|
727
|
-
);
|
|
728
|
-
if (!nextIndex.includes(session.authSessionId)) {
|
|
729
|
-
nextIndex.push(session.authSessionId);
|
|
730
|
-
}
|
|
731
|
-
await stateAdapter.set(
|
|
732
|
-
sessionIndexKey(session.userId, session.provider),
|
|
733
|
-
JSON.stringify(nextIndex),
|
|
734
|
-
ttlMs
|
|
735
|
-
);
|
|
736
|
-
}
|
|
737
|
-
async function patchMcpAuthSession(authSessionId, patch) {
|
|
738
|
-
const current = await getMcpAuthSession(authSessionId);
|
|
739
|
-
if (!current) {
|
|
740
|
-
throw new Error(`Unknown MCP auth session: ${authSessionId}`);
|
|
741
|
-
}
|
|
742
|
-
const next = {
|
|
743
|
-
...current,
|
|
744
|
-
...patch,
|
|
745
|
-
schemaVersion: 2,
|
|
746
|
-
authSessionId: current.authSessionId,
|
|
747
|
-
provider: current.provider,
|
|
748
|
-
userId: current.userId,
|
|
749
|
-
conversationId: current.conversationId,
|
|
750
|
-
...current.destination ? { destination: current.destination } : void 0,
|
|
751
|
-
sessionId: current.sessionId,
|
|
752
|
-
userMessage: current.userMessage,
|
|
753
|
-
createdAtMs: current.createdAtMs,
|
|
754
|
-
updatedAtMs: Date.now()
|
|
755
|
-
};
|
|
756
|
-
await putMcpAuthSession(next);
|
|
757
|
-
return next;
|
|
758
|
-
}
|
|
759
|
-
async function deleteMcpAuthSession(authSessionId) {
|
|
760
|
-
const stateAdapter = await getConnectedStateAdapter();
|
|
761
|
-
const current = parseMcpAuthSession(
|
|
762
|
-
await stateAdapter.get(sessionKey(authSessionId))
|
|
763
|
-
);
|
|
764
|
-
await stateAdapter.delete(sessionKey(authSessionId));
|
|
765
|
-
if (!current) {
|
|
766
|
-
return;
|
|
767
|
-
}
|
|
768
|
-
const nextIndex = parseSessionIndex(
|
|
769
|
-
await stateAdapter.get(sessionIndexKey(current.userId, current.provider))
|
|
770
|
-
).filter((id) => id !== authSessionId);
|
|
771
|
-
if (nextIndex.length > 0) {
|
|
772
|
-
await stateAdapter.set(
|
|
773
|
-
sessionIndexKey(current.userId, current.provider),
|
|
774
|
-
JSON.stringify(nextIndex),
|
|
775
|
-
MCP_AUTH_SESSION_TTL_MS
|
|
776
|
-
);
|
|
777
|
-
return;
|
|
778
|
-
}
|
|
779
|
-
await stateAdapter.delete(sessionIndexKey(current.userId, current.provider));
|
|
780
|
-
}
|
|
781
|
-
async function deleteMcpAuthSessionsForUserProvider(userId, provider) {
|
|
782
|
-
const stateAdapter = await getConnectedStateAdapter();
|
|
783
|
-
const indexKey = sessionIndexKey(userId, provider);
|
|
784
|
-
const authSessionIds = parseSessionIndex(await stateAdapter.get(indexKey));
|
|
785
|
-
for (const authSessionId of authSessionIds) {
|
|
786
|
-
await stateAdapter.delete(sessionKey(authSessionId));
|
|
787
|
-
}
|
|
788
|
-
await stateAdapter.delete(indexKey);
|
|
789
|
-
}
|
|
790
|
-
async function getMcpStoredOAuthCredentials(userId, provider) {
|
|
791
|
-
const stateAdapter = await getConnectedStateAdapter();
|
|
792
|
-
return parseStoredCredentials(
|
|
793
|
-
await stateAdapter.get(credentialsKey(userId, provider))
|
|
794
|
-
);
|
|
795
|
-
}
|
|
796
|
-
async function putMcpStoredOAuthCredentials(userId, provider, value, ttlMs = MCP_AUTH_CREDENTIALS_TTL_MS) {
|
|
797
|
-
const stateAdapter = await getConnectedStateAdapter();
|
|
798
|
-
await stateAdapter.set(
|
|
799
|
-
credentialsKey(userId, provider),
|
|
800
|
-
JSON.stringify(value),
|
|
801
|
-
ttlMs
|
|
802
|
-
);
|
|
803
|
-
}
|
|
804
|
-
async function deleteMcpStoredOAuthCredentials(userId, provider) {
|
|
805
|
-
const stateAdapter = await getConnectedStateAdapter();
|
|
806
|
-
await stateAdapter.delete(credentialsKey(userId, provider));
|
|
807
|
-
}
|
|
808
|
-
async function getMcpServerSessionId(userId, provider) {
|
|
809
|
-
const stateAdapter = await getConnectedStateAdapter();
|
|
810
|
-
return parseServerSession(
|
|
811
|
-
await stateAdapter.get(serverSessionKey(userId, provider))
|
|
812
|
-
)?.sessionId;
|
|
813
|
-
}
|
|
814
|
-
async function putMcpServerSessionId(userId, provider, sessionId, ttlMs = MCP_SERVER_SESSION_TTL_MS) {
|
|
815
|
-
const stateAdapter = await getConnectedStateAdapter();
|
|
816
|
-
await stateAdapter.set(
|
|
817
|
-
serverSessionKey(userId, provider),
|
|
818
|
-
JSON.stringify({
|
|
819
|
-
sessionId,
|
|
820
|
-
updatedAtMs: Date.now()
|
|
821
|
-
}),
|
|
822
|
-
ttlMs
|
|
823
|
-
);
|
|
824
|
-
}
|
|
825
|
-
async function deleteMcpServerSessionId(userId, provider) {
|
|
826
|
-
const stateAdapter = await getConnectedStateAdapter();
|
|
827
|
-
await stateAdapter.delete(serverSessionKey(userId, provider));
|
|
828
|
-
}
|
|
829
|
-
|
|
830
|
-
// src/chat/mcp/oauth.ts
|
|
831
|
-
import { randomUUID } from "crypto";
|
|
832
|
-
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
833
|
-
|
|
834
|
-
// src/chat/mcp/oauth-provider.ts
|
|
835
|
-
function createClientMetadata(callbackUrl) {
|
|
836
|
-
return {
|
|
837
|
-
client_name: "Junior MCP Client",
|
|
838
|
-
redirect_uris: [callbackUrl],
|
|
839
|
-
grant_types: ["authorization_code", "refresh_token"],
|
|
840
|
-
response_types: ["code"],
|
|
841
|
-
token_endpoint_auth_method: "none"
|
|
842
|
-
};
|
|
843
|
-
}
|
|
844
|
-
function clientAllowsRedirectUri(clientInformation, callbackUrl) {
|
|
845
|
-
const redirectUris = clientInformation.redirect_uris;
|
|
846
|
-
if (!Array.isArray(redirectUris)) {
|
|
847
|
-
return true;
|
|
848
|
-
}
|
|
849
|
-
return redirectUris.includes(callbackUrl);
|
|
850
|
-
}
|
|
851
|
-
var StateBackedMcpOAuthClientProvider = class {
|
|
852
|
-
constructor(authSessionId, callbackUrl, sessionContext, runCredentialMutation) {
|
|
853
|
-
this.authSessionId = authSessionId;
|
|
854
|
-
this.callbackUrl = callbackUrl;
|
|
855
|
-
this.sessionContext = sessionContext;
|
|
856
|
-
this.runCredentialMutation = runCredentialMutation;
|
|
857
|
-
this.clientMetadata = createClientMetadata(callbackUrl);
|
|
858
|
-
}
|
|
859
|
-
authSessionId;
|
|
860
|
-
callbackUrl;
|
|
861
|
-
sessionContext;
|
|
862
|
-
runCredentialMutation;
|
|
863
|
-
clientMetadata;
|
|
864
|
-
get redirectUrl() {
|
|
865
|
-
return this.callbackUrl;
|
|
866
|
-
}
|
|
867
|
-
async state() {
|
|
868
|
-
return this.authSessionId;
|
|
869
|
-
}
|
|
870
|
-
async clientInformation() {
|
|
871
|
-
const session = await this.getCredentialContext();
|
|
872
|
-
const credentials = await getMcpStoredOAuthCredentials(
|
|
873
|
-
session.userId,
|
|
874
|
-
session.provider
|
|
875
|
-
);
|
|
876
|
-
const clientInformation = credentials?.clientInformation;
|
|
877
|
-
if (!clientInformation) {
|
|
878
|
-
return void 0;
|
|
879
|
-
}
|
|
880
|
-
if (clientAllowsRedirectUri(clientInformation, this.callbackUrl)) {
|
|
881
|
-
return clientInformation;
|
|
882
|
-
}
|
|
883
|
-
return void 0;
|
|
884
|
-
}
|
|
885
|
-
async saveClientInformation(clientInformation) {
|
|
886
|
-
await this.mutateCredentials(async () => {
|
|
887
|
-
const session = await this.getCredentialContext();
|
|
888
|
-
const credentials = await getMcpStoredOAuthCredentials(
|
|
889
|
-
session.userId,
|
|
890
|
-
session.provider
|
|
891
|
-
) ?? {};
|
|
892
|
-
await putMcpStoredOAuthCredentials(session.userId, session.provider, {
|
|
893
|
-
...credentials,
|
|
894
|
-
clientInformation
|
|
895
|
-
});
|
|
896
|
-
});
|
|
897
|
-
}
|
|
898
|
-
async tokens() {
|
|
899
|
-
const session = await this.getCredentialContext();
|
|
900
|
-
const credentials = await getMcpStoredOAuthCredentials(
|
|
901
|
-
session.userId,
|
|
902
|
-
session.provider
|
|
903
|
-
);
|
|
904
|
-
return credentials?.tokens;
|
|
905
|
-
}
|
|
906
|
-
async saveTokens(tokens) {
|
|
907
|
-
await this.mutateCredentials(async () => {
|
|
908
|
-
const session = await this.getCredentialContext();
|
|
909
|
-
const credentials = await getMcpStoredOAuthCredentials(
|
|
910
|
-
session.userId,
|
|
911
|
-
session.provider
|
|
912
|
-
) ?? {};
|
|
913
|
-
await putMcpStoredOAuthCredentials(session.userId, session.provider, {
|
|
914
|
-
...credentials,
|
|
915
|
-
tokens
|
|
916
|
-
});
|
|
917
|
-
});
|
|
918
|
-
}
|
|
919
|
-
async redirectToAuthorization(authorizationUrl) {
|
|
920
|
-
const existing = await getMcpAuthSession(this.authSessionId);
|
|
921
|
-
if (existing?.authorizationUrl && existing.authorizationUrl !== authorizationUrl.toString()) {
|
|
922
|
-
throw new Error("MCP OAuth authorization attempt is already initialized");
|
|
923
|
-
}
|
|
924
|
-
await this.ensureSession({
|
|
925
|
-
authorizationUrl: authorizationUrl.toString()
|
|
926
|
-
});
|
|
927
|
-
}
|
|
928
|
-
async saveCodeVerifier(codeVerifier) {
|
|
929
|
-
const existing = await getMcpAuthSession(this.authSessionId);
|
|
930
|
-
if (existing?.codeVerifier && existing.codeVerifier !== codeVerifier) {
|
|
931
|
-
throw new Error("MCP OAuth authorization attempt is already initialized");
|
|
932
|
-
}
|
|
933
|
-
await this.ensureSession({ codeVerifier });
|
|
934
|
-
}
|
|
935
|
-
async codeVerifier() {
|
|
936
|
-
const session = await this.requireSession();
|
|
937
|
-
if (!session.codeVerifier) {
|
|
938
|
-
throw new Error("Missing MCP OAuth code verifier");
|
|
939
|
-
}
|
|
940
|
-
return session.codeVerifier;
|
|
941
|
-
}
|
|
942
|
-
async saveDiscoveryState(state) {
|
|
943
|
-
await this.mutateCredentials(async () => {
|
|
944
|
-
const session = await this.getCredentialContext();
|
|
945
|
-
const credentials = await getMcpStoredOAuthCredentials(
|
|
946
|
-
session.userId,
|
|
947
|
-
session.provider
|
|
948
|
-
) ?? {};
|
|
949
|
-
await putMcpStoredOAuthCredentials(session.userId, session.provider, {
|
|
950
|
-
...credentials,
|
|
951
|
-
discoveryState: state
|
|
952
|
-
});
|
|
953
|
-
});
|
|
954
|
-
}
|
|
955
|
-
async discoveryState() {
|
|
956
|
-
const session = await this.getCredentialContext();
|
|
957
|
-
const credentials = await getMcpStoredOAuthCredentials(
|
|
958
|
-
session.userId,
|
|
959
|
-
session.provider
|
|
960
|
-
);
|
|
961
|
-
return credentials?.discoveryState;
|
|
962
|
-
}
|
|
963
|
-
async invalidateCredentials(scope) {
|
|
964
|
-
await this.mutateCredentials(async () => {
|
|
965
|
-
const session = await this.getCredentialContext();
|
|
966
|
-
const credentials = await getMcpStoredOAuthCredentials(
|
|
967
|
-
session.userId,
|
|
968
|
-
session.provider
|
|
969
|
-
) ?? {};
|
|
970
|
-
await putMcpStoredOAuthCredentials(session.userId, session.provider, {
|
|
971
|
-
...scope === "tokens" || scope === "all" ? void 0 : credentials.tokens ? { tokens: credentials.tokens } : {},
|
|
972
|
-
...scope === "client" || scope === "all" ? void 0 : credentials.clientInformation ? { clientInformation: credentials.clientInformation } : {},
|
|
973
|
-
...scope === "discovery" || scope === "all" ? void 0 : credentials.discoveryState ? { discoveryState: credentials.discoveryState } : {}
|
|
974
|
-
});
|
|
975
|
-
});
|
|
976
|
-
}
|
|
977
|
-
async getMcpServerSessionId() {
|
|
978
|
-
const session = await this.getCredentialContext();
|
|
979
|
-
return await getMcpServerSessionId(session.userId, session.provider);
|
|
980
|
-
}
|
|
981
|
-
async saveMcpServerSessionId(sessionId) {
|
|
982
|
-
await this.mutateCredentials(async () => {
|
|
983
|
-
const session = await this.getCredentialContext();
|
|
984
|
-
if (!sessionId) {
|
|
985
|
-
await deleteMcpServerSessionId(session.userId, session.provider);
|
|
986
|
-
return;
|
|
987
|
-
}
|
|
988
|
-
await putMcpServerSessionId(session.userId, session.provider, sessionId);
|
|
989
|
-
});
|
|
990
|
-
}
|
|
991
|
-
/** Route shared credential writes through callback-owned freshness control. */
|
|
992
|
-
async mutateCredentials(mutation) {
|
|
993
|
-
return this.runCredentialMutation ? await this.runCredentialMutation(mutation) : await mutation();
|
|
994
|
-
}
|
|
995
|
-
async getCredentialContext() {
|
|
996
|
-
return this.sessionContext ?? await this.requireSession();
|
|
997
|
-
}
|
|
998
|
-
async ensureSession(patch) {
|
|
999
|
-
const existing = await getMcpAuthSession(this.authSessionId);
|
|
1000
|
-
if (existing) {
|
|
1001
|
-
return await patchMcpAuthSession(this.authSessionId, patch);
|
|
1002
|
-
}
|
|
1003
|
-
if (!this.sessionContext) {
|
|
1004
|
-
throw new Error(`Unknown MCP auth session: ${this.authSessionId}`);
|
|
1005
|
-
}
|
|
1006
|
-
const now = Date.now();
|
|
1007
|
-
const nextSession = {
|
|
1008
|
-
schemaVersion: 2,
|
|
1009
|
-
authSessionId: this.authSessionId,
|
|
1010
|
-
...this.sessionContext,
|
|
1011
|
-
...patch,
|
|
1012
|
-
createdAtMs: now,
|
|
1013
|
-
updatedAtMs: now
|
|
1014
|
-
};
|
|
1015
|
-
await putMcpAuthSession(nextSession);
|
|
1016
|
-
return nextSession;
|
|
1017
|
-
}
|
|
1018
|
-
async requireSession() {
|
|
1019
|
-
const session = await getMcpAuthSession(this.authSessionId);
|
|
1020
|
-
if (!session) {
|
|
1021
|
-
throw new Error(`Unknown MCP auth session: ${this.authSessionId}`);
|
|
1022
|
-
}
|
|
1023
|
-
return session;
|
|
1024
|
-
}
|
|
1025
|
-
};
|
|
1026
|
-
|
|
1027
|
-
// src/chat/mcp/oauth.ts
|
|
1028
|
-
function getMcpOAuthCallbackPath(provider) {
|
|
1029
|
-
return `/api/oauth/callback/mcp/${provider}`;
|
|
1030
|
-
}
|
|
1031
|
-
function requirePluginWithMcp(provider) {
|
|
1032
|
-
const plugin = pluginCatalogRuntime.getDefinition(provider);
|
|
1033
|
-
if (!plugin?.manifest.mcp) {
|
|
1034
|
-
throw new Error(`Plugin "${provider}" does not support MCP`);
|
|
1035
|
-
}
|
|
1036
|
-
return plugin;
|
|
1037
|
-
}
|
|
1038
|
-
async function createMcpOAuthClientProvider(input) {
|
|
1039
|
-
requirePluginWithMcp(input.provider);
|
|
1040
|
-
const baseUrl = resolveBaseUrl();
|
|
1041
|
-
if (!baseUrl) {
|
|
1042
|
-
throw new Error(
|
|
1043
|
-
"Cannot determine base URL (set JUNIOR_BASE_URL or deploy to Vercel)"
|
|
1044
|
-
);
|
|
1045
|
-
}
|
|
1046
|
-
const authSessionId = input.createAuthorizationState ? await input.createAuthorizationState() : randomUUID();
|
|
1047
|
-
return new StateBackedMcpOAuthClientProvider(
|
|
1048
|
-
authSessionId,
|
|
1049
|
-
`${baseUrl}${getMcpOAuthCallbackPath(input.provider)}`,
|
|
1050
|
-
{
|
|
1051
|
-
provider: input.provider,
|
|
1052
|
-
userId: input.userId,
|
|
1053
|
-
conversationId: input.conversationId,
|
|
1054
|
-
...input.destination ? { destination: input.destination } : void 0,
|
|
1055
|
-
...input.source ? { source: input.source } : void 0,
|
|
1056
|
-
sessionId: input.sessionId,
|
|
1057
|
-
userMessage: input.userMessage,
|
|
1058
|
-
...input.channelId ? { channelId: input.channelId } : void 0,
|
|
1059
|
-
...input.threadTs ? { threadTs: input.threadTs } : void 0,
|
|
1060
|
-
...input.toolChannelId ? { toolChannelId: input.toolChannelId } : void 0,
|
|
1061
|
-
...input.configuration ? { configuration: input.configuration } : void 0
|
|
1062
|
-
}
|
|
1063
|
-
);
|
|
1064
|
-
}
|
|
1065
|
-
async function finalizeMcpAuthorization(provider, authSessionId, authorizationCode, runCredentialMutation) {
|
|
1066
|
-
const plugin = requirePluginWithMcp(provider);
|
|
1067
|
-
const mcp = plugin.manifest.mcp;
|
|
1068
|
-
if (!mcp) {
|
|
1069
|
-
throw new Error(`Plugin "${provider}" does not support MCP`);
|
|
1070
|
-
}
|
|
1071
|
-
const session = await getMcpAuthSession(authSessionId);
|
|
1072
|
-
if (!session) {
|
|
1073
|
-
throw new Error(`Unknown MCP auth session: ${authSessionId}`);
|
|
1074
|
-
}
|
|
1075
|
-
if (session.provider !== provider) {
|
|
1076
|
-
throw new Error(
|
|
1077
|
-
`MCP auth session provider mismatch: expected "${provider}", got "${session.provider}"`
|
|
1078
|
-
);
|
|
1079
|
-
}
|
|
1080
|
-
const baseUrl = resolveBaseUrl();
|
|
1081
|
-
if (!baseUrl) {
|
|
1082
|
-
throw new Error(
|
|
1083
|
-
"Cannot determine base URL (set JUNIOR_BASE_URL or deploy to Vercel)"
|
|
1084
|
-
);
|
|
1085
|
-
}
|
|
1086
|
-
const callbackUrl = `${baseUrl}${getMcpOAuthCallbackPath(provider)}`;
|
|
1087
|
-
const authProvider = new StateBackedMcpOAuthClientProvider(
|
|
1088
|
-
authSessionId,
|
|
1089
|
-
callbackUrl,
|
|
1090
|
-
void 0,
|
|
1091
|
-
runCredentialMutation
|
|
1092
|
-
);
|
|
1093
|
-
const requestInit = {};
|
|
1094
|
-
if (mcp.headers && Object.keys(mcp.headers).length > 0) {
|
|
1095
|
-
requestInit.headers = new Headers(mcp.headers);
|
|
1096
|
-
}
|
|
1097
|
-
let providerStatus2;
|
|
1098
|
-
const transport = new StreamableHTTPClientTransport(new URL(mcp.url), {
|
|
1099
|
-
...Object.keys(requestInit).length > 0 ? { requestInit } : void 0,
|
|
1100
|
-
authProvider,
|
|
1101
|
-
fetch: fetchWithBoundedOAuthErrorBodies(void 0, (status) => {
|
|
1102
|
-
providerStatus2 = status;
|
|
1103
|
-
})
|
|
1104
|
-
});
|
|
1105
|
-
let failure;
|
|
1106
|
-
try {
|
|
1107
|
-
await transport.finishAuth(authorizationCode);
|
|
1108
|
-
} catch (error) {
|
|
1109
|
-
failure = toMcpProviderError(error, {
|
|
1110
|
-
phase: "oauth_callback",
|
|
1111
|
-
provider,
|
|
1112
|
-
resourceHost: new URL(mcp.url).hostname,
|
|
1113
|
-
status: providerStatus2
|
|
1114
|
-
});
|
|
1115
|
-
}
|
|
1116
|
-
try {
|
|
1117
|
-
await transport.close();
|
|
1118
|
-
} catch (error) {
|
|
1119
|
-
failure ??= toMcpProviderError(error, {
|
|
1120
|
-
phase: "oauth_callback",
|
|
1121
|
-
provider,
|
|
1122
|
-
resourceHost: new URL(mcp.url).hostname
|
|
1123
|
-
});
|
|
1124
|
-
}
|
|
1125
|
-
if (failure) {
|
|
1126
|
-
throw failure;
|
|
1127
|
-
}
|
|
1128
|
-
const nextSession = await getMcpAuthSession(authSessionId);
|
|
1129
|
-
if (!nextSession) {
|
|
1130
|
-
throw new Error(`Unknown MCP auth session: ${authSessionId}`);
|
|
1131
|
-
}
|
|
1132
|
-
return nextSession;
|
|
1133
|
-
}
|
|
1134
|
-
|
|
1135
|
-
export {
|
|
1136
|
-
createUserTokenStore,
|
|
1137
|
-
issueProviderCredentialLease,
|
|
1138
|
-
McpProviderError,
|
|
1139
|
-
toMcpProviderError,
|
|
1140
|
-
getMcpProviderErrorAttributes,
|
|
1141
|
-
McpToolError,
|
|
1142
|
-
getMcpAwareErrorType,
|
|
1143
|
-
getMcpAwareTelemetryMessage,
|
|
1144
|
-
lookupSlackUserProfile,
|
|
1145
|
-
lookupSlackUserByEmail,
|
|
1146
|
-
searchSlackUsers,
|
|
1147
|
-
selectPluginGrant,
|
|
1148
|
-
onPluginEgressResponse,
|
|
1149
|
-
hasEgressCredentialHooks,
|
|
1150
|
-
getOAuthAccountProviders,
|
|
1151
|
-
resolvePluginOAuthAccount,
|
|
1152
|
-
issuePluginCredential,
|
|
1153
|
-
getMcpAuthSession,
|
|
1154
|
-
patchMcpAuthSession,
|
|
1155
|
-
deleteMcpAuthSession,
|
|
1156
|
-
deleteMcpAuthSessionsForUserProvider,
|
|
1157
|
-
getMcpStoredOAuthCredentials,
|
|
1158
|
-
putMcpStoredOAuthCredentials,
|
|
1159
|
-
deleteMcpStoredOAuthCredentials,
|
|
1160
|
-
deleteMcpServerSessionId,
|
|
1161
|
-
createMcpOAuthClientProvider,
|
|
1162
|
-
finalizeMcpAuthorization
|
|
1163
|
-
};
|