@kontext-dev/js-sdk 1.0.0 → 1.1.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.
Files changed (63) hide show
  1. package/dist/adapters/ai/index.cjs +12 -2
  2. package/dist/adapters/ai/index.cjs.map +1 -1
  3. package/dist/adapters/ai/index.js +12 -2
  4. package/dist/adapters/ai/index.js.map +1 -1
  5. package/dist/adapters/cloudflare/index.cjs +13 -0
  6. package/dist/adapters/cloudflare/index.cjs.map +1 -1
  7. package/dist/adapters/cloudflare/index.js +13 -0
  8. package/dist/adapters/cloudflare/index.js.map +1 -1
  9. package/dist/adapters/cloudflare/react.cjs +12 -2
  10. package/dist/adapters/cloudflare/react.cjs.map +1 -1
  11. package/dist/adapters/cloudflare/react.js +12 -2
  12. package/dist/adapters/cloudflare/react.js.map +1 -1
  13. package/dist/adapters/react/index.cjs +12 -2
  14. package/dist/adapters/react/index.cjs.map +1 -1
  15. package/dist/adapters/react/index.js +12 -2
  16. package/dist/adapters/react/index.js.map +1 -1
  17. package/dist/client/index.cjs +89 -68
  18. package/dist/client/index.cjs.map +1 -1
  19. package/dist/client/index.d.cts +2 -0
  20. package/dist/client/index.d.ts +2 -0
  21. package/dist/client/index.js +90 -70
  22. package/dist/client/index.js.map +1 -1
  23. package/dist/errors.cjs +78 -0
  24. package/dist/errors.cjs.map +1 -1
  25. package/dist/errors.d.cts +7 -1
  26. package/dist/errors.d.ts +7 -1
  27. package/dist/errors.js +78 -1
  28. package/dist/errors.js.map +1 -1
  29. package/dist/index.cjs +124 -86
  30. package/dist/index.cjs.map +1 -1
  31. package/dist/index.d.cts +1 -1
  32. package/dist/index.d.ts +1 -1
  33. package/dist/index.js +125 -87
  34. package/dist/index.js.map +1 -1
  35. package/dist/{kontext-CgIBANFo.d.cts → kontext-CBPuE-hq.d.cts} +3 -0
  36. package/dist/{kontext-CgIBANFo.d.ts → kontext-CBPuE-hq.d.ts} +3 -0
  37. package/dist/management/index.cjs +15 -0
  38. package/dist/management/index.cjs.map +1 -1
  39. package/dist/management/index.d.cts +2 -2
  40. package/dist/management/index.d.ts +2 -2
  41. package/dist/management/index.js +15 -1
  42. package/dist/management/index.js.map +1 -1
  43. package/dist/mcp/index.cjs +13 -2
  44. package/dist/mcp/index.cjs.map +1 -1
  45. package/dist/mcp/index.d.cts +3 -0
  46. package/dist/mcp/index.d.ts +3 -0
  47. package/dist/mcp/index.js +14 -3
  48. package/dist/mcp/index.js.map +1 -1
  49. package/dist/oauth/index.cjs +12 -2
  50. package/dist/oauth/index.cjs.map +1 -1
  51. package/dist/oauth/index.d.cts +1 -1
  52. package/dist/oauth/index.d.ts +1 -1
  53. package/dist/oauth/index.js +12 -2
  54. package/dist/oauth/index.js.map +1 -1
  55. package/dist/server/index.cjs +47 -20
  56. package/dist/server/index.cjs.map +1 -1
  57. package/dist/server/index.d.cts +2 -2
  58. package/dist/server/index.d.ts +2 -2
  59. package/dist/server/index.js +48 -21
  60. package/dist/server/index.js.map +1 -1
  61. package/dist/{types-C6ep5fVw.d.cts → types-DicGI7ix.d.cts} +21 -1
  62. package/dist/{types-C6ep5fVw.d.ts → types-DicGI7ix.d.ts} +21 -1
  63. package/package.json +1 -1
package/dist/index.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { ClientState, ConnectSessionResult, IntegrationInfo, KontextClient, KontextClientConfig, KontextTool, ToolResult, createKontextClient } from './client/index.cjs';
2
2
  export { K as KontextOrchestrator, a as KontextOrchestratorConfig, b as KontextOrchestratorState, c as createKontextOrchestrator } from './index-DcL4a5Vq.cjs';
3
- export { I as IntegrationCredential, a as IntegrationName, b as IntegrationResolvedCredentials, K as KnownIntegration, c as Kontext, d as KontextOptions, M as McpServerFactory, e as McpServerOrFactory, f as MiddlewareOptions } from './kontext-CgIBANFo.cjs';
3
+ export { I as IntegrationCredential, a as IntegrationName, b as IntegrationResolvedCredentials, K as KnownIntegration, c as Kontext, d as KontextOptions, M as McpServerFactory, e as McpServerOrFactory, f as MiddlewareOptions } from './kontext-CBPuE-hq.cjs';
4
4
  export { K as KontextTokenVerifier, a as KontextTokenVerifierConfig } from './verifier-CoJmYiw3.cjs';
5
5
  export { AuthorizationRequiredError, ConfigError, ElicitationEntry, HttpError, IntegrationConnectionRequiredError, KontextError, NetworkError, OAuthError, isKontextError, isNetworkError, isUnauthorizedError, parseHttpError } from './errors.cjs';
6
6
  import './mcp/index.cjs';
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { ClientState, ConnectSessionResult, IntegrationInfo, KontextClient, KontextClientConfig, KontextTool, ToolResult, createKontextClient } from './client/index.js';
2
2
  export { K as KontextOrchestrator, a as KontextOrchestratorConfig, b as KontextOrchestratorState, c as createKontextOrchestrator } from './index-D5hS5PGn.js';
3
- export { I as IntegrationCredential, a as IntegrationName, b as IntegrationResolvedCredentials, K as KnownIntegration, c as Kontext, d as KontextOptions, M as McpServerFactory, e as McpServerOrFactory, f as MiddlewareOptions } from './kontext-CgIBANFo.js';
3
+ export { I as IntegrationCredential, a as IntegrationName, b as IntegrationResolvedCredentials, K as KnownIntegration, c as Kontext, d as KontextOptions, M as McpServerFactory, e as McpServerOrFactory, f as MiddlewareOptions } from './kontext-CBPuE-hq.js';
4
4
  export { K as KontextTokenVerifier, a as KontextTokenVerifierConfig } from './verifier-CoJmYiw3.js';
5
5
  export { AuthorizationRequiredError, ConfigError, ElicitationEntry, HttpError, IntegrationConnectionRequiredError, KontextError, NetworkError, OAuthError, isKontextError, isNetworkError, isUnauthorizedError, parseHttpError } from './errors.js';
6
6
  import './mcp/index.js';
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Client } from '@modelcontextprotocol/sdk/client/index.js';
2
2
  import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
3
- import { isInitializeRequest, UrlElicitationRequiredError, ElicitRequestSchema, ElicitationCompleteNotificationSchema } from '@modelcontextprotocol/sdk/types.js';
3
+ import { ErrorCode, isInitializeRequest, UrlElicitationRequiredError, ElicitRequestSchema, ElicitationCompleteNotificationSchema } from '@modelcontextprotocol/sdk/types.js';
4
4
  import { createHash, randomBytes } from 'crypto';
5
5
  import { createRequire } from 'module';
6
6
  import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
@@ -73,8 +73,6 @@ var StorageKeys = {
73
73
  function resourceTokenKey(resource) {
74
74
  return `${StorageKeys.RESOURCE_TOKENS}:${resource}`;
75
75
  }
76
-
77
- // src/errors.ts
78
76
  var KontextError = class extends Error {
79
77
  /** Brand field for type narrowing without instanceof */
80
78
  kontextError = true;
@@ -207,12 +205,20 @@ function isNetworkError(err) {
207
205
  if (typeof causeCode === "string" && NETWORK_ERROR_CODES.has(causeCode))
208
206
  return true;
209
207
  }
208
+ if (err.name === "TypeError") {
209
+ const msg = err.message.toLowerCase();
210
+ if (msg === "failed to fetch" || msg === "load failed" || msg.includes("networkerror")) {
211
+ return true;
212
+ }
213
+ }
210
214
  return false;
211
215
  }
212
216
  function isUnauthorizedError(err) {
213
217
  const props = errorProps(err);
214
218
  if (props.statusCode === 401 || props.status === 401) return true;
219
+ if (props.code === 401) return true;
215
220
  if (err.name === "UnauthorizedError") return true;
221
+ if (err.constructor?.name === "UnauthorizedError") return true;
216
222
  if (err.message === "Unauthorized") return true;
217
223
  return false;
218
224
  }
@@ -271,6 +277,73 @@ function parseHttpError(statusCode, body) {
271
277
  });
272
278
  }
273
279
  }
280
+ var MCP_CODE_MAP = {
281
+ [ErrorCode.ParseError]: { code: "kontext_mcp_parse_error" },
282
+ [ErrorCode.InvalidRequest]: { code: "kontext_mcp_invalid_request" },
283
+ [ErrorCode.MethodNotFound]: { code: "kontext_mcp_method_not_found" },
284
+ [ErrorCode.InvalidParams]: { code: "kontext_mcp_invalid_params" },
285
+ [ErrorCode.InternalError]: {
286
+ code: "kontext_mcp_internal_error",
287
+ statusCode: 500
288
+ },
289
+ [ErrorCode.RequestTimeout]: {
290
+ code: "kontext_mcp_session_expired",
291
+ statusCode: 401
292
+ },
293
+ [ErrorCode.ConnectionClosed]: { code: "kontext_mcp_session_error" }
294
+ };
295
+ function translateError(err) {
296
+ if (isKontextError(err)) return err;
297
+ if (!(err instanceof Error)) {
298
+ return new KontextError(String(err), "kontext_unknown_error");
299
+ }
300
+ const props = err;
301
+ if (props.code === ErrorCode.UrlElicitationRequired) {
302
+ const elicitations = props.elicitations ?? props.data?.elicitations;
303
+ const elicitation = elicitations?.[0];
304
+ return new IntegrationConnectionRequiredError(
305
+ elicitation?.integrationId ?? "unknown",
306
+ {
307
+ integrationName: elicitation?.integrationName,
308
+ connectUrl: elicitation?.url,
309
+ message: elicitation?.message,
310
+ cause: err
311
+ }
312
+ );
313
+ }
314
+ if (typeof props.code === "number" && props.code < 0) {
315
+ const entry = MCP_CODE_MAP[props.code];
316
+ if (entry) {
317
+ return new KontextError(err.message, entry.code, {
318
+ statusCode: entry.statusCode,
319
+ cause: err
320
+ });
321
+ }
322
+ return new KontextError(err.message, "kontext_mcp_error", {
323
+ cause: err,
324
+ meta: { mcpCode: props.code }
325
+ });
326
+ }
327
+ const statusCode = props.statusCode ?? props.status ?? (typeof props.code === "number" && props.code >= 400 && props.code < 600 ? props.code : void 0);
328
+ if (typeof statusCode === "number" && statusCode >= 400) {
329
+ if (statusCode === 401) {
330
+ return new AuthorizationRequiredError(err.message, { cause: err });
331
+ }
332
+ return new KontextError(err.message, "kontext_server_error", {
333
+ statusCode,
334
+ cause: err
335
+ });
336
+ }
337
+ if (isUnauthorizedError(err)) {
338
+ return new AuthorizationRequiredError(err.message, { cause: err });
339
+ }
340
+ if (isNetworkError(err)) {
341
+ return new NetworkError(err.message, { cause: err });
342
+ }
343
+ return new KontextError(err.message, "kontext_unknown_error", {
344
+ cause: err
345
+ });
346
+ }
274
347
 
275
348
  // src/oauth/provider.ts
276
349
  var KontextOAuthProvider = class {
@@ -1438,35 +1511,21 @@ async function withTransientRetry(operation, maxRetries = 1) {
1438
1511
  function toKontextError(err, context) {
1439
1512
  const contextMeta = context ? { ...context } : void 0;
1440
1513
  const mergeMeta = (base) => contextMeta ? { ...base ?? {}, ...contextMeta } : base ?? {};
1441
- if (isKontextError(err)) {
1442
- if (!contextMeta) {
1443
- return err;
1444
- }
1445
- const cloned = Object.create(Object.getPrototypeOf(err));
1446
- Object.defineProperties(cloned, Object.getOwnPropertyDescriptors(err));
1447
- Object.defineProperty(cloned, "meta", {
1448
- value: mergeMeta(err.meta),
1449
- enumerable: true,
1450
- writable: true,
1451
- configurable: true
1452
- });
1453
- return cloned;
1514
+ const translated = translateError(err);
1515
+ if (!contextMeta) {
1516
+ return translated;
1454
1517
  }
1455
- if (err instanceof Error) {
1456
- if (isUnauthorizedError(err)) {
1457
- return new AuthorizationRequiredError(err.message, {
1458
- meta: mergeMeta(),
1459
- cause: err
1460
- });
1461
- }
1462
- return new KontextError(err.message, "kontext_unknown_error", {
1463
- meta: mergeMeta(),
1464
- cause: err
1465
- });
1466
- }
1467
- return new KontextError(String(err), "kontext_unknown_error", {
1468
- meta: mergeMeta()
1518
+ const cloned = Object.create(
1519
+ Object.getPrototypeOf(translated)
1520
+ );
1521
+ Object.defineProperties(cloned, Object.getOwnPropertyDescriptors(translated));
1522
+ Object.defineProperty(cloned, "meta", {
1523
+ value: mergeMeta(translated.meta),
1524
+ enumerable: true,
1525
+ writable: true,
1526
+ configurable: true
1469
1527
  });
1528
+ return cloned;
1470
1529
  }
1471
1530
  function isAuthorizationRequired(err) {
1472
1531
  if (err instanceof AuthorizationRequiredError) return true;
@@ -2180,45 +2239,6 @@ function extractTextContent(result) {
2180
2239
  }
2181
2240
  return JSON.stringify(result);
2182
2241
  }
2183
- function translateError(err) {
2184
- if (isKontextError(err)) return err;
2185
- if (!(err instanceof Error)) {
2186
- return new KontextError(String(err), "kontext_unknown_error");
2187
- }
2188
- const props = err;
2189
- if (props.code === -32042) {
2190
- const elicitations = props.elicitations ?? props.data?.elicitations;
2191
- const elicitation = elicitations?.[0];
2192
- return new IntegrationConnectionRequiredError(
2193
- elicitation?.integrationId ?? "unknown",
2194
- {
2195
- integrationName: elicitation?.integrationName,
2196
- connectUrl: elicitation?.url,
2197
- message: elicitation?.message,
2198
- cause: err
2199
- }
2200
- );
2201
- }
2202
- const statusCode = props.statusCode ?? props.status;
2203
- if (typeof statusCode === "number" && statusCode >= 400) {
2204
- if (statusCode === 401) {
2205
- return new AuthorizationRequiredError(err.message, { cause: err });
2206
- }
2207
- return new KontextError(err.message, "kontext_server_error", {
2208
- statusCode,
2209
- cause: err
2210
- });
2211
- }
2212
- if (isUnauthorizedError(err)) {
2213
- return new AuthorizationRequiredError(err.message, { cause: err });
2214
- }
2215
- if (isNetworkError(err)) {
2216
- return new NetworkError(err.message, { cause: err });
2217
- }
2218
- return new KontextError(err.message, "kontext_unknown_error", {
2219
- cause: err
2220
- });
2221
- }
2222
2242
  function createSingleEndpointKontextClient(config) {
2223
2243
  if (!config.clientId) {
2224
2244
  throw new ConfigError(
@@ -2508,6 +2528,7 @@ function createKontextClient(config) {
2508
2528
  // src/management/types.ts
2509
2529
  var TOKEN_EXCHANGE_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:token-exchange";
2510
2530
  var TOKEN_TYPE_ACCESS_TOKEN = "urn:ietf:params:oauth:token-type:access_token";
2531
+ var TOKEN_TYPE_USER_ID = "urn:kontext:user-id";
2511
2532
 
2512
2533
  // src/oauth/token-exchange.ts
2513
2534
  async function exchangeToken(config, subjectToken, resource, scope, subjectTokenType = TOKEN_TYPE_ACCESS_TOKEN) {
@@ -2987,7 +3008,7 @@ var Kontext = class _Kontext {
2987
3008
  oauthMetadata = null;
2988
3009
  metadataFetchedAt = 0;
2989
3010
  metadataPromise = null;
2990
- // Token exchange caching: keyed by `${integration}\0${subjectToken}`
3011
+ // Token exchange caching: keyed by `${mode}\0${integration}\0${subjectToken}`
2991
3012
  credentialCache = /* @__PURE__ */ new Map();
2992
3013
  resolvedCredentialCache = /* @__PURE__ */ new Map();
2993
3014
  runtimeAuthCache = /* @__PURE__ */ new Map();
@@ -3145,23 +3166,28 @@ var Kontext = class _Kontext {
3145
3166
  router.delete(mcpPath, mcpHandler.delete);
3146
3167
  return router;
3147
3168
  }
3148
- // ===========================================================================
3149
- // require()
3150
- // ===========================================================================
3151
- /**
3152
- * Exchange a user's access token for an integration credential.
3153
- *
3154
- * @param integration - Integration name (e.g., "github")
3155
- * @param token - The user's Bearer token (from `authInfo.token`)
3156
- * @returns Integration credential with `accessToken` and `authorization` header
3157
- *
3158
- * @throws {IntegrationConnectionRequiredError} User hasn't connected this integration
3159
- * @throws {OAuthError} Token exchange failed
3160
- */
3161
- async require(integration, token) {
3169
+ async require(integration, tokenOrOpts) {
3170
+ let isUserIdMode = false;
3171
+ let subjectToken = "";
3172
+ if (typeof tokenOrOpts === "string") {
3173
+ subjectToken = tokenOrOpts;
3174
+ } else if (tokenOrOpts !== null && typeof tokenOrOpts === "object" && typeof tokenOrOpts.userId === "string") {
3175
+ isUserIdMode = true;
3176
+ subjectToken = tokenOrOpts.userId.trim();
3177
+ if (!subjectToken) {
3178
+ throw new TypeError(
3179
+ "Kontext.require() expects a non-empty userId when called with { userId }."
3180
+ );
3181
+ }
3182
+ } else {
3183
+ throw new TypeError(
3184
+ "Kontext.require() expects a token string or { userId: string }."
3185
+ );
3186
+ }
3187
+ const subjectTokenType = isUserIdMode ? TOKEN_TYPE_USER_ID : void 0;
3162
3188
  const now = Date.now();
3163
3189
  this.evictExpiredCredentials(now);
3164
- const cacheKey = `${integration}\0${token}`;
3190
+ const cacheKey = isUserIdMode ? `u\0${integration}\0${subjectToken}` : `t\0${integration}\0${subjectToken}`;
3165
3191
  const cached = this.credentialCache.get(cacheKey);
3166
3192
  if (cached && now < cached.expiresAt) {
3167
3193
  this.credentialCache.delete(cacheKey);
@@ -3178,13 +3204,25 @@ var Kontext = class _Kontext {
3178
3204
  };
3179
3205
  let response;
3180
3206
  try {
3181
- response = await exchangeToken(exchangeConfig, token, integration);
3207
+ response = await exchangeToken(
3208
+ exchangeConfig,
3209
+ subjectToken,
3210
+ integration,
3211
+ void 0,
3212
+ subjectTokenType
3213
+ );
3182
3214
  } catch (err) {
3183
3215
  if (err instanceof OAuthError) {
3184
3216
  if (err.errorCode === "integration_required" || err.message.includes("not connected") || err.message.includes("expired") && err.message.includes("reconnect")) {
3185
3217
  const integrationId = err.meta.integrationId || integration;
3218
+ if (isUserIdMode) {
3219
+ throw new IntegrationConnectionRequiredError(integrationId, {
3220
+ integrationName: err.meta.integrationName,
3221
+ message: err.message
3222
+ });
3223
+ }
3186
3224
  const connectUrl = await this.fetchConnectUrl(
3187
- token,
3225
+ subjectToken,
3188
3226
  integrationId,
3189
3227
  exchangeConfig
3190
3228
  );