@thalovant/mcp-server 0.1.7 → 0.1.9
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/.env.example +3 -0
- package/CHANGELOG.md +13 -0
- package/README.md +41 -3
- package/dist/index.js +47 -21
- package/examples/claude_desktop_config.json +1 -0
- package/package.json +2 -2
- package/server.json +3 -3
package/.env.example
CHANGED
|
@@ -3,6 +3,9 @@ THALOVANT_PROFILE=prod
|
|
|
3
3
|
THALOVANT_API_URL=https://api.thalovant.com
|
|
4
4
|
|
|
5
5
|
# Use one control-plane auth option for private/admin tools.
|
|
6
|
+
# Recommended: a scoped, revocable API token (dashboard or device flow).
|
|
7
|
+
# Minimum scopes for the full tool surface: hubs:read, clients:write, memory:read, memory:write.
|
|
8
|
+
# THALOVANT_API_TOKEN=
|
|
6
9
|
# THALOVANT_ACCESS_TOKEN=
|
|
7
10
|
# THALOVANT_EMAIL=
|
|
8
11
|
# THALOVANT_PASSWORD=
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.9
|
|
4
|
+
|
|
5
|
+
- Added first-class scoped API token auth for the control plane: set `THALOVANT_API_TOKEN` and the server authenticates with the token directly and never calls the login endpoint. Precedence is `THALOVANT_API_TOKEN`, then `THALOVANT_ACCESS_TOKEN`, then the `THALOVANT_EMAIL`/`THALOVANT_PASSWORD` login fallback.
|
|
6
|
+
- `thalovant_config_status` now reports the active control-plane auth mode (`controlPlaneAuthMode`: `api-token`, `access-token`, `email-password`, or `none`) and `hasApiToken`, without revealing token values.
|
|
7
|
+
- Documented API tokens as the recommended auth mode for AI and CI use, including the minimum token scopes for the full control-plane tool surface: `hubs:read`, `clients:write`, `memory:read`, `memory:write`.
|
|
8
|
+
- Clarified the unauthenticated-tool error to name `THALOVANT_API_TOKEN` as the recommended option.
|
|
9
|
+
|
|
10
|
+
## 0.1.8
|
|
11
|
+
|
|
12
|
+
- Tightened memory tool input validation to match the control-plane API exactly: `title` max 160, `content` max 4096, `source` max 64, `consentScope` max 128, `consentVersion` max 64, `retentionPolicy` max 64 on create/update, and list `query` max 240, so invalid values fail with a clear client-side error instead of an opaque API 422.
|
|
13
|
+
- Capped `thalovant_list_public_hubs` `limit` at 48 to match the public hubs endpoint, which silently clamps larger page sizes; authenticated `thalovant_list_hubs` keeps its limit of 100.
|
|
14
|
+
- Corrected `thalovant_get_hub` and `thalovant_create_client_identity` descriptions: the authenticated hub routes require a hub UUID; slugs are only accepted by the public hub tools.
|
|
15
|
+
|
|
3
16
|
## 0.1.7
|
|
4
17
|
|
|
5
18
|
- Updated the MCP transport SDK to 1.30.0, removing the affected Hono adapter dependency range.
|
package/README.md
CHANGED
|
@@ -29,13 +29,43 @@ npm run build
|
|
|
29
29
|
|
|
30
30
|
Node.js 20 or newer is required.
|
|
31
31
|
|
|
32
|
-
##
|
|
32
|
+
## Control-Plane Auth
|
|
33
33
|
|
|
34
34
|
Public hub discovery does not need Thalovant credentials. Private control-plane tools and runtime hub tools read credentials only from the MCP server environment or server-side principal credential files. Do not pass API tokens or passwords through chat or tool arguments.
|
|
35
35
|
|
|
36
|
+
The server selects control-plane auth in this order:
|
|
37
|
+
|
|
38
|
+
1. `THALOVANT_API_TOKEN` — a scoped Thalovant API token. Recommended.
|
|
39
|
+
2. `THALOVANT_ACCESS_TOKEN` — a pre-issued session access token.
|
|
40
|
+
3. `THALOVANT_EMAIL` + `THALOVANT_PASSWORD` — interactive-account login fallback.
|
|
41
|
+
|
|
42
|
+
When a token is set, the server never calls the login endpoint. `thalovant_config_status` reports the active mode as `controlPlaneAuthMode` without revealing token values.
|
|
43
|
+
|
|
44
|
+
### API Tokens (Recommended For AI Agents And CI)
|
|
45
|
+
|
|
46
|
+
Scoped API tokens are the right credential for AI and automation use: they are minted from the Thalovant dashboard (or through the device flow), carry only the scopes you grant, can be revoked individually, and never involve your account password or MFA. Tokens start with `tvpat_`.
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
export THALOVANT_API_TOKEN="tvpat_..."
|
|
50
|
+
export THALOVANT_API_URL="https://api.thalovant.com"
|
|
51
|
+
|
|
52
|
+
npm start
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Minimum scopes for the full control-plane tool surface:
|
|
56
|
+
|
|
57
|
+
| Scope | Used by |
|
|
58
|
+
|-------|---------|
|
|
59
|
+
| `hubs:read` | `thalovant_list_hubs`, `thalovant_get_hub`, `thalovant_get_analytics_overview`, and the hub lookup inside `thalovant_create_client_identity` |
|
|
60
|
+
| `clients:write` | `thalovant_create_client_identity` (`POST /v1/clients`) |
|
|
61
|
+
| `memory:read` | `thalovant_list_memory_items`, `thalovant_get_memory_summary`, `thalovant_get_memory_item` |
|
|
62
|
+
| `memory:write` | `thalovant_create_memory_item`, `thalovant_update_memory_item`, `thalovant_delete_memory_item` |
|
|
63
|
+
|
|
64
|
+
Grant fewer scopes for narrower deployments: a read-only assistant needs only `hubs:read` and `memory:read`. `thalovant_get_analytics_overview` with `admin: true` additionally requires an admin account with `admin:analytics`, which API tokens for regular use should not carry. Runtime hub tools (`thalovant_ask`, `thalovant_send_action`, and friends) use Thalovant client identities, not control-plane tokens.
|
|
65
|
+
|
|
66
|
+
### Login Fallback
|
|
67
|
+
|
|
36
68
|
```bash
|
|
37
|
-
export THALOVANT_ACCESS_TOKEN="..."
|
|
38
|
-
# or
|
|
39
69
|
export THALOVANT_EMAIL="you@example.com"
|
|
40
70
|
export THALOVANT_PASSWORD="..."
|
|
41
71
|
|
|
@@ -45,6 +75,10 @@ export THALOVANT_API_URL="https://api.thalovant.com"
|
|
|
45
75
|
npm start
|
|
46
76
|
```
|
|
47
77
|
|
|
78
|
+
If neither a token nor email/password is configured, authenticated control-plane tools fail with a clear error naming the supported options.
|
|
79
|
+
|
|
80
|
+
## Local Stdio
|
|
81
|
+
|
|
48
82
|
The server speaks MCP over stdio and does not write logs to stdout.
|
|
49
83
|
|
|
50
84
|
Runtime hub tools load local identities in this order:
|
|
@@ -208,6 +242,8 @@ MCP_HTTP_TRUST_PROXY=false
|
|
|
208
242
|
|
|
209
243
|
## Claude Desktop
|
|
210
244
|
|
|
245
|
+
Recommended: authenticate with a scoped API token so the MCP config never contains your account password.
|
|
246
|
+
|
|
211
247
|
```json
|
|
212
248
|
{
|
|
213
249
|
"mcpServers": {
|
|
@@ -215,6 +251,7 @@ MCP_HTTP_TRUST_PROXY=false
|
|
|
215
251
|
"command": "node",
|
|
216
252
|
"args": ["/home/goldyfruit/Development/Thalovant/mcp/dist/index.js"],
|
|
217
253
|
"env": {
|
|
254
|
+
"THALOVANT_API_TOKEN": "tvpat_...",
|
|
218
255
|
"THALOVANT_PROFILE": "prod"
|
|
219
256
|
}
|
|
220
257
|
}
|
|
@@ -233,6 +270,7 @@ Use the same stdio command in your MCP client config:
|
|
|
233
270
|
"command": "node",
|
|
234
271
|
"args": ["/home/goldyfruit/Development/Thalovant/mcp/dist/index.js"],
|
|
235
272
|
"env": {
|
|
273
|
+
"THALOVANT_API_TOKEN": "tvpat_...",
|
|
236
274
|
"THALOVANT_PROFILE": "prod"
|
|
237
275
|
}
|
|
238
276
|
}
|
package/dist/index.js
CHANGED
|
@@ -14,10 +14,11 @@ import { mkdir } from "node:fs/promises";
|
|
|
14
14
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
15
15
|
import { createRemoteJWKSet, jwtVerify } from "jose";
|
|
16
16
|
import { z } from "zod";
|
|
17
|
-
const VERSION = "0.1.
|
|
17
|
+
const VERSION = "0.1.9";
|
|
18
18
|
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
19
19
|
const MAX_TIMEOUT_MS = 300_000;
|
|
20
20
|
const MAX_LIMIT = 100;
|
|
21
|
+
const MAX_PUBLIC_HUBS_LIMIT = 48;
|
|
21
22
|
const DEFAULT_HTTP_BODY_LIMIT_BYTES = 1_048_576;
|
|
22
23
|
const DEFAULT_HTTP_RATE_LIMIT_MAX = 120;
|
|
23
24
|
const DEFAULT_HTTP_RATE_LIMIT_WINDOW_MS = 60_000;
|
|
@@ -48,7 +49,14 @@ const SECRET_KEYS = [
|
|
|
48
49
|
"secret",
|
|
49
50
|
];
|
|
50
51
|
const protocolSchema = z.enum(["wss", "https", "mqtt"]);
|
|
51
|
-
const limitSchema = z.number().int().min(1).max(MAX_LIMIT).default(25);
|
|
52
|
+
const limitSchema = z.number().int().min(1).max(MAX_LIMIT).default(25).describe("Page size, 1-100.");
|
|
53
|
+
const publicHubsLimitSchema = z
|
|
54
|
+
.number()
|
|
55
|
+
.int()
|
|
56
|
+
.min(1)
|
|
57
|
+
.max(MAX_PUBLIC_HUBS_LIMIT)
|
|
58
|
+
.default(25)
|
|
59
|
+
.describe("Page size, 1-48. The public hubs endpoint caps page size at 48.");
|
|
52
60
|
const optionalCursorSchema = z.string().min(1).optional();
|
|
53
61
|
const jsonRecordSchema = z.record(z.unknown());
|
|
54
62
|
const controlPlaneSchema = {
|
|
@@ -415,7 +423,8 @@ async function createControlPlane(options) {
|
|
|
415
423
|
const credential = await credentialForPrincipal(principal);
|
|
416
424
|
const canUseShared = allowSharedThalovantCredentials(principal);
|
|
417
425
|
const apiUrl = options.apiUrl ?? credential?.control?.apiUrl ?? (canUseShared ? process.env.THALOVANT_API_URL : undefined) ?? DEFAULT_CONTROL_API_URL;
|
|
418
|
-
const accessToken = credential?.control?.accessToken ??
|
|
426
|
+
const accessToken = credential?.control?.accessToken ??
|
|
427
|
+
(canUseShared ? process.env.THALOVANT_API_TOKEN ?? process.env.THALOVANT_ACCESS_TOKEN : undefined);
|
|
419
428
|
const api = new ThalovantControlPlane(apiUrl, {
|
|
420
429
|
accessToken,
|
|
421
430
|
userAgent: `thalovant-mcp/${VERSION}`,
|
|
@@ -431,9 +440,18 @@ async function createControlPlane(options) {
|
|
|
431
440
|
}
|
|
432
441
|
function ensureAuthenticated(api) {
|
|
433
442
|
if (!api.accessToken) {
|
|
434
|
-
throw new Error("This tool requires Thalovant API auth. Configure THALOVANT_ACCESS_TOKEN, THALOVANT_EMAIL/THALOVANT_PASSWORD, or per-principal Thalovant credentials for this MCP principal.");
|
|
443
|
+
throw new Error("This tool requires Thalovant API auth. Configure THALOVANT_API_TOKEN (recommended: a scoped, revocable API token), THALOVANT_ACCESS_TOKEN, THALOVANT_EMAIL/THALOVANT_PASSWORD, or per-principal Thalovant credentials for this MCP principal.");
|
|
435
444
|
}
|
|
436
445
|
}
|
|
446
|
+
function controlPlaneAuthModeFromEnv() {
|
|
447
|
+
if (process.env.THALOVANT_API_TOKEN)
|
|
448
|
+
return "api-token";
|
|
449
|
+
if (process.env.THALOVANT_ACCESS_TOKEN)
|
|
450
|
+
return "access-token";
|
|
451
|
+
if (process.env.THALOVANT_EMAIL && process.env.THALOVANT_PASSWORD)
|
|
452
|
+
return "email-password";
|
|
453
|
+
return "none";
|
|
454
|
+
}
|
|
437
455
|
async function createRuntimeClient(options) {
|
|
438
456
|
const protocol = options.protocol ?? "wss";
|
|
439
457
|
const principal = currentPrincipal();
|
|
@@ -888,6 +906,8 @@ export function createServer() {
|
|
|
888
906
|
},
|
|
889
907
|
}, async () => jsonContent({
|
|
890
908
|
apiUrl: process.env.THALOVANT_API_URL ?? DEFAULT_CONTROL_API_URL,
|
|
909
|
+
controlPlaneAuthMode: controlPlaneAuthModeFromEnv(),
|
|
910
|
+
hasApiToken: Boolean(process.env.THALOVANT_API_TOKEN),
|
|
891
911
|
hasAccessToken: Boolean(process.env.THALOVANT_ACCESS_TOKEN),
|
|
892
912
|
hasEmailPassword: Boolean(process.env.THALOVANT_EMAIL && process.env.THALOVANT_PASSWORD),
|
|
893
913
|
profile: process.env.THALOVANT_PROFILE,
|
|
@@ -897,9 +917,9 @@ export function createServer() {
|
|
|
897
917
|
}));
|
|
898
918
|
registerThalovantTool(server, "thalovant_list_public_hubs", {
|
|
899
919
|
title: "List Public Hubs",
|
|
900
|
-
description: "List Thalovant public hubs. This read-only discovery call does not require authentication.",
|
|
920
|
+
description: "List Thalovant public hubs. This read-only discovery call does not require authentication. Page size is capped at 48.",
|
|
901
921
|
inputSchema: {
|
|
902
|
-
limit:
|
|
922
|
+
limit: publicHubsLimitSchema,
|
|
903
923
|
cursor: optionalCursorSchema,
|
|
904
924
|
apiUrl: controlPlaneSchema.apiUrl,
|
|
905
925
|
},
|
|
@@ -946,10 +966,13 @@ export function createServer() {
|
|
|
946
966
|
});
|
|
947
967
|
registerThalovantTool(server, "thalovant_get_hub", {
|
|
948
968
|
title: "Get Hub",
|
|
949
|
-
description: "Fetch one authenticated Thalovant hub by
|
|
969
|
+
description: "Fetch one authenticated Thalovant hub by UUID.",
|
|
950
970
|
inputSchema: {
|
|
951
971
|
...controlPlaneSchema,
|
|
952
|
-
hubId: z
|
|
972
|
+
hubId: z
|
|
973
|
+
.string()
|
|
974
|
+
.min(1)
|
|
975
|
+
.describe("Hub UUID. This authenticated route rejects slugs; slugs are only accepted by the public hub tools (thalovant_get_public_hub)."),
|
|
953
976
|
},
|
|
954
977
|
annotations: {
|
|
955
978
|
readOnlyHint: true,
|
|
@@ -965,7 +988,10 @@ export function createServer() {
|
|
|
965
988
|
description: "Create a Thalovant client identity for a hub. The identity is secret; output is redacted unless savePath is used.",
|
|
966
989
|
inputSchema: {
|
|
967
990
|
...controlPlaneSchema,
|
|
968
|
-
hubId: z
|
|
991
|
+
hubId: z
|
|
992
|
+
.string()
|
|
993
|
+
.min(1)
|
|
994
|
+
.describe("Hub UUID. This authenticated route rejects slugs; slugs are only accepted by the public hub tools (thalovant_get_public_hub)."),
|
|
969
995
|
name: z.string().min(1).max(128),
|
|
970
996
|
siteId: z.string().min(1).optional(),
|
|
971
997
|
ownerId: z.string().min(1).optional(),
|
|
@@ -1259,7 +1285,7 @@ export function createServer() {
|
|
|
1259
1285
|
kind: z.enum(["note", "preference", "fact"]).optional(),
|
|
1260
1286
|
ownerId: z.string().min(1).optional(),
|
|
1261
1287
|
hubId: z.string().min(1).optional(),
|
|
1262
|
-
query: z.string().min(1).optional(),
|
|
1288
|
+
query: z.string().min(1).max(240).optional(),
|
|
1263
1289
|
includeDeleted: z.boolean().optional(),
|
|
1264
1290
|
includeExpired: z.boolean().optional(),
|
|
1265
1291
|
limit: limitSchema,
|
|
@@ -1314,16 +1340,16 @@ export function createServer() {
|
|
|
1314
1340
|
...controlPlaneSchema,
|
|
1315
1341
|
scope: z.enum(["personal", "workspace", "hub"]).optional(),
|
|
1316
1342
|
kind: z.enum(["note", "preference", "fact"]).optional(),
|
|
1317
|
-
title: z.string().max(
|
|
1318
|
-
content: z.string().min(1).max(
|
|
1343
|
+
title: z.string().max(160).nullable().optional(),
|
|
1344
|
+
content: z.string().min(1).max(4_096),
|
|
1319
1345
|
tags: z.array(z.string().min(1)).optional(),
|
|
1320
1346
|
ownerId: z.string().min(1).optional(),
|
|
1321
1347
|
hubId: z.string().min(1).optional(),
|
|
1322
|
-
source: z.string().min(1).optional(),
|
|
1348
|
+
source: z.string().min(1).max(64).optional(),
|
|
1323
1349
|
metadata: jsonRecordSchema.optional(),
|
|
1324
|
-
consentScope: z.string().min(1).optional(),
|
|
1325
|
-
consentVersion: z.string().nullable().optional(),
|
|
1326
|
-
retentionPolicy: z.string().min(1).optional(),
|
|
1350
|
+
consentScope: z.string().min(1).max(128).optional(),
|
|
1351
|
+
consentVersion: z.string().max(64).nullable().optional(),
|
|
1352
|
+
retentionPolicy: z.string().min(1).max(64).optional(),
|
|
1327
1353
|
expiresAt: z.string().nullable().optional(),
|
|
1328
1354
|
},
|
|
1329
1355
|
annotations: {
|
|
@@ -1345,13 +1371,13 @@ export function createServer() {
|
|
|
1345
1371
|
...controlPlaneSchema,
|
|
1346
1372
|
memoryId: z.string().min(1),
|
|
1347
1373
|
kind: z.enum(["note", "preference", "fact"]).optional(),
|
|
1348
|
-
title: z.string().max(
|
|
1349
|
-
content: z.string().min(1).max(
|
|
1374
|
+
title: z.string().max(160).nullable().optional(),
|
|
1375
|
+
content: z.string().min(1).max(4_096).optional(),
|
|
1350
1376
|
tags: z.array(z.string().min(1)).optional(),
|
|
1351
1377
|
metadata: jsonRecordSchema.optional(),
|
|
1352
|
-
consentScope: z.string().min(1).optional(),
|
|
1353
|
-
consentVersion: z.string().nullable().optional(),
|
|
1354
|
-
retentionPolicy: z.string().min(1).optional(),
|
|
1378
|
+
consentScope: z.string().min(1).max(128).optional(),
|
|
1379
|
+
consentVersion: z.string().max(64).nullable().optional(),
|
|
1380
|
+
retentionPolicy: z.string().min(1).max(64).optional(),
|
|
1355
1381
|
expiresAt: z.string().nullable().optional(),
|
|
1356
1382
|
clearExpiresAt: z.boolean().optional(),
|
|
1357
1383
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thalovant/mcp-server",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
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",
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"zod": "^3.25.76"
|
|
70
70
|
},
|
|
71
71
|
"overrides": {
|
|
72
|
-
"fast-uri": "3.1.
|
|
72
|
+
"fast-uri": "3.1.5"
|
|
73
73
|
},
|
|
74
74
|
"devDependencies": {
|
|
75
75
|
"@types/node": "^22.18.12",
|
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.
|
|
10
|
+
"version": "0.1.9",
|
|
11
11
|
"packages": [
|
|
12
12
|
{
|
|
13
13
|
"registryType": "npm",
|
|
14
14
|
"identifier": "@thalovant/mcp-server",
|
|
15
|
-
"version": "0.1.
|
|
15
|
+
"version": "0.1.9",
|
|
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.
|
|
22
|
+
"identifier": "ghcr.io/thalovant/thalovant-mcp:0.1.9",
|
|
23
23
|
"transport": {
|
|
24
24
|
"type": "streamable-http",
|
|
25
25
|
"url": "http://127.0.0.1:3000/mcp"
|