@frontmcp/plugin-dashboard 1.9.5 → 1.9.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +64 -27
- package/app/dashboard.app.d.ts +7 -8
- package/app/dashboard.app.d.ts.map +1 -1
- package/dashboard.plugin.d.ts +19 -14
- package/dashboard.plugin.d.ts.map +1 -1
- package/esm/index.mjs +45 -0
- package/esm/package.json +9 -8
- package/index.js +45 -0
- package/package.json +9 -8
package/README.md
CHANGED
|
@@ -1,46 +1,83 @@
|
|
|
1
1
|
# @frontmcp/plugin-dashboard
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A FrontMCP add-on that shows a server's apps, tools, resources and prompts in a browser, as a graph. Use it while
|
|
4
|
+
you build a server, to see what it serves. It has two parts: `DashboardApp`, which you add to `apps` and which
|
|
5
|
+
serves the dashboard, and `DashboardPlugin`, which sets its options. It runs on FrontMCP's Node HTTP server.
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
[](https://www.npmjs.com/package/@frontmcp/plugin-dashboard) [](https://github.com/agentfront/frontmcp/blob/main/LICENSE)
|
|
8
|
+
|
|
9
|
+
## Install
|
|
6
10
|
|
|
7
11
|
```bash
|
|
8
|
-
npm install @frontmcp/plugin-dashboard
|
|
12
|
+
npm install @frontmcp/plugin-dashboard @frontmcp/sdk
|
|
9
13
|
```
|
|
10
14
|
|
|
15
|
+
Requires Node.js 24 or later. Keep `@frontmcp/sdk` on the same version as this package.
|
|
16
|
+
|
|
11
17
|
## Usage
|
|
12
18
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
19
|
+
Add `DashboardApp` to `apps`, and register `DashboardPlugin.init()` for its options:
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import { DashboardApp, DashboardPlugin } from '@frontmcp/plugin-dashboard';
|
|
23
|
+
import { App, FrontMcp } from '@frontmcp/sdk';
|
|
16
24
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
25
|
+
@App({ id: 'help-desk', name: 'Help Desk' })
|
|
26
|
+
class HelpDeskApp {}
|
|
27
|
+
|
|
28
|
+
@FrontMcp({
|
|
29
|
+
info: { name: 'help-desk', version: '1.0.0' },
|
|
30
|
+
apps: [HelpDeskApp, DashboardApp],
|
|
31
|
+
plugins: [DashboardPlugin.init({ auth: { enabled: true, token: process.env.DASHBOARD_TOKEN } })],
|
|
32
|
+
})
|
|
33
|
+
export default class Server {}
|
|
20
34
|
```
|
|
21
35
|
|
|
22
|
-
|
|
36
|
+
Then open `/dashboard` on the server's port (`PORT`, else 3000), with the token as `Authorization: Bearer <token>` or
|
|
37
|
+
`?token=<token>`. The page then sets a cookie that its own requests send.
|
|
23
38
|
|
|
24
|
-
-
|
|
25
|
-
|
|
26
|
-
-
|
|
27
|
-
-
|
|
39
|
+
- **`GET /dashboard`** answers with an HTML page that loads React, React Flow and dagre from `esm.sh`, so the browser
|
|
40
|
+
needs internet access.
|
|
41
|
+
- **`/dashboard`** is also an MCP endpoint of its own, with three tools that describe the whole server:
|
|
42
|
+
`dashboard:graph`, `dashboard:list-tools` and `dashboard:list-resources`. They aren't listed at the server's main
|
|
43
|
+
endpoint. The page reads from them.
|
|
44
|
+
- **`DashboardPlugin.init()` alone serves nothing.** `DashboardApp` serves the dashboard, with the default options
|
|
45
|
+
when there's no plugin.
|
|
46
|
+
- **It's off in production.** Unless you set `enabled`, the dashboard is on when `NODE_ENV` isn't `production`, and
|
|
47
|
+
answers `404` when it is. To use it in production, set `enabled: true` with `auth`.
|
|
28
48
|
|
|
29
|
-
##
|
|
49
|
+
## Options
|
|
30
50
|
|
|
31
|
-
|
|
32
|
-
|
|
51
|
+
| Option | Default | Description |
|
|
52
|
+
| ---------------------------- | ------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
53
|
+
| `enabled` | On, unless `NODE_ENV` is `production` | Serve the dashboard. |
|
|
54
|
+
| `basePath` | `'/dashboard'` | Where the page is served. |
|
|
55
|
+
| `auth.enabled`, `auth.token` | Off | Require the token for the page and the MCP endpoint, and answer `401` without it. `enabled` without `token` makes `init()` throw. |
|
|
56
|
+
| `cdn` | `esm.sh` URLs | Where the page loads its libraries from: `entrypoint`, `react`, `reactDom`, `reactDomClient`, `reactJsxRuntime`, `reactRouter`, `xyflow`, `dagre`, `xyflowCss`. |
|
|
33
57
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
58
|
+
The dashboard's MCP endpoint applies the server's own authentication first, then the dashboard's `auth`. On a server
|
|
59
|
+
with its own authentication, send the dashboard token in the `x-frontmcp-dashboard-token` header.
|
|
60
|
+
|
|
61
|
+
Dashboard options apply to the whole process: a second `DashboardPlugin.init()` with a different `auth` throws, and
|
|
62
|
+
one with other differences replaces the first for every server, with a warning.
|
|
63
|
+
|
|
64
|
+
## Docs
|
|
65
|
+
|
|
66
|
+
- [The dashboard](https://frontmcp.dev/reference/plugins#the-dashboard): options, tokens, and the MCP endpoint
|
|
67
|
+
- [Plugins and adapters](https://frontmcp.dev/reference/plugins): every official plugin, and where to register it
|
|
68
|
+
|
|
69
|
+
## Related packages
|
|
70
|
+
|
|
71
|
+
- [`@frontmcp/sdk`](https://www.npmjs.com/package/@frontmcp/sdk): the framework this add-on extends
|
|
72
|
+
- [`@frontmcp/plugins`](https://www.npmjs.com/package/@frontmcp/plugins): a meta-package that re-exports
|
|
73
|
+
`DashboardApp` and `DashboardPlugin` with three other plugins
|
|
74
|
+
|
|
75
|
+
## Contributing
|
|
76
|
+
|
|
77
|
+
Source: [`plugins/plugin-dashboard`](https://github.com/agentfront/frontmcp/tree/main/plugins/plugin-dashboard).
|
|
78
|
+
Issues and pull requests are welcome; see
|
|
79
|
+
[CONTRIBUTING.md](https://github.com/agentfront/frontmcp/blob/main/CONTRIBUTING.md).
|
|
43
80
|
|
|
44
81
|
## License
|
|
45
82
|
|
|
46
|
-
Apache-2.0
|
|
83
|
+
Apache-2.0. See [LICENSE](https://github.com/agentfront/frontmcp/blob/main/LICENSE).
|
package/app/dashboard.app.d.ts
CHANGED
|
@@ -36,22 +36,21 @@ declare class DashboardHttpPlugin extends DynamicPlugin<DashboardPluginOptions,
|
|
|
36
36
|
* - Real-time event streaming via SSE (built into FrontMCP)
|
|
37
37
|
* - Access to server scope for monitoring
|
|
38
38
|
*
|
|
39
|
-
* The dashboard UI is loaded from CDN (esm.sh by default) and
|
|
40
|
-
*
|
|
39
|
+
* The dashboard UI is loaded from CDN (esm.sh by default) and reads the server's
|
|
40
|
+
* structure from the dashboard's own MCP endpoint, at the dashboard's path.
|
|
41
41
|
*
|
|
42
42
|
* @example
|
|
43
43
|
* ```typescript
|
|
44
|
-
* import { DashboardApp } from '@frontmcp/
|
|
44
|
+
* import { DashboardApp } from '@frontmcp/plugin-dashboard';
|
|
45
45
|
*
|
|
46
|
-
* @
|
|
47
|
-
* name: 'my-server',
|
|
48
|
-
* apps: [DashboardApp],
|
|
46
|
+
* @FrontMcp({
|
|
47
|
+
* info: { name: 'my-server', version: '1.0.0' },
|
|
48
|
+
* apps: [MyApp, DashboardApp],
|
|
49
49
|
* })
|
|
50
50
|
* class MyServer {}
|
|
51
51
|
* ```
|
|
52
52
|
*
|
|
53
|
-
* Then
|
|
54
|
-
* The dashboard connects via SSE at the standard `/sse` endpoint.
|
|
53
|
+
* Then open the dashboard at `http://localhost:3000/dashboard`. Register `DashboardPlugin.init()` to set its options.
|
|
55
54
|
*/
|
|
56
55
|
export declare class DashboardApp {
|
|
57
56
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dashboard.app.d.ts","sourceRoot":"","sources":["../../src/app/dashboard.app.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,aAAa,EAOb,KAAK,SAAS,EAGd,KAAK,YAAY,EAIlB,MAAM,eAAe,CAAC;AAYvB,OAAO,EAIL,KAAK,sBAAsB,EAC3B,KAAK,2BAA2B,EACjC,MAAM,oBAAoB,CAAC;AAsF5B;;;;;GAKG;AACH,cAIM,mBAAoB,SAAQ,aAAa,CAAC,sBAAsB,EAAE,2BAA2B,CAAC;IAClG,OAAO,EAAE,sBAAsB,CAAC;gBAEpB,OAAO,GAAE,2BAAgC;IAQrD;;;;;;;;;;OAUG;IAEG,gBAAgB,CAAC,OAAO,EAAE,SAAS,CAAC,cAAc,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAoBzE,gEAAgE;IAChE,OAAO,CAAC,OAAO;IAQf;;OAEG;WACa,gBAAgB,CAAC,OAAO,EAAE,2BAA2B,GAAG,YAAY,EAAE;CAgCvF;AAED
|
|
1
|
+
{"version":3,"file":"dashboard.app.d.ts","sourceRoot":"","sources":["../../src/app/dashboard.app.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,aAAa,EAOb,KAAK,SAAS,EAGd,KAAK,YAAY,EAIlB,MAAM,eAAe,CAAC;AAYvB,OAAO,EAIL,KAAK,sBAAsB,EAC3B,KAAK,2BAA2B,EACjC,MAAM,oBAAoB,CAAC;AAsF5B;;;;;GAKG;AACH,cAIM,mBAAoB,SAAQ,aAAa,CAAC,sBAAsB,EAAE,2BAA2B,CAAC;IAClG,OAAO,EAAE,sBAAsB,CAAC;gBAEpB,OAAO,GAAE,2BAAgC;IAQrD;;;;;;;;;;OAUG;IAEG,gBAAgB,CAAC,OAAO,EAAE,SAAS,CAAC,cAAc,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAoBzE,gEAAgE;IAChE,OAAO,CAAC,OAAO;IAQf;;OAEG;WACa,gBAAgB,CAAC,OAAO,EAAE,2BAA2B,GAAG,YAAY,EAAE;CAgCvF;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,qBAmCa,YAAY;CAAG;AAG5B,OAAO,EAAE,mBAAmB,EAAE,CAAC"}
|
package/dashboard.plugin.d.ts
CHANGED
|
@@ -8,41 +8,46 @@ import { type DashboardPluginOptions, type DashboardPluginOptionsInput } from '.
|
|
|
8
8
|
* - Real-time monitoring via MCP protocol
|
|
9
9
|
* - Graph-based visualization of your MCP architecture
|
|
10
10
|
*
|
|
11
|
-
* The dashboard UI is loaded from CDN (esm.sh by default) and
|
|
12
|
-
*
|
|
11
|
+
* The dashboard UI is loaded from CDN (esm.sh by default) and reads the server's
|
|
12
|
+
* structure from the dashboard's own MCP endpoint, at the dashboard's path.
|
|
13
|
+
*
|
|
14
|
+
* `DashboardApp` serves the dashboard; this plugin, registered with `init()`, sets its options.
|
|
13
15
|
*
|
|
14
16
|
* @example Basic usage
|
|
15
17
|
* ```typescript
|
|
16
|
-
* import { DashboardPlugin } from '@frontmcp/
|
|
18
|
+
* import { DashboardApp, DashboardPlugin } from '@frontmcp/plugin-dashboard';
|
|
17
19
|
*
|
|
18
|
-
* @
|
|
19
|
-
* name: 'my-server',
|
|
20
|
-
*
|
|
20
|
+
* @FrontMcp({
|
|
21
|
+
* info: { name: 'my-server', version: '1.0.0' },
|
|
22
|
+
* apps: [MyApp, DashboardApp],
|
|
23
|
+
* plugins: [DashboardPlugin.init()],
|
|
21
24
|
* })
|
|
22
25
|
* class MyServer {}
|
|
23
26
|
* ```
|
|
24
27
|
*
|
|
25
28
|
* @example With authentication
|
|
26
29
|
* ```typescript
|
|
27
|
-
* @
|
|
28
|
-
* name: 'my-server',
|
|
30
|
+
* @FrontMcp({
|
|
31
|
+
* info: { name: 'my-server', version: '1.0.0' },
|
|
32
|
+
* apps: [MyApp, DashboardApp],
|
|
29
33
|
* plugins: [
|
|
30
|
-
*
|
|
31
|
-
* auth: { enabled: true, token:
|
|
34
|
+
* DashboardPlugin.init({
|
|
35
|
+
* auth: { enabled: true, token: process.env.DASHBOARD_TOKEN },
|
|
32
36
|
* }),
|
|
33
37
|
* ],
|
|
34
38
|
* })
|
|
35
39
|
* class MyServer {}
|
|
36
40
|
*
|
|
37
|
-
* // Access: http://localhost:3000/dashboard?token
|
|
41
|
+
* // Access: http://localhost:3000/dashboard?token=<DASHBOARD_TOKEN>
|
|
38
42
|
* ```
|
|
39
43
|
*
|
|
40
44
|
* @example With custom CDN entrypoint
|
|
41
45
|
* ```typescript
|
|
42
|
-
* @
|
|
43
|
-
* name: 'my-server',
|
|
46
|
+
* @FrontMcp({
|
|
47
|
+
* info: { name: 'my-server', version: '1.0.0' },
|
|
48
|
+
* apps: [MyApp, DashboardApp],
|
|
44
49
|
* plugins: [
|
|
45
|
-
*
|
|
50
|
+
* DashboardPlugin.init({
|
|
46
51
|
* cdn: {
|
|
47
52
|
* entrypoint: 'https://cdn.example.com/dashboard-ui@1.0.0/index.js',
|
|
48
53
|
* },
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dashboard.plugin.d.ts","sourceRoot":"","sources":["../src/dashboard.plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAU,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAIzE,OAAO,EAGL,KAAK,sBAAsB,EAC3B,KAAK,2BAA2B,EACjC,MAAM,mBAAmB,CAAC;AAE3B
|
|
1
|
+
{"version":3,"file":"dashboard.plugin.d.ts","sourceRoot":"","sources":["../src/dashboard.plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAU,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAIzE,OAAO,EAGL,KAAK,sBAAsB,EAC3B,KAAK,2BAA2B,EACjC,MAAM,mBAAmB,CAAC;AAE3B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AAMH,MAAM,CAAC,OAAO,OAAO,eAAgB,SAAQ,aAAa,CAAC,sBAAsB,EAAE,2BAA2B,CAAC;IAC7G,OAAO,EAAE,sBAAsB,CAAC;gBAEpB,OAAO,GAAE,2BAAgC;IAUrD;;;;OAIG;WACa,gBAAgB,CAAC,OAAO,EAAE,2BAA2B,GAAG,YAAY,EAAE;CAkBvF"}
|
package/esm/index.mjs
CHANGED
|
@@ -1206,6 +1206,14 @@ var init_base = __esm({
|
|
|
1206
1206
|
"Use the memory, Redis, Upstash or Vercel KV adapter for an atomic compare-and-delete."
|
|
1207
1207
|
);
|
|
1208
1208
|
}
|
|
1209
|
+
/** Default: not supported, for the same reason as {@link deleteIfEquals}. */
|
|
1210
|
+
async expireIfEquals(_key, _expectedValue, _ttlSeconds) {
|
|
1211
|
+
throw new StorageNotSupportedError(
|
|
1212
|
+
"expireIfEquals",
|
|
1213
|
+
this.backendName,
|
|
1214
|
+
"Use the memory, Redis, Upstash or Vercel KV adapter for an atomic compare-and-expire."
|
|
1215
|
+
);
|
|
1216
|
+
}
|
|
1209
1217
|
// ============================================
|
|
1210
1218
|
// Pub/Sub (default: not supported)
|
|
1211
1219
|
// ============================================
|
|
@@ -1360,6 +1368,12 @@ var init_memory = __esm({
|
|
|
1360
1368
|
if (!entry || isExpired(entry.expiresAt) || entry.value !== expectedValue) return false;
|
|
1361
1369
|
return this.deleteEntry(key);
|
|
1362
1370
|
}
|
|
1371
|
+
async expireIfEquals(key, expectedValue, ttlSeconds) {
|
|
1372
|
+
this.ensureConnected();
|
|
1373
|
+
const entry = this.store.get(key);
|
|
1374
|
+
if (!entry || isExpired(entry.expiresAt) || entry.value !== expectedValue) return false;
|
|
1375
|
+
return this.expire(key, ttlSeconds);
|
|
1376
|
+
}
|
|
1363
1377
|
async exists(key) {
|
|
1364
1378
|
this.ensureConnected();
|
|
1365
1379
|
const entry = this.store.get(key);
|
|
@@ -2663,10 +2677,12 @@ var init_utils = __esm({
|
|
|
2663
2677
|
}
|
|
2664
2678
|
});
|
|
2665
2679
|
var COMPARE_AND_DELETE_SCRIPT;
|
|
2680
|
+
var COMPARE_AND_EXPIRE_SCRIPT;
|
|
2666
2681
|
var init_compare_and_delete = __esm({
|
|
2667
2682
|
"libs/utils/src/storage/utils/compare-and-delete.ts"() {
|
|
2668
2683
|
"use strict";
|
|
2669
2684
|
COMPARE_AND_DELETE_SCRIPT = `if redis.call('GET', KEYS[1]) == ARGV[1] then return redis.call('DEL', KEYS[1]) end return 0`;
|
|
2685
|
+
COMPARE_AND_EXPIRE_SCRIPT = `if redis.call('GET', KEYS[1]) == ARGV[1] then return redis.call('EXPIRE', KEYS[1], ARGV[2]) end return 0`;
|
|
2670
2686
|
}
|
|
2671
2687
|
});
|
|
2672
2688
|
var redis_exports = {};
|
|
@@ -2909,6 +2925,17 @@ var init_redis = __esm({
|
|
|
2909
2925
|
);
|
|
2910
2926
|
return deleted === 1;
|
|
2911
2927
|
}
|
|
2928
|
+
async expireIfEquals(key, expectedValue, ttlSeconds) {
|
|
2929
|
+
validateTTL(ttlSeconds);
|
|
2930
|
+
const extended = await this.getConnectedClient().eval(
|
|
2931
|
+
COMPARE_AND_EXPIRE_SCRIPT,
|
|
2932
|
+
1,
|
|
2933
|
+
this.prefixKey(key),
|
|
2934
|
+
expectedValue,
|
|
2935
|
+
String(ttlSeconds)
|
|
2936
|
+
);
|
|
2937
|
+
return extended === 1;
|
|
2938
|
+
}
|
|
2912
2939
|
async exists(key) {
|
|
2913
2940
|
const result = await this.getConnectedClient().exists(this.prefixKey(key));
|
|
2914
2941
|
return result > 0;
|
|
@@ -3240,6 +3267,15 @@ var init_vercel_kv = __esm({
|
|
|
3240
3267
|
);
|
|
3241
3268
|
return deleted === 1;
|
|
3242
3269
|
}
|
|
3270
|
+
async expireIfEquals(key, expectedValue, ttlSeconds) {
|
|
3271
|
+
validateTTL(ttlSeconds);
|
|
3272
|
+
const extended = await this.getConnectedClient().eval(
|
|
3273
|
+
COMPARE_AND_EXPIRE_SCRIPT,
|
|
3274
|
+
[this.prefixKey(key)],
|
|
3275
|
+
[expectedValue, String(ttlSeconds)]
|
|
3276
|
+
);
|
|
3277
|
+
return extended === 1;
|
|
3278
|
+
}
|
|
3243
3279
|
async exists(key) {
|
|
3244
3280
|
const result = await this.getConnectedClient().exists(this.prefixKey(key));
|
|
3245
3281
|
return result > 0;
|
|
@@ -3474,6 +3510,15 @@ var init_upstash = __esm({
|
|
|
3474
3510
|
);
|
|
3475
3511
|
return deleted === 1;
|
|
3476
3512
|
}
|
|
3513
|
+
async expireIfEquals(key, expectedValue, ttlSeconds) {
|
|
3514
|
+
validateTTL(ttlSeconds);
|
|
3515
|
+
const extended = await this.getConnectedClient().eval(
|
|
3516
|
+
COMPARE_AND_EXPIRE_SCRIPT,
|
|
3517
|
+
[this.prefixKey(key)],
|
|
3518
|
+
[expectedValue, String(ttlSeconds)]
|
|
3519
|
+
);
|
|
3520
|
+
return extended === 1;
|
|
3521
|
+
}
|
|
3477
3522
|
async exists(key) {
|
|
3478
3523
|
const result = await this.getConnectedClient().exists(this.prefixKey(key));
|
|
3479
3524
|
return result > 0;
|
package/esm/package.json
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frontmcp/plugin-dashboard",
|
|
3
|
-
"version": "1.9.
|
|
4
|
-
"description": "Dashboard plugin for FrontMCP
|
|
3
|
+
"version": "1.9.7",
|
|
4
|
+
"description": "Dashboard plugin for FrontMCP: a web page and MCP endpoint that show a server's apps, tools, resources and prompts",
|
|
5
5
|
"author": "AgentFront <info@agentfront.dev>",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"engines": {
|
|
8
8
|
"node": ">=24.0.0"
|
|
9
9
|
},
|
|
10
10
|
"keywords": [
|
|
11
|
+
"frontmcp",
|
|
11
12
|
"mcp",
|
|
13
|
+
"model-context-protocol",
|
|
12
14
|
"dashboard",
|
|
13
|
-
"
|
|
15
|
+
"devtools",
|
|
16
|
+
"inspector",
|
|
14
17
|
"plugin",
|
|
15
|
-
"frontmcp",
|
|
16
18
|
"agentfront"
|
|
17
19
|
],
|
|
18
20
|
"repository": {
|
|
@@ -46,13 +48,12 @@
|
|
|
46
48
|
}
|
|
47
49
|
}
|
|
48
50
|
},
|
|
49
|
-
"dependencies": {
|
|
50
|
-
"@frontmcp/sdk": "1.9.5"
|
|
51
|
-
},
|
|
52
51
|
"devDependencies": {
|
|
52
|
+
"@frontmcp/sdk": "1.9.7",
|
|
53
53
|
"reflect-metadata": "^0.2.2"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
|
-
"@frontmcp/lazy-zod": "1.9.
|
|
56
|
+
"@frontmcp/lazy-zod": "1.9.7",
|
|
57
|
+
"@frontmcp/sdk": "1.9.7"
|
|
57
58
|
}
|
|
58
59
|
}
|
package/index.js
CHANGED
|
@@ -1215,6 +1215,14 @@ var init_base = __esm({
|
|
|
1215
1215
|
"Use the memory, Redis, Upstash or Vercel KV adapter for an atomic compare-and-delete."
|
|
1216
1216
|
);
|
|
1217
1217
|
}
|
|
1218
|
+
/** Default: not supported, for the same reason as {@link deleteIfEquals}. */
|
|
1219
|
+
async expireIfEquals(_key, _expectedValue, _ttlSeconds) {
|
|
1220
|
+
throw new StorageNotSupportedError(
|
|
1221
|
+
"expireIfEquals",
|
|
1222
|
+
this.backendName,
|
|
1223
|
+
"Use the memory, Redis, Upstash or Vercel KV adapter for an atomic compare-and-expire."
|
|
1224
|
+
);
|
|
1225
|
+
}
|
|
1218
1226
|
// ============================================
|
|
1219
1227
|
// Pub/Sub (default: not supported)
|
|
1220
1228
|
// ============================================
|
|
@@ -1369,6 +1377,12 @@ var init_memory = __esm({
|
|
|
1369
1377
|
if (!entry || isExpired(entry.expiresAt) || entry.value !== expectedValue) return false;
|
|
1370
1378
|
return this.deleteEntry(key);
|
|
1371
1379
|
}
|
|
1380
|
+
async expireIfEquals(key, expectedValue, ttlSeconds) {
|
|
1381
|
+
this.ensureConnected();
|
|
1382
|
+
const entry = this.store.get(key);
|
|
1383
|
+
if (!entry || isExpired(entry.expiresAt) || entry.value !== expectedValue) return false;
|
|
1384
|
+
return this.expire(key, ttlSeconds);
|
|
1385
|
+
}
|
|
1372
1386
|
async exists(key) {
|
|
1373
1387
|
this.ensureConnected();
|
|
1374
1388
|
const entry = this.store.get(key);
|
|
@@ -2672,10 +2686,12 @@ var init_utils = __esm({
|
|
|
2672
2686
|
}
|
|
2673
2687
|
});
|
|
2674
2688
|
var COMPARE_AND_DELETE_SCRIPT;
|
|
2689
|
+
var COMPARE_AND_EXPIRE_SCRIPT;
|
|
2675
2690
|
var init_compare_and_delete = __esm({
|
|
2676
2691
|
"libs/utils/src/storage/utils/compare-and-delete.ts"() {
|
|
2677
2692
|
"use strict";
|
|
2678
2693
|
COMPARE_AND_DELETE_SCRIPT = `if redis.call('GET', KEYS[1]) == ARGV[1] then return redis.call('DEL', KEYS[1]) end return 0`;
|
|
2694
|
+
COMPARE_AND_EXPIRE_SCRIPT = `if redis.call('GET', KEYS[1]) == ARGV[1] then return redis.call('EXPIRE', KEYS[1], ARGV[2]) end return 0`;
|
|
2679
2695
|
}
|
|
2680
2696
|
});
|
|
2681
2697
|
var redis_exports = {};
|
|
@@ -2918,6 +2934,17 @@ var init_redis = __esm({
|
|
|
2918
2934
|
);
|
|
2919
2935
|
return deleted === 1;
|
|
2920
2936
|
}
|
|
2937
|
+
async expireIfEquals(key, expectedValue, ttlSeconds) {
|
|
2938
|
+
validateTTL(ttlSeconds);
|
|
2939
|
+
const extended = await this.getConnectedClient().eval(
|
|
2940
|
+
COMPARE_AND_EXPIRE_SCRIPT,
|
|
2941
|
+
1,
|
|
2942
|
+
this.prefixKey(key),
|
|
2943
|
+
expectedValue,
|
|
2944
|
+
String(ttlSeconds)
|
|
2945
|
+
);
|
|
2946
|
+
return extended === 1;
|
|
2947
|
+
}
|
|
2921
2948
|
async exists(key) {
|
|
2922
2949
|
const result = await this.getConnectedClient().exists(this.prefixKey(key));
|
|
2923
2950
|
return result > 0;
|
|
@@ -3249,6 +3276,15 @@ var init_vercel_kv = __esm({
|
|
|
3249
3276
|
);
|
|
3250
3277
|
return deleted === 1;
|
|
3251
3278
|
}
|
|
3279
|
+
async expireIfEquals(key, expectedValue, ttlSeconds) {
|
|
3280
|
+
validateTTL(ttlSeconds);
|
|
3281
|
+
const extended = await this.getConnectedClient().eval(
|
|
3282
|
+
COMPARE_AND_EXPIRE_SCRIPT,
|
|
3283
|
+
[this.prefixKey(key)],
|
|
3284
|
+
[expectedValue, String(ttlSeconds)]
|
|
3285
|
+
);
|
|
3286
|
+
return extended === 1;
|
|
3287
|
+
}
|
|
3252
3288
|
async exists(key) {
|
|
3253
3289
|
const result = await this.getConnectedClient().exists(this.prefixKey(key));
|
|
3254
3290
|
return result > 0;
|
|
@@ -3483,6 +3519,15 @@ var init_upstash = __esm({
|
|
|
3483
3519
|
);
|
|
3484
3520
|
return deleted === 1;
|
|
3485
3521
|
}
|
|
3522
|
+
async expireIfEquals(key, expectedValue, ttlSeconds) {
|
|
3523
|
+
validateTTL(ttlSeconds);
|
|
3524
|
+
const extended = await this.getConnectedClient().eval(
|
|
3525
|
+
COMPARE_AND_EXPIRE_SCRIPT,
|
|
3526
|
+
[this.prefixKey(key)],
|
|
3527
|
+
[expectedValue, String(ttlSeconds)]
|
|
3528
|
+
);
|
|
3529
|
+
return extended === 1;
|
|
3530
|
+
}
|
|
3486
3531
|
async exists(key) {
|
|
3487
3532
|
const result = await this.getConnectedClient().exists(this.prefixKey(key));
|
|
3488
3533
|
return result > 0;
|
package/package.json
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frontmcp/plugin-dashboard",
|
|
3
|
-
"version": "1.9.
|
|
4
|
-
"description": "Dashboard plugin for FrontMCP
|
|
3
|
+
"version": "1.9.7",
|
|
4
|
+
"description": "Dashboard plugin for FrontMCP: a web page and MCP endpoint that show a server's apps, tools, resources and prompts",
|
|
5
5
|
"author": "AgentFront <info@agentfront.dev>",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"engines": {
|
|
8
8
|
"node": ">=24.0.0"
|
|
9
9
|
},
|
|
10
10
|
"keywords": [
|
|
11
|
+
"frontmcp",
|
|
11
12
|
"mcp",
|
|
13
|
+
"model-context-protocol",
|
|
12
14
|
"dashboard",
|
|
13
|
-
"
|
|
15
|
+
"devtools",
|
|
16
|
+
"inspector",
|
|
14
17
|
"plugin",
|
|
15
|
-
"frontmcp",
|
|
16
18
|
"agentfront"
|
|
17
19
|
],
|
|
18
20
|
"repository": {
|
|
@@ -46,13 +48,12 @@
|
|
|
46
48
|
}
|
|
47
49
|
}
|
|
48
50
|
},
|
|
49
|
-
"dependencies": {
|
|
50
|
-
"@frontmcp/sdk": "1.9.5"
|
|
51
|
-
},
|
|
52
51
|
"devDependencies": {
|
|
52
|
+
"@frontmcp/sdk": "1.9.7",
|
|
53
53
|
"reflect-metadata": "^0.2.2"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
|
-
"@frontmcp/lazy-zod": "1.9.
|
|
56
|
+
"@frontmcp/lazy-zod": "1.9.7",
|
|
57
|
+
"@frontmcp/sdk": "1.9.7"
|
|
57
58
|
}
|
|
58
59
|
}
|