@thalovant/mcp-server 0.1.19 → 0.1.20

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/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.20
4
+
5
+ - Require HTTPS for credential-bearing API calls, preserving explicit loopback HTTP for local development. Reject API redirects through the SDK. Instantiate runtime clients only after acquiring their identity lease.
6
+ - Bind server-managed Thalovant tokens and password login to their configured API origin. Reject cross-origin tool overrides before any outbound request, including credentials for remote principals; retain configured custom origins and anonymous public discovery.
7
+ - Add `thalovant_query` for routed HiveMind queries, preserving query/request/session correlation and runtime identity cleanup; hide it in read-only mode. The default catalogue now contains 41 tools.
8
+ - Add read-only `thalovant_intent_inventory` and `thalovant_get_operation` tools for runtime discovery and asynchronous provisioning status. Inventory preserves fallback knowledge and conservative answerability.
9
+ - Require Node SDK 0.3.9 for aligned intent discovery, request correlation, whole-operation reply deadlines, fixed Ask settlement windows, cancellation and retained cleanup ownership, and credential-safe control-plane redirect handling.
10
+ - Retain the runtime identity lease until actual cleanup, even after a caller close timeout; failed cleanup blocks later identity reuse with an explicit error. Bound queued lease acquisition to six seconds without releasing pending ownership or executing expired callers.
11
+ - Upgrade the test runner to Vitest 4.1.11 to remove GHSA-82fw-gwwq-j7x9.
12
+ - Test Node 20, 22 and 24; add per-principal/shared credential regressions and exercise the inventory tool over a real HTTPS Noise session.
13
+
3
14
  ## 0.1.19
4
15
 
5
16
  - Require Node SDK 0.3.5 or newer so runtime tools receive secure MQTT URL enforcement and bounded MQTT setup and HTTP failure cleanup.
package/README.md CHANGED
@@ -93,6 +93,13 @@ npm start
93
93
 
94
94
  If neither a token nor email/password is configured, authenticated control-plane tools fail with a clear error naming the supported options.
95
95
 
96
+ Configured API tokens and login credentials are bound to the origin of their
97
+ configured `apiUrl` or `THALOVANT_API_URL` (default `https://api.thalovant.com`).
98
+ A tool argument cannot redirect those credentials to another origin. Equivalent
99
+ URL spellings and paths on the same origin are allowed; custom origins must be
100
+ configured alongside their credentials. Anonymous public discovery may still
101
+ select a custom API URL.
102
+
96
103
  ## Local Stdio
97
104
 
98
105
  The server speaks MCP over stdio and does not write logs to stdout.
@@ -305,8 +312,10 @@ Read-only:
305
312
  - `thalovant_get_public_hub`
306
313
  - `thalovant_list_hubs`
307
314
  - `thalovant_get_hub`
315
+ - `thalovant_get_operation`
308
316
  - `thalovant_identity_status`
309
317
  - `thalovant_healthcheck`
318
+ - `thalovant_intent_inventory`
310
319
  - `thalovant_wait_for_event`
311
320
  - `thalovant_get_analytics_overview`
312
321
  - `thalovant_list_memory_items`
@@ -327,6 +336,7 @@ Writes or hub events:
327
336
 
328
337
  - `thalovant_create_client_identity`
329
338
  - `thalovant_ask`
339
+ - `thalovant_query`
330
340
  - `thalovant_send_action`
331
341
  - `thalovant_send_code`
332
342
  - `thalovant_emit_event`
@@ -355,6 +365,16 @@ Destructive, **not registered unless explicitly enabled** (see [Destructive Tool
355
365
 
356
366
  Tool outputs redact credential-shaped fields. `thalovant_create_client_identity` does not return secret identity material; pass `savePath` when you want the full identity written to a local file with mode `0600`. `savePath` is confined to the server's identity directory (`THALOVANT_MCP_IDENTITY_DIR`, default `<config-dir>/thalovant/identities`): pass a plain filename, since absolute paths outside that directory and `..` traversal are rejected, so a model cannot drop a credential file into a git working tree or synced folder. `thalovant_config_status` reports the active `identityDir`.
357
367
 
368
+ `thalovant_intent_inventory` uses the runtime identity and accepts `languages`,
369
+ `describe`, `fallback`, and a per-query/batch `timeoutMs`. It returns registered
370
+ intents and examples, fallback skills, `fallbacks_known`, and `may_answer` by
371
+ requested language. A missing or denied optional fallback-skill query remains
372
+ unknown, rather than being reported as a known empty list. The optional probe
373
+ adds at most 1500ms. A silent listing can use engine manifests; disable this with
374
+ `fallback: false`. Both new tools are available in read-only mode and respect
375
+ per-principal policy. `thalovant_get_operation` reads an operation ID returned by
376
+ provisioning without replaying the write.
377
+
358
378
  ## Destructive Tools
359
379
 
360
380
  `thalovant_delete_hub` and `thalovant_delete_runtime_group` are **disabled by default**. They are not merely blocked when called — they are never registered, so they do not appear in `tools/list` and a model cannot see or attempt them.
@@ -399,7 +419,7 @@ Runtime calls sharing the same hub client identity run sequentially within one
399
419
  MCP process. Use a distinct client identity for each independently running MCP
400
420
  server so the hub can keep their sessions separate.
401
421
 
402
- Version 0.1.19 uses `@thalovant/sdk` 0.3.5 or newer. This release enforces
422
+ Version 0.1.20 uses `@thalovant/sdk` `^0.3.9` (0.3.9 through versions below 0.4.0). This release enforces
403
423
  secure effective MQTT URLs and carries a single connection deadline through
404
424
  MQTT setup and HTTP failure cleanup. Runtime tools support
405
425
  HiveMind v3 Noise over WSS, HTTPS and MQTT over TLS. `thalovant_healthcheck`
@@ -413,6 +433,13 @@ failure preserves those pins; replacing a server key requires an explicit,
413
433
  verified trust change. HTTPS identities must advertise the hub's HTTPS plugin,
414
434
  and MQTT requires the identity's broker credentials and topic prefix.
415
435
 
436
+ Runtime tools retain their identity lease through actual cleanup. If the close
437
+ caller times out, a later tool waits up to six seconds for that cleanup before opening a session.
438
+ A queued tool that reaches this deadline fails without executing later or releasing
439
+ the previous session's identity.
440
+ An actual cleanup failure marks the identity unavailable to later calls instead
441
+ of risking concurrent sessions.
442
+
416
443
  ## Development
417
444
 
418
445
  ```bash
@@ -429,3 +456,16 @@ npm pack --dry-run
429
456
  ## License
430
457
 
431
458
  MIT. This is the right default for a public integration server: it is permissive, compatible with the MIT Thalovant Node SDK and MCP TypeScript SDK, and does not force downstream agent or enterprise users into a reciprocal licensing model.
459
+
460
+ Credential-bearing control-plane calls require HTTPS. Explicit loopback HTTP
461
+ (`localhost`, `127.0.0.1`, `[::1]`) remains supported for local development.
462
+ API redirects are rejected so password-login bodies cannot be forwarded.
463
+ Runtime clients are constructed only after acquiring their identity lease.
464
+
465
+ `thalovant_query` sends a routed HiveMind query; the hub may cascade it according
466
+ to its routing policy. It accepts `text`, `timeoutMs`, `lang`, `sessionId`,
467
+ `requestId`, `queryId`, `context` and `replySettleMs`, plus the runtime identity
468
+ options, and returns the same normalized reply shape as `thalovant_ask`. A query
469
+ may trigger actions, so read-only mode hides it. Conversation workflows use
470
+ `sessionId` with ask/query and the existing event-wait tool; MCP does not expose
471
+ one tool for every SDK conversation or event-listener method.
package/SECURITY.md CHANGED
@@ -20,3 +20,13 @@ Do not open public issues for vulnerabilities. Email security reports to `hello@
20
20
  - Keep `THALOVANT_ALLOW_SHARED_CREDENTIALS=false` for multi-user deployments.
21
21
  - Set exact `MCP_HTTP_ALLOWED_HOSTS` and `MCP_HTTP_ALLOWED_ORIGINS`.
22
22
  - Send audit logs to your log pipeline with `MCP_AUDIT_LOG=stderr` or `MCP_AUDIT_LOG_FILE`.
23
+
24
+
25
+ Server-managed Thalovant tokens and password login are bound to their configured
26
+ control-plane origin. Tool-provided `apiUrl` overrides with a different origin
27
+ are rejected before any request is sent. Configure a custom API origin together
28
+ with its credentials; per-principal credentials follow the same rule.
29
+
30
+ Control-plane credentials require HTTPS, with an explicit loopback HTTP exception
31
+ for local development (`localhost`, `127.0.0.1`, `[::1]`). API redirects are rejected,
32
+ including redirects that would preserve password-login request bodies.
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ import { withRuntimeLease } from "./runtime-lease.js";
2
3
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
4
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
5
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
@@ -77,7 +78,7 @@ const controlPlaneSchema = {
77
78
  .string()
78
79
  .url()
79
80
  .optional()
80
- .describe("Control-plane base URL. Defaults to https://api.thalovant.com."),
81
+ .describe("Control-plane base URL. Configured credentials may only be used on their configured origin; defaults to https://api.thalovant.com."),
81
82
  };
82
83
  const contextSchema = z
83
84
  .object({
@@ -567,20 +568,40 @@ function redactSecrets(value) {
567
568
  }
568
569
  return output;
569
570
  }
571
+ /** Keep server-managed credentials on their configured control-plane origin. */
570
572
  async function createControlPlane(options) {
571
573
  const principal = currentPrincipal();
572
574
  const credential = await credentialForPrincipal(principal);
573
575
  const canUseShared = allowSharedThalovantCredentials(principal);
574
- const apiUrl = options.apiUrl ?? credential?.control?.apiUrl ?? (canUseShared ? process.env.THALOVANT_API_URL : undefined) ?? DEFAULT_CONTROL_API_URL;
576
+ const configuredApiUrl = credential?.control?.apiUrl ?? (canUseShared ? process.env.THALOVANT_API_URL : undefined) ?? DEFAULT_CONTROL_API_URL;
577
+ const apiUrl = options.apiUrl ?? configuredApiUrl;
575
578
  const accessToken = credential?.control?.accessToken ??
576
579
  (canUseShared ? process.env.THALOVANT_API_TOKEN ?? process.env.THALOVANT_ACCESS_TOKEN : undefined);
580
+ const email = canUseShared ? process.env.THALOVANT_EMAIL : undefined;
581
+ const password = canUseShared ? process.env.THALOVANT_PASSWORD : undefined;
582
+ if (accessToken || (email && password)) {
583
+ let endpoint;
584
+ let configuredEndpoint;
585
+ try {
586
+ endpoint = new URL(apiUrl);
587
+ configuredEndpoint = new URL(configuredApiUrl);
588
+ }
589
+ catch {
590
+ throw new Error("The configured control-plane API URL is invalid.");
591
+ }
592
+ if (endpoint.origin !== configuredEndpoint.origin) {
593
+ throw new Error("apiUrl must use the configured Thalovant credential origin.");
594
+ }
595
+ const loopback = ["localhost", "127.0.0.1", "[::1]"].includes(endpoint.hostname);
596
+ if (endpoint.protocol !== "https:" && !(endpoint.protocol === "http:" && loopback)) {
597
+ throw new Error("Credential-bearing control-plane requests require HTTPS (except explicit loopback HTTP).");
598
+ }
599
+ }
577
600
  const api = new ThalovantControlPlane(apiUrl, {
578
601
  accessToken,
579
602
  userAgent: `thalovant-mcp/${VERSION}`,
580
603
  });
581
604
  if (!accessToken && canUseShared) {
582
- const email = process.env.THALOVANT_EMAIL;
583
- const password = process.env.THALOVANT_PASSWORD;
584
605
  if (email && password) {
585
606
  await api.login(email, password, { scope: options.loginScope ?? process.env.THALOVANT_SCOPE });
586
607
  }
@@ -601,28 +622,26 @@ function controlPlaneAuthModeFromEnv() {
601
622
  return "email-password";
602
623
  return "none";
603
624
  }
604
- async function createRuntimeClient(options) {
605
- const protocol = options.protocol ?? "wss";
625
+ async function createRuntimeIdentity(options) {
606
626
  const principal = currentPrincipal();
607
627
  const credential = await credentialForPrincipal(principal);
608
628
  const runtimeCredential = credential?.runtime;
609
629
  const canUseShared = allowSharedThalovantCredentials(principal);
610
630
  const canUseToolPaths = canUseShared || parseBool(process.env.MCP_HTTP_ALLOW_CLIENT_CREDENTIAL_PATHS, false);
611
631
  if (runtimeCredential?.identity) {
612
- return new ThalovantClient(new ThalovantIdentity(runtimeCredential.identity), { protocol });
632
+ return new ThalovantIdentity(runtimeCredential.identity);
613
633
  }
614
634
  if (runtimeCredential?.identityFile) {
615
- return ThalovantClient.fromIdentityFile(runtimeCredential.identityFile, { protocol });
635
+ return ThalovantIdentity.fromFile(runtimeCredential.identityFile);
616
636
  }
617
637
  if (runtimeCredential?.configPath || runtimeCredential?.profile) {
618
- return ThalovantClient.fromConfig({
638
+ return ThalovantIdentity.fromConfig({
619
639
  path: runtimeCredential.configPath,
620
640
  profile: runtimeCredential.profile,
621
- protocol,
622
641
  });
623
642
  }
624
643
  if (runtimeCredential?.fromEnv && canUseShared) {
625
- return ThalovantClient.fromEnv({ protocol });
644
+ return ThalovantIdentity.fromEnv();
626
645
  }
627
646
  if (isRemotePrincipal(principal) && !canUseShared && !canUseToolPaths) {
628
647
  throw new Error("No per-principal Thalovant runtime identity is configured for this authenticated MCP principal.");
@@ -631,57 +650,31 @@ async function createRuntimeClient(options) {
631
650
  throw new Error("Runtime identity path/config arguments are disabled for remote MCP principals.");
632
651
  }
633
652
  if (options.identityFile) {
634
- return ThalovantClient.fromIdentityFile(options.identityFile, { protocol });
653
+ return ThalovantIdentity.fromFile(options.identityFile);
635
654
  }
636
655
  if (options.configPath || options.profile) {
637
- return ThalovantClient.fromConfig({
656
+ return ThalovantIdentity.fromConfig({
638
657
  path: options.configPath,
639
658
  profile: options.profile ?? process.env.THALOVANT_PROFILE,
640
- protocol,
641
659
  });
642
660
  }
643
661
  if (!canUseShared) {
644
662
  throw new Error("No per-principal Thalovant runtime identity is configured for this authenticated MCP principal.");
645
663
  }
646
664
  if (options.fromEnv || process.env.THALOVANT_ACCESS_KEY) {
647
- return ThalovantClient.fromEnv({ protocol });
665
+ return ThalovantIdentity.fromEnv();
648
666
  }
649
- return ThalovantClient.fromConfig({
667
+ return ThalovantIdentity.fromConfig({
650
668
  profile: process.env.THALOVANT_PROFILE,
651
- protocol,
652
669
  });
653
670
  }
654
- // The hub HTTP/MQTT queues and Noise sessions are keyed by client identity.
655
- // A second tool call must finish after the first closes that identity's session.
656
- const runtimeLeases = new Map();
657
- /**
658
- * Hold an identity lease through both the tool action and client cleanup.
659
- * This keeps HTTP/MQTT admissions from replacing another call's Noise session;
660
- * a failed action still closes its client before the next lease starts.
661
- */
671
+ /** Use one identity lease through the action and actual retained cleanup. */
662
672
  async function withRuntimeClient(options, run) {
663
- const client = await createRuntimeClient(options);
673
+ const identity = await createRuntimeIdentity(options);
664
674
  const key = createHash("sha256").update(JSON.stringify([
665
- client.identity.endpointBase(), client.identity.accessKey,
675
+ identity.endpointBase(), identity.accessKey,
666
676
  ])).digest("hex");
667
- const previous = runtimeLeases.get(key) ?? Promise.resolve();
668
- let release;
669
- const current = new Promise(resolve => { release = resolve; });
670
- runtimeLeases.set(key, current);
671
- await previous;
672
- try {
673
- return await run(client);
674
- }
675
- finally {
676
- try {
677
- await client.close();
678
- }
679
- finally {
680
- release();
681
- if (runtimeLeases.get(key) === current)
682
- runtimeLeases.delete(key);
683
- }
684
- }
677
+ return withRuntimeLease(key, () => new ThalovantClient(identity, { protocol: options.protocol ?? "wss" }), run);
685
678
  }
686
679
  function summarizeReply(reply) {
687
680
  const displayItems = reply.displayItems({ maxTextChars: 1_000 });
@@ -1214,6 +1207,16 @@ export function createServer() {
1214
1207
  ensureAuthenticated(api);
1215
1208
  return jsonContent(redactSecrets(await api.getHub(hubId)));
1216
1209
  });
1210
+ registerThalovantTool(server, "thalovant_get_operation", {
1211
+ title: "Get Provisioning Operation",
1212
+ description: "Read the status of an asynchronous control-plane operation returned by provisioning.",
1213
+ inputSchema: { ...controlPlaneSchema, operationId: z.string().min(1).max(500) },
1214
+ annotations: { readOnlyHint: true, openWorldHint: true },
1215
+ }, async ({ operationId, ...auth }) => {
1216
+ const api = await createControlPlane(auth);
1217
+ ensureAuthenticated(api);
1218
+ return jsonContent(redactSecrets(await api.getOperation(operationId)));
1219
+ });
1217
1220
  registerThalovantTool(server, "thalovant_create_client_identity", {
1218
1221
  title: "Create Client Identity",
1219
1222
  description: "Create a Thalovant client identity for a hub. The identity is secret; output is redacted unless savePath is used.",
@@ -1309,6 +1312,24 @@ export function createServer() {
1309
1312
  return jsonContent(redactSecrets(client.healthcheck()));
1310
1313
  });
1311
1314
  });
1315
+ registerThalovantTool(server, "thalovant_intent_inventory", {
1316
+ title: "Runtime Intent Inventory",
1317
+ description: "Discover a hub's registered intents, language examples and fallback skills using its runtime identity. Unknown fallback support remains distinct from a known empty list; answerability is a conservative hint.",
1318
+ inputSchema: {
1319
+ ...runtimeAuthSchema,
1320
+ languages: z.array(z.string().trim().min(1).max(100)).min(1).max(20).default(["en-us"]),
1321
+ describe: z.boolean().default(true),
1322
+ fallback: z.boolean().default(true).describe("Try engine manifests if the intent listing is refused or unanswered."),
1323
+ timeoutMs: z.number().int().min(1_000).max(MAX_TIMEOUT_MS).default(DEFAULT_TIMEOUT_MS).describe("Per-query/batch timeout; the optional fallback-skill probe is capped at 1500ms."),
1324
+ },
1325
+ annotations: { readOnlyHint: true, openWorldHint: true },
1326
+ }, async ({ languages, describe, fallback, timeoutMs, ...runtime }) => withRuntimeClient(runtime, async (client) => {
1327
+ const inventory = await client.intents(languages, { describe, fallback, timeoutMs: clampTimeout(timeoutMs) });
1328
+ return jsonContent(redactSecrets({
1329
+ ...inventory.asObject(),
1330
+ may_answer: Object.fromEntries(inventory.languages.map(lang => [lang, inventory.mayAnswer(lang)])),
1331
+ }));
1332
+ }));
1312
1333
  registerThalovantTool(server, "thalovant_ask", {
1313
1334
  title: "Ask Hub",
1314
1335
  description: "Send one text request to a Thalovant hub using a saved identity and return the normalized reply.",
@@ -1344,6 +1365,30 @@ export function createServer() {
1344
1365
  return jsonContent(redactSecrets(summarizeReply(reply)));
1345
1366
  });
1346
1367
  });
1368
+ registerThalovantTool(server, "thalovant_query", {
1369
+ title: "Query Hub",
1370
+ description: "Send a routed HiveMind query using a saved identity and return the normalized reply. The hub may cascade the query according to its routing policy; a query can trigger actions.",
1371
+ inputSchema: {
1372
+ ...runtimeAuthSchema,
1373
+ text: z.string().trim().min(1).max(20_000),
1374
+ timeoutMs: z.number().int().min(1_000).max(MAX_TIMEOUT_MS).default(DEFAULT_TIMEOUT_MS),
1375
+ lang: z.string().min(1).optional(),
1376
+ sessionId: z.string().min(1).optional(),
1377
+ requestId: z.string().min(1).optional(),
1378
+ queryId: z.string().min(1).optional(),
1379
+ context: contextSchema,
1380
+ replySettleMs: z.number().int().min(0).max(10_000).optional(),
1381
+ },
1382
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
1383
+ }, async ({ text, timeoutMs, lang, sessionId, requestId, queryId, context, replySettleMs, ...runtime }) => {
1384
+ return withRuntimeClient(runtime, async (client) => {
1385
+ const reply = await client.query(text, {
1386
+ timeoutMs: clampTimeout(timeoutMs), lang, sessionId, requestId, queryId,
1387
+ context: context ? buildClientContext({}, context) : undefined, replySettleMs,
1388
+ });
1389
+ return jsonContent(redactSecrets(summarizeReply(reply)));
1390
+ });
1391
+ });
1347
1392
  registerThalovantTool(server, "thalovant_send_action", {
1348
1393
  title: "Send Action",
1349
1394
  description: "Send a structured action payload to a Thalovant hub.",
@@ -0,0 +1,8 @@
1
+ /** Runtime identities admit one Noise session at a time across MCP tool calls. */
2
+ interface RuntimeClosable {
3
+ close(): Promise<void>;
4
+ waitForClosed(): Promise<void>;
5
+ }
6
+ /** A caller timeout never releases an identity before actual cleanup settles. */
7
+ export declare function withRuntimeLease<T, C extends RuntimeClosable>(key: string, createClient: () => C, run: (client: C) => Promise<T>, acquireTimeoutMs?: number): Promise<T>;
8
+ export {};
@@ -0,0 +1,64 @@
1
+ const leases = new Map();
2
+ /** A caller timeout never releases an identity before actual cleanup settles. */
3
+ export async function withRuntimeLease(key, createClient, run, acquireTimeoutMs = 6000) {
4
+ if (!Number.isFinite(acquireTimeoutMs) || acquireTimeoutMs <= 0) {
5
+ throw new Error("Runtime identity acquisition deadline expired.");
6
+ }
7
+ const previous = leases.get(key) ?? Promise.resolve();
8
+ let release;
9
+ let fail;
10
+ const current = new Promise((resolve, reject) => { release = resolve; fail = reject; });
11
+ // A failed cleanup poisons this identity's barrier. Observe it even when no
12
+ // later caller arrives; later tools fail instead of replacing that session.
13
+ void current.catch(() => undefined);
14
+ leases.set(key, current);
15
+ const releaseIdentity = () => {
16
+ release();
17
+ if (leases.get(key) === current)
18
+ leases.delete(key);
19
+ };
20
+ const failIdentity = () => {
21
+ fail(new Error("Previous runtime session cleanup failed; this identity remains unavailable."));
22
+ };
23
+ let timer;
24
+ try {
25
+ await Promise.race([
26
+ previous,
27
+ new Promise((_, reject) => {
28
+ timer = setTimeout(() => reject(new Error("Runtime identity acquisition deadline expired; previous session cleanup is still pending.")), acquireTimeoutMs);
29
+ }),
30
+ ]);
31
+ }
32
+ catch (error) {
33
+ // An expired waiter never runs or closes its unused client. Its place in
34
+ // the chain remains occupied until the prior owner actually retires.
35
+ void previous.then(releaseIdentity, failIdentity);
36
+ throw error;
37
+ }
38
+ finally {
39
+ clearTimeout(timer);
40
+ }
41
+ let client;
42
+ try {
43
+ client = createClient();
44
+ }
45
+ catch (error) {
46
+ releaseIdentity();
47
+ throw error;
48
+ }
49
+ try {
50
+ return await run(client);
51
+ }
52
+ finally {
53
+ let closing;
54
+ try {
55
+ closing = client.close();
56
+ }
57
+ catch (error) {
58
+ failIdentity();
59
+ throw error;
60
+ }
61
+ void client.waitForClosed().then(releaseIdentity, failIdentity);
62
+ await closing;
63
+ }
64
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thalovant/mcp-server",
3
- "version": "0.1.19",
3
+ "version": "0.1.20",
4
4
  "description": "Stdio and Streamable HTTP MCP server for Thalovant control-plane and hub runtime APIs.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -64,7 +64,7 @@
64
64
  },
65
65
  "dependencies": {
66
66
  "@modelcontextprotocol/sdk": "^1.30.0",
67
- "@thalovant/sdk": "^0.3.5",
67
+ "@thalovant/sdk": "^0.3.9",
68
68
  "jose": "^6.2.8",
69
69
  "zod": "^3.25.76"
70
70
  },
@@ -74,6 +74,6 @@
74
74
  "devDependencies": {
75
75
  "@types/node": "^22.18.12",
76
76
  "typescript": "^5.9.3",
77
- "vitest": "^3.2.4"
77
+ "vitest": "^4.1.11"
78
78
  }
79
79
  }
package/server.json CHANGED
@@ -7,19 +7,19 @@
7
7
  "url": "https://github.com/thalovant/thalovant-mcp",
8
8
  "source": "github"
9
9
  },
10
- "version": "0.1.19",
10
+ "version": "0.1.20",
11
11
  "packages": [
12
12
  {
13
13
  "registryType": "npm",
14
14
  "identifier": "@thalovant/mcp-server",
15
- "version": "0.1.19",
15
+ "version": "0.1.20",
16
16
  "transport": {
17
17
  "type": "stdio"
18
18
  }
19
19
  },
20
20
  {
21
21
  "registryType": "oci",
22
- "identifier": "ghcr.io/thalovant/thalovant-mcp:0.1.19",
22
+ "identifier": "ghcr.io/thalovant/thalovant-mcp:0.1.20",
23
23
  "transport": {
24
24
  "type": "streamable-http",
25
25
  "url": "http://127.0.0.1:3000/mcp"