@frontmcp/plugin-dashboard 1.8.2 → 1.8.4
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/app/dashboard.app.d.ts +18 -4
- package/app/dashboard.app.d.ts.map +1 -1
- package/auth/dashboard-access.d.ts +42 -0
- package/auth/dashboard-access.d.ts.map +1 -0
- package/auth/dashboard-auth.d.ts +46 -1
- package/auth/dashboard-auth.d.ts.map +1 -1
- package/esm/index.mjs +149 -34
- package/esm/package.json +3 -3
- package/html/html.generator.d.ts +7 -4
- package/html/html.generator.d.ts.map +1 -1
- package/index.js +164 -51
- package/package.json +3 -3
- package/tools/graph.tool.d.ts.map +1 -1
- package/tools/list-resources.tool.d.ts.map +1 -1
- package/tools/list-tools.tool.d.ts.map +1 -1
package/app/dashboard.app.d.ts
CHANGED
|
@@ -1,16 +1,30 @@
|
|
|
1
|
-
import { DynamicPlugin, type ProviderType } from '@frontmcp/sdk';
|
|
1
|
+
import { DynamicPlugin, type FlowCtxOf, type ProviderType } from '@frontmcp/sdk';
|
|
2
2
|
import { type DashboardPluginOptions, type DashboardPluginOptionsInput } from '../dashboard.types';
|
|
3
3
|
/**
|
|
4
4
|
* Internal Dashboard HTTP Plugin.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* Serves the dashboard HTML, and gates the dashboard's MCP endpoint. The SSE transport
|
|
7
|
+
* and MCP protocol are handled by FrontMCP's built-in transport layer.
|
|
8
8
|
*/
|
|
9
9
|
declare class DashboardHttpPlugin extends DynamicPlugin<DashboardPluginOptions, DashboardPluginOptionsInput> {
|
|
10
10
|
options: DashboardPluginOptions;
|
|
11
11
|
constructor(options?: DashboardPluginOptionsInput);
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
13
|
+
* Gate every request the dashboard's MCP endpoint is about to handle.
|
|
14
|
+
*
|
|
15
|
+
* `enabled` and `auth` used to reach only the page middleware, so with the dashboard
|
|
16
|
+
* turned off, or without its token, `POST /dashboard` still answered `initialize` and
|
|
17
|
+
* `dashboard:graph` returned the whole server's inventory. This hook runs in the
|
|
18
|
+
* dashboard scope's `http:request` flow, on Node and on the fetch handler alike, after
|
|
19
|
+
* the router picked an MCP transport and before any of them runs. The server's own
|
|
20
|
+
* authentication (the `checkAuthorization` stage) still applies first; the dashboard
|
|
21
|
+
* token is required on top of it.
|
|
22
|
+
*/
|
|
23
|
+
gateDashboardMcp(flowCtx: FlowCtxOf<'http:request'>): Promise<void>;
|
|
24
|
+
/** `this.get`, or `undefined` when the token isn't provided. */
|
|
25
|
+
private resolve;
|
|
26
|
+
/**
|
|
27
|
+
* Provide the dashboard config and page middleware registration via DI.
|
|
14
28
|
*/
|
|
15
29
|
static dynamicProviders(options: DashboardPluginOptionsInput): ProviderType[];
|
|
16
30
|
}
|
|
@@ -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,YAAY,
|
|
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;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,qBAmCa,YAAY;CAAG;AAG5B,OAAO,EAAE,mBAAmB,EAAE,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { PublicMcpError, type ToolContext } from '@frontmcp/sdk';
|
|
2
|
+
import { type DashboardPluginOptions } from '../dashboard.types';
|
|
3
|
+
/** Context methods the gate marker needs (a `FrontMcpContext`). */
|
|
4
|
+
interface ContextStore {
|
|
5
|
+
set<T>(key: symbol, value: T): void;
|
|
6
|
+
get<T>(key: symbol): T | undefined;
|
|
7
|
+
}
|
|
8
|
+
/** Record that the HTTP gate checked this request's dashboard token. */
|
|
9
|
+
export declare function markDashboardGatePassed(ctx: ContextStore | undefined): void;
|
|
10
|
+
/** The dashboard is turned off (`enabled: false`, or the production default): its MCP endpoint and tools refuse. */
|
|
11
|
+
export declare class DashboardDisabledError extends PublicMcpError {
|
|
12
|
+
readonly mcpErrorCode: -32003;
|
|
13
|
+
constructor();
|
|
14
|
+
}
|
|
15
|
+
/** `auth` is on and the caller has not shown the dashboard token. */
|
|
16
|
+
export declare class DashboardUnauthorizedError extends PublicMcpError {
|
|
17
|
+
readonly mcpErrorCode: -32001;
|
|
18
|
+
constructor();
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Refuse a dashboard tool call that the dashboard's own options don't allow.
|
|
22
|
+
*
|
|
23
|
+
* The HTTP gate (a hook on the dashboard scope's `http:request` flow) already refuses such
|
|
24
|
+
* requests; this check makes the tools refuse them wherever they are served from (a
|
|
25
|
+
* direct or stdio server, another app they were added to), so `enabled` and `auth` hold
|
|
26
|
+
* for every transport, not only for the web page.
|
|
27
|
+
*
|
|
28
|
+
* @param options - the dashboard's effective options
|
|
29
|
+
* @param ctx - the request's context: the gate's mark, and its `x-frontmcp-*` headers
|
|
30
|
+
*/
|
|
31
|
+
export declare function assertDashboardAccess(options: DashboardPluginOptions, ctx: (ContextStore & {
|
|
32
|
+
metadata?: {
|
|
33
|
+
customHeaders?: Readonly<Record<string, string>>;
|
|
34
|
+
};
|
|
35
|
+
}) | undefined): void;
|
|
36
|
+
/**
|
|
37
|
+
* {@link assertDashboardAccess} for one of the dashboard's tools: the options come from the
|
|
38
|
+
* dashboard's config provider (the ones its HTTP gate uses), else the published options.
|
|
39
|
+
*/
|
|
40
|
+
export declare function assertDashboardToolAccess(tool: ToolContext): void;
|
|
41
|
+
export {};
|
|
42
|
+
//# sourceMappingURL=dashboard-access.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dashboard-access.d.ts","sourceRoot":"","sources":["../../src/auth/dashboard-access.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,cAAc,EAAE,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAIlF,OAAO,EAAsB,KAAK,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AASrF,mEAAmE;AACnE,UAAU,YAAY;IACpB,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;IACpC,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAAC;CACpC;AAED,wEAAwE;AACxE,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,YAAY,GAAG,SAAS,GAAG,IAAI,CAE3E;AAED,oHAAoH;AACpH,qBAAa,sBAAuB,SAAQ,cAAc;IACxD,QAAQ,CAAC,YAAY,SAA6B;;CAKnD;AAED,qEAAqE;AACrE,qBAAa,0BAA2B,SAAQ,cAAc;IAC5D,QAAQ,CAAC,YAAY,SAAgC;;CAUtD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,sBAAsB,EAC/B,GAAG,EAAE,CAAC,YAAY,GAAG;IAAE,QAAQ,CAAC,EAAE;QAAE,aAAa,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;KAAE,CAAA;CAAE,CAAC,GAAG,SAAS,GACpG,IAAI,CAaN;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,CAGjE"}
|
package/auth/dashboard-auth.d.ts
CHANGED
|
@@ -13,10 +13,55 @@ export interface DashboardAuthRequest {
|
|
|
13
13
|
headers?: Record<string, string | string[] | undefined> | undefined;
|
|
14
14
|
query?: Record<string, string | string[] | undefined> | undefined;
|
|
15
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* Header an MCP client sends the dashboard token in. `Authorization` belongs to the
|
|
18
|
+
* server's own authentication, so on an authenticated server the dashboard token needs
|
|
19
|
+
* a channel of its own.
|
|
20
|
+
*/
|
|
21
|
+
export declare const DASHBOARD_TOKEN_HEADER = "x-frontmcp-dashboard-token";
|
|
22
|
+
/**
|
|
23
|
+
* Cookie the dashboard page gets once its token was checked, so the page's own MCP
|
|
24
|
+
* client (an `EventSource`, which cannot send headers) reaches the MCP endpoint without
|
|
25
|
+
* putting the token in a URL.
|
|
26
|
+
*/
|
|
27
|
+
export declare const DASHBOARD_SESSION_COOKIE = "frontmcp_dashboard";
|
|
28
|
+
/**
|
|
29
|
+
* Which part of the dashboard a request is for.
|
|
30
|
+
*
|
|
31
|
+
* - `page`: the HTML page. Accepts `Authorization: Bearer`, `?token=` (the documented
|
|
32
|
+
* link) and the session cookie.
|
|
33
|
+
* - `mcp`: the dashboard's MCP endpoint. Accepts `Authorization: Bearer`, the
|
|
34
|
+
* `x-frontmcp-dashboard-token` header and the session cookie, but not `?token=`: a URL
|
|
35
|
+
* token lands in access logs, `Referer` headers and browser history.
|
|
36
|
+
*/
|
|
37
|
+
export type DashboardAuthSurface = 'page' | 'mcp';
|
|
16
38
|
export interface DashboardAuthResult {
|
|
17
39
|
authorized: boolean;
|
|
18
40
|
status?: 401;
|
|
19
41
|
message?: string;
|
|
20
42
|
}
|
|
21
|
-
|
|
43
|
+
/**
|
|
44
|
+
* Constant-time token comparison.
|
|
45
|
+
*
|
|
46
|
+
* Both sides are hashed to fixed-length digests first, so neither the comparison
|
|
47
|
+
* time nor `timingSafeEqual`'s equal-length requirement leaks the token length.
|
|
48
|
+
* This is the same idiom the DCR client registry uses for client secrets.
|
|
49
|
+
*/
|
|
50
|
+
export declare function tokenMatches(presented: string, expected: string): boolean;
|
|
51
|
+
/**
|
|
52
|
+
* The session cookie's value: an HMAC of a fixed label keyed by the token, so the cookie
|
|
53
|
+
* never holds the token itself and cannot be computed without it.
|
|
54
|
+
*/
|
|
55
|
+
export declare function deriveDashboardCookieValue(token: string): string;
|
|
56
|
+
/**
|
|
57
|
+
* Create the dashboard's HTTP auth check for one surface (see {@link DashboardAuthSurface}).
|
|
58
|
+
*
|
|
59
|
+
* Returns `undefined` when authentication is not enabled, so the caller can skip
|
|
60
|
+
* the check entirely rather than branch on a permissive validator.
|
|
61
|
+
*
|
|
62
|
+
* `Authorization: Bearer <token>` is accepted on both surfaces; the page also takes
|
|
63
|
+
* `?token=<token>` (the documented link), the MCP endpoint also takes
|
|
64
|
+
* `x-frontmcp-dashboard-token`, and both take the cookie the page sets.
|
|
65
|
+
*/
|
|
66
|
+
export declare function createDashboardAuthValidator(auth: DashboardAuth | undefined, surface?: DashboardAuthSurface): ((req: DashboardAuthRequest) => DashboardAuthResult) | undefined;
|
|
22
67
|
//# sourceMappingURL=dashboard-auth.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dashboard-auth.d.ts","sourceRoot":"","sources":["../../src/auth/dashboard-auth.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD;;;;;;;;;GASG;AACH,MAAM,WAAW,oBAAoB;IACnC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,GAAG,SAAS,CAAC;IACpE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,GAAG,SAAS,CAAC;CACnE;AAED,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,GAAG,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;
|
|
1
|
+
{"version":3,"file":"dashboard-auth.d.ts","sourceRoot":"","sources":["../../src/auth/dashboard-auth.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD;;;;;;;;;GASG;AACH,MAAM,WAAW,oBAAoB;IACnC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,GAAG,SAAS,CAAC;IACpE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,GAAG,SAAS,CAAC;CACnE;AAED;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,+BAA+B,CAAC;AAEnE;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,uBAAuB,CAAC;AAE7D;;;;;;;;GAQG;AACH,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,KAAK,CAAC;AAElD,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,GAAG,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAiBD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAGzE;AAID;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhE;AA+BD;;;;;;;;;GASG;AACH,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,aAAa,GAAG,SAAS,EAC/B,OAAO,GAAE,oBAA6B,GACrC,CAAC,CAAC,GAAG,EAAE,oBAAoB,KAAK,mBAAmB,CAAC,GAAG,SAAS,CA8BlE"}
|
package/esm/index.mjs
CHANGED
|
@@ -421,11 +421,16 @@ import {
|
|
|
421
421
|
App,
|
|
422
422
|
DynamicPlugin as DynamicPlugin2,
|
|
423
423
|
FrontMcpConfig,
|
|
424
|
+
FrontMcpContextStorage,
|
|
424
425
|
FrontMcpServer,
|
|
426
|
+
HttpHook,
|
|
425
427
|
Plugin as Plugin2,
|
|
426
428
|
ScopeEntry as ScopeEntry2
|
|
427
429
|
} from "@frontmcp/sdk";
|
|
428
430
|
|
|
431
|
+
// plugins/plugin-dashboard/src/auth/dashboard-access.ts
|
|
432
|
+
import { MCP_ERROR_CODES, PublicMcpError } from "@frontmcp/sdk";
|
|
433
|
+
|
|
429
434
|
// libs/utils/dist/esm/index.mjs
|
|
430
435
|
import { createRequire as __frontmcpCreateRequire } from "module";
|
|
431
436
|
import { z as z2 } from "@frontmcp/lazy-zod";
|
|
@@ -2606,6 +2611,9 @@ function randomBytes(length) {
|
|
|
2606
2611
|
function sha256(data) {
|
|
2607
2612
|
return getCrypto().sha256(data);
|
|
2608
2613
|
}
|
|
2614
|
+
function hmacSha256(key, data) {
|
|
2615
|
+
return getCrypto().hmacSha256(key, data);
|
|
2616
|
+
}
|
|
2609
2617
|
function encryptAesGcm(key, plaintext, iv) {
|
|
2610
2618
|
if (key.length !== 32) {
|
|
2611
2619
|
throw new Error(`AES-256-GCM requires a 32-byte key, got ${key.length} bytes`);
|
|
@@ -3796,6 +3804,8 @@ init_crypto();
|
|
|
3796
3804
|
init_runtime();
|
|
3797
3805
|
|
|
3798
3806
|
// plugins/plugin-dashboard/src/auth/dashboard-auth.ts
|
|
3807
|
+
var DASHBOARD_TOKEN_HEADER = "x-frontmcp-dashboard-token";
|
|
3808
|
+
var DASHBOARD_SESSION_COOKIE = "frontmcp_dashboard";
|
|
3799
3809
|
var AUTHORIZED = { authorized: true };
|
|
3800
3810
|
function readHeader(headers, name) {
|
|
3801
3811
|
if (!headers) return void 0;
|
|
@@ -3811,6 +3821,19 @@ function tokenMatches(presented, expected) {
|
|
|
3811
3821
|
if (presented.length === 0 || expected.length === 0) return false;
|
|
3812
3822
|
return timingSafeEqual(sha256(presented), sha256(expected));
|
|
3813
3823
|
}
|
|
3824
|
+
var encoder = new TextEncoder();
|
|
3825
|
+
function deriveDashboardCookieValue(token) {
|
|
3826
|
+
return base64urlEncode(hmacSha256(encoder.encode(token), encoder.encode("frontmcp-dashboard-session:v1")));
|
|
3827
|
+
}
|
|
3828
|
+
function readCookie(header, name) {
|
|
3829
|
+
if (!header) return void 0;
|
|
3830
|
+
for (const part of header.split(";")) {
|
|
3831
|
+
const separator = part.indexOf("=");
|
|
3832
|
+
if (separator === -1) continue;
|
|
3833
|
+
if (part.slice(0, separator).trim() === name) return part.slice(separator + 1).trim();
|
|
3834
|
+
}
|
|
3835
|
+
return void 0;
|
|
3836
|
+
}
|
|
3814
3837
|
function readBearerToken(header) {
|
|
3815
3838
|
if (!header) return void 0;
|
|
3816
3839
|
const separator = header.indexOf(" ");
|
|
@@ -3819,36 +3842,81 @@ function readBearerToken(header) {
|
|
|
3819
3842
|
const token = header.slice(separator + 1).trim();
|
|
3820
3843
|
return token.length > 0 ? token : void 0;
|
|
3821
3844
|
}
|
|
3822
|
-
function createDashboardAuthValidator(auth) {
|
|
3845
|
+
function createDashboardAuthValidator(auth, surface = "page") {
|
|
3823
3846
|
if (!auth?.enabled) return void 0;
|
|
3824
3847
|
const expected = auth.token;
|
|
3848
|
+
const expectedCookie = expected ? deriveDashboardCookieValue(expected) : void 0;
|
|
3825
3849
|
return (req) => {
|
|
3826
|
-
if (!expected) {
|
|
3850
|
+
if (!expected || !expectedCookie) {
|
|
3827
3851
|
return { authorized: false, status: 401, message: "Dashboard authentication is misconfigured" };
|
|
3828
3852
|
}
|
|
3829
3853
|
const bearer = readBearerToken(readHeader(req.headers, "authorization"));
|
|
3830
3854
|
if (bearer && tokenMatches(bearer, expected)) return AUTHORIZED;
|
|
3831
|
-
|
|
3832
|
-
|
|
3855
|
+
if (surface === "mcp") {
|
|
3856
|
+
const headerToken = readHeader(req.headers, DASHBOARD_TOKEN_HEADER);
|
|
3857
|
+
if (headerToken && tokenMatches(headerToken, expected)) return AUTHORIZED;
|
|
3858
|
+
} else {
|
|
3859
|
+
const queryToken = readQuery(req.query, "token");
|
|
3860
|
+
if (queryToken && tokenMatches(queryToken, expected)) return AUTHORIZED;
|
|
3861
|
+
}
|
|
3862
|
+
const cookie = readCookie(readHeader(req.headers, "cookie"), DASHBOARD_SESSION_COOKIE);
|
|
3863
|
+
if (cookie && tokenMatches(cookie, expectedCookie)) return AUTHORIZED;
|
|
3833
3864
|
return { authorized: false, status: 401, message: "Unauthorized" };
|
|
3834
3865
|
};
|
|
3835
3866
|
}
|
|
3836
3867
|
|
|
3868
|
+
// plugins/plugin-dashboard/src/auth/dashboard-access.ts
|
|
3869
|
+
var GATE_PASSED = /* @__PURE__ */ Symbol("frontmcp:dashboard:gate-passed");
|
|
3870
|
+
function markDashboardGatePassed(ctx) {
|
|
3871
|
+
ctx?.set(GATE_PASSED, true);
|
|
3872
|
+
}
|
|
3873
|
+
var DashboardDisabledError = class extends PublicMcpError {
|
|
3874
|
+
mcpErrorCode = MCP_ERROR_CODES.FORBIDDEN;
|
|
3875
|
+
constructor() {
|
|
3876
|
+
super("The FrontMCP dashboard is disabled.", "DASHBOARD_DISABLED", 404);
|
|
3877
|
+
}
|
|
3878
|
+
};
|
|
3879
|
+
var DashboardUnauthorizedError = class extends PublicMcpError {
|
|
3880
|
+
mcpErrorCode = MCP_ERROR_CODES.UNAUTHORIZED;
|
|
3881
|
+
constructor() {
|
|
3882
|
+
super(
|
|
3883
|
+
`The dashboard token is required. Send it in the ${DASHBOARD_TOKEN_HEADER} header.`,
|
|
3884
|
+
"DASHBOARD_UNAUTHORIZED",
|
|
3885
|
+
401,
|
|
3886
|
+
'Bearer realm="frontmcp-dashboard"'
|
|
3887
|
+
);
|
|
3888
|
+
}
|
|
3889
|
+
};
|
|
3890
|
+
function assertDashboardAccess(options, ctx) {
|
|
3891
|
+
if (!isDashboardEnabled(options)) {
|
|
3892
|
+
throw new DashboardDisabledError();
|
|
3893
|
+
}
|
|
3894
|
+
if (!options.auth?.enabled) return;
|
|
3895
|
+
if (ctx?.get(GATE_PASSED) === true) return;
|
|
3896
|
+
const expected = options.auth.token;
|
|
3897
|
+
const presented = ctx?.metadata?.customHeaders?.[DASHBOARD_TOKEN_HEADER];
|
|
3898
|
+
if (expected && presented && tokenMatches(presented, expected)) return;
|
|
3899
|
+
throw new DashboardUnauthorizedError();
|
|
3900
|
+
}
|
|
3901
|
+
function assertDashboardToolAccess(tool) {
|
|
3902
|
+
const configured = tool.tryGet(DashboardConfigToken);
|
|
3903
|
+
assertDashboardAccess(configured ?? resolveDashboardOptions(), tool.tryGetContext());
|
|
3904
|
+
}
|
|
3905
|
+
|
|
3837
3906
|
// plugins/plugin-dashboard/src/html/html.generator.ts
|
|
3838
3907
|
function escapeForJs(str) {
|
|
3839
3908
|
return str.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/"/g, '\\"').replace(/</g, "\\x3c").replace(/>/g, "\\x3e").replace(/\n/g, "\\n").replace(/\r/g, "\\r");
|
|
3840
3909
|
}
|
|
3841
|
-
function generateDashboardHtml(options) {
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
if (cdn.entrypoint) {
|
|
3845
|
-
return generateExternalEntrypointHtml(options);
|
|
3910
|
+
function generateDashboardHtml(options, mcpPath = options.basePath) {
|
|
3911
|
+
if (options.cdn.entrypoint) {
|
|
3912
|
+
return generateExternalEntrypointHtml(options, mcpPath);
|
|
3846
3913
|
}
|
|
3847
|
-
return generateInlineDashboardHtml(options);
|
|
3914
|
+
return generateInlineDashboardHtml(options, mcpPath);
|
|
3848
3915
|
}
|
|
3849
|
-
function generateExternalEntrypointHtml(options) {
|
|
3916
|
+
function generateExternalEntrypointHtml(options, mcpPath) {
|
|
3850
3917
|
const { cdn } = options;
|
|
3851
3918
|
const safeBasePath = escapeForJs(options.basePath);
|
|
3919
|
+
const safeMcpPath = escapeForJs(mcpPath);
|
|
3852
3920
|
const safeReact = escapeForJs(cdn.react);
|
|
3853
3921
|
const safeReactDom = escapeForJs(cdn.reactDom);
|
|
3854
3922
|
const safeReactDomClient = escapeForJs(cdn.reactDomClient);
|
|
@@ -3888,7 +3956,8 @@ function generateExternalEntrypointHtml(options) {
|
|
|
3888
3956
|
// Dashboard configuration
|
|
3889
3957
|
window.__FRONTMCP_DASHBOARD__ = {
|
|
3890
3958
|
basePath: '${safeBasePath}',
|
|
3891
|
-
|
|
3959
|
+
mcpPath: '${safeMcpPath}',
|
|
3960
|
+
sseUrl: '${safeMcpPath}/sse',
|
|
3892
3961
|
};
|
|
3893
3962
|
|
|
3894
3963
|
// Load external dashboard UI
|
|
@@ -3904,10 +3973,11 @@ function generateExternalEntrypointHtml(options) {
|
|
|
3904
3973
|
</body>
|
|
3905
3974
|
</html>`;
|
|
3906
3975
|
}
|
|
3907
|
-
function generateInlineDashboardHtml(options) {
|
|
3976
|
+
function generateInlineDashboardHtml(options, mcpPath) {
|
|
3908
3977
|
const { cdn } = options;
|
|
3909
3978
|
const safeBasePath = escapeForJs(options.basePath);
|
|
3910
|
-
const
|
|
3979
|
+
const safeMcpPath = escapeForJs(mcpPath);
|
|
3980
|
+
const sseUrl = `${safeMcpPath}/sse`;
|
|
3911
3981
|
const safeReact = escapeForJs(cdn.react);
|
|
3912
3982
|
const safeReactDom = escapeForJs(cdn.reactDom);
|
|
3913
3983
|
const safeReactDomClient = escapeForJs(cdn.reactDomClient);
|
|
@@ -4105,6 +4175,7 @@ function generateInlineDashboardHtml(options) {
|
|
|
4105
4175
|
// Configuration
|
|
4106
4176
|
const config = {
|
|
4107
4177
|
basePath: '${safeBasePath}',
|
|
4178
|
+
mcpPath: '${safeMcpPath}',
|
|
4108
4179
|
sseUrl: '${sseUrl}',
|
|
4109
4180
|
};
|
|
4110
4181
|
|
|
@@ -4302,7 +4373,7 @@ function generateInlineDashboardHtml(options) {
|
|
|
4302
4373
|
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
|
|
4303
4374
|
|
|
4304
4375
|
useEffect(() => {
|
|
4305
|
-
const client = new McpClient(config.sseUrl, config.
|
|
4376
|
+
const client = new McpClient(config.sseUrl, config.mcpPath);
|
|
4306
4377
|
|
|
4307
4378
|
async function init() {
|
|
4308
4379
|
try {
|
|
@@ -4693,6 +4764,7 @@ var graphToolInputSchema = {
|
|
|
4693
4764
|
};
|
|
4694
4765
|
var GraphTool = class extends ToolContext {
|
|
4695
4766
|
async execute(input) {
|
|
4767
|
+
assertDashboardToolAccess(this);
|
|
4696
4768
|
const graphProvider = this.get(GraphDataProvider);
|
|
4697
4769
|
if (input.refresh) {
|
|
4698
4770
|
graphProvider.invalidateCache();
|
|
@@ -4762,6 +4834,7 @@ var listResourcesOutputSchema = z5.object({
|
|
|
4762
4834
|
});
|
|
4763
4835
|
var ListResourcesTool = class extends ToolContext2 {
|
|
4764
4836
|
async execute(input) {
|
|
4837
|
+
assertDashboardToolAccess(this);
|
|
4765
4838
|
const parentScope = this.tryGet(ParentScopeToken);
|
|
4766
4839
|
const targetScope = parentScope || this.scope;
|
|
4767
4840
|
const results = [];
|
|
@@ -4841,6 +4914,7 @@ var listToolsOutputSchema = z6.object({
|
|
|
4841
4914
|
});
|
|
4842
4915
|
var ListToolsTool = class extends ToolContext3 {
|
|
4843
4916
|
async execute(input) {
|
|
4917
|
+
assertDashboardToolAccess(this);
|
|
4844
4918
|
const parentScope = this.tryGet(ParentScopeToken);
|
|
4845
4919
|
const targetScope = parentScope || this.scope;
|
|
4846
4920
|
let allTools = [];
|
|
@@ -4884,9 +4958,23 @@ ListToolsTool = __decorateClass([
|
|
|
4884
4958
|
|
|
4885
4959
|
// plugins/plugin-dashboard/src/app/dashboard.app.ts
|
|
4886
4960
|
var DashboardMiddlewareToken = /* @__PURE__ */ Symbol("dashboard:middleware");
|
|
4887
|
-
function
|
|
4888
|
-
const
|
|
4889
|
-
|
|
4961
|
+
function sessionCookies(options, paths, secure) {
|
|
4962
|
+
const token = options.auth.token;
|
|
4963
|
+
if (!options.auth.enabled || !token) return [];
|
|
4964
|
+
const value = deriveDashboardCookieValue(token);
|
|
4965
|
+
return paths.map(
|
|
4966
|
+
(path) => `${DASHBOARD_SESSION_COOKIE}=${value}; Path=${path}; HttpOnly; SameSite=Strict${secure ? "; Secure" : ""}`
|
|
4967
|
+
);
|
|
4968
|
+
}
|
|
4969
|
+
function isHttps(req) {
|
|
4970
|
+
const socket = req.socket;
|
|
4971
|
+
const forwarded = req.headers?.["x-forwarded-proto"];
|
|
4972
|
+
return socket?.encrypted === true || typeof forwarded === "string" && forwarded.split(",")[0]?.trim() === "https";
|
|
4973
|
+
}
|
|
4974
|
+
function createDashboardMiddleware(options, mcpPath) {
|
|
4975
|
+
const html = generateDashboardHtml(options, mcpPath);
|
|
4976
|
+
const authorize = createDashboardAuthValidator(options.auth, "page");
|
|
4977
|
+
const cookiePaths = [.../* @__PURE__ */ new Set([mcpPath, options.basePath])];
|
|
4890
4978
|
return async (req, res, next) => {
|
|
4891
4979
|
if (!isDashboardEnabled(options)) {
|
|
4892
4980
|
return next();
|
|
@@ -4894,7 +4982,10 @@ function createDashboardMiddleware(options) {
|
|
|
4894
4982
|
const urlPath = req.path || req.url || "/";
|
|
4895
4983
|
const method = (req.method || "GET").toUpperCase();
|
|
4896
4984
|
const isPageRequest = method === "GET" && (urlPath === "/" || urlPath === "");
|
|
4897
|
-
if (isPageRequest
|
|
4985
|
+
if (!isPageRequest) {
|
|
4986
|
+
return next();
|
|
4987
|
+
}
|
|
4988
|
+
if (authorize) {
|
|
4898
4989
|
const result = authorize({
|
|
4899
4990
|
headers: req.headers,
|
|
4900
4991
|
query: req.query
|
|
@@ -4904,15 +4995,11 @@ function createDashboardMiddleware(options) {
|
|
|
4904
4995
|
return;
|
|
4905
4996
|
}
|
|
4906
4997
|
}
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
res.status(200).send(html);
|
|
4913
|
-
return;
|
|
4914
|
-
}
|
|
4915
|
-
return next();
|
|
4998
|
+
const writer = res;
|
|
4999
|
+
writer.setHeader?.("Content-Type", "text/html");
|
|
5000
|
+
const cookies = sessionCookies(options, cookiePaths, isHttps(req));
|
|
5001
|
+
if (cookies.length > 0) writer.setHeader?.("Set-Cookie", cookies);
|
|
5002
|
+
res.status(200).send(html);
|
|
4916
5003
|
};
|
|
4917
5004
|
}
|
|
4918
5005
|
var DashboardHttpPlugin = class extends DynamicPlugin2 {
|
|
@@ -4924,8 +5011,33 @@ var DashboardHttpPlugin = class extends DynamicPlugin2 {
|
|
|
4924
5011
|
...options
|
|
4925
5012
|
});
|
|
4926
5013
|
}
|
|
5014
|
+
async gateDashboardMcp(flowCtx) {
|
|
5015
|
+
const options = this.resolve(DashboardConfigToken) ?? resolveDashboardOptions();
|
|
5016
|
+
if (!isDashboardEnabled(options)) {
|
|
5017
|
+
throw new DashboardDisabledError();
|
|
5018
|
+
}
|
|
5019
|
+
const authorize = createDashboardAuthValidator(options.auth, "mcp");
|
|
5020
|
+
if (!authorize) return;
|
|
5021
|
+
const request = flowCtx.rawInput.request;
|
|
5022
|
+
const result = authorize({
|
|
5023
|
+
headers: request.headers,
|
|
5024
|
+
query: request.query
|
|
5025
|
+
});
|
|
5026
|
+
if (!result.authorized) {
|
|
5027
|
+
throw new DashboardUnauthorizedError();
|
|
5028
|
+
}
|
|
5029
|
+
markDashboardGatePassed(this.resolve(FrontMcpContextStorage)?.getStore());
|
|
5030
|
+
}
|
|
5031
|
+
/** `this.get`, or `undefined` when the token isn't provided. */
|
|
5032
|
+
resolve(token) {
|
|
5033
|
+
try {
|
|
5034
|
+
return this.get(token);
|
|
5035
|
+
} catch {
|
|
5036
|
+
return void 0;
|
|
5037
|
+
}
|
|
5038
|
+
}
|
|
4927
5039
|
/**
|
|
4928
|
-
* Provide the dashboard config and middleware registration via DI.
|
|
5040
|
+
* Provide the dashboard config and page middleware registration via DI.
|
|
4929
5041
|
*/
|
|
4930
5042
|
static dynamicProviders(options) {
|
|
4931
5043
|
return [
|
|
@@ -4939,17 +5051,20 @@ var DashboardHttpPlugin = class extends DynamicPlugin2 {
|
|
|
4939
5051
|
{
|
|
4940
5052
|
name: "dashboard:middleware",
|
|
4941
5053
|
provide: DashboardMiddlewareToken,
|
|
4942
|
-
inject: () => [FrontMcpServer],
|
|
4943
|
-
useFactory: (server) => {
|
|
5054
|
+
inject: () => [FrontMcpServer, ScopeEntry2],
|
|
5055
|
+
useFactory: (server, scope) => {
|
|
4944
5056
|
const effectiveOptions = resolveDashboardOptions(options);
|
|
4945
|
-
const
|
|
4946
|
-
server.registerMiddleware(effectiveOptions.basePath,
|
|
4947
|
-
return { registered: true };
|
|
5057
|
+
const mcpPath = scope.fullPath;
|
|
5058
|
+
server.registerMiddleware(effectiveOptions.basePath, createDashboardMiddleware(effectiveOptions, mcpPath));
|
|
5059
|
+
return { registered: true, mcpPath };
|
|
4948
5060
|
}
|
|
4949
5061
|
}
|
|
4950
5062
|
];
|
|
4951
5063
|
}
|
|
4952
5064
|
};
|
|
5065
|
+
__decorateClass([
|
|
5066
|
+
HttpHook.Will("handleMcp2026", { priority: 1e3 })
|
|
5067
|
+
], DashboardHttpPlugin.prototype, "gateDashboardMcp", 1);
|
|
4953
5068
|
DashboardHttpPlugin = __decorateClass([
|
|
4954
5069
|
Plugin2({
|
|
4955
5070
|
name: "dashboard:http",
|
package/esm/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frontmcp/plugin-dashboard",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.4",
|
|
4
4
|
"description": "Dashboard plugin for FrontMCP - visual monitoring and introspection of MCP servers",
|
|
5
5
|
"author": "AgentFront <info@agentfront.dev>",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -47,12 +47,12 @@
|
|
|
47
47
|
}
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@frontmcp/sdk": "1.8.
|
|
50
|
+
"@frontmcp/sdk": "1.8.4"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"reflect-metadata": "^0.2.2"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
|
-
"@frontmcp/lazy-zod": "1.8.
|
|
56
|
+
"@frontmcp/lazy-zod": "1.8.4"
|
|
57
57
|
}
|
|
58
58
|
}
|
package/html/html.generator.d.ts
CHANGED
|
@@ -11,9 +11,12 @@ import type { DashboardPluginOptions } from '../dashboard.types';
|
|
|
11
11
|
*
|
|
12
12
|
* The configured `auth.token` is deliberately NOT embedded here, and not put in
|
|
13
13
|
* the SSE query string: a URL token lands in access logs, `Referer` headers and
|
|
14
|
-
* browser history,
|
|
15
|
-
*
|
|
16
|
-
*
|
|
14
|
+
* browser history (GHSA-rgxj-434m-vxh3). When `auth` is on, the page's MCP
|
|
15
|
+
* client authenticates with the HttpOnly cookie the server sets with the page.
|
|
16
|
+
*
|
|
17
|
+
* @param mcpPath - where the dashboard's MCP endpoint is served (its scope's
|
|
18
|
+
* route, `/dashboard` unless the server has an `entryPath`), which `basePath`
|
|
19
|
+
* does not move. Defaults to `basePath`.
|
|
17
20
|
*/
|
|
18
|
-
export declare function generateDashboardHtml(options: DashboardPluginOptions): string;
|
|
21
|
+
export declare function generateDashboardHtml(options: DashboardPluginOptions, mcpPath?: string): string;
|
|
19
22
|
//# sourceMappingURL=html.generator.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"html.generator.d.ts","sourceRoot":"","sources":["../../src/html/html.generator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAiBjE;;;;;;GAMG;AACH
|
|
1
|
+
{"version":3,"file":"html.generator.d.ts","sourceRoot":"","sources":["../../src/html/html.generator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAiBjE;;;;;;GAMG;AACH;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,sBAAsB,EAAE,OAAO,GAAE,MAAyB,GAAG,MAAM,CAQjH"}
|
package/index.js
CHANGED
|
@@ -437,7 +437,10 @@ DashboardPlugin = __decorateClass([
|
|
|
437
437
|
], DashboardPlugin);
|
|
438
438
|
|
|
439
439
|
// plugins/plugin-dashboard/src/app/dashboard.app.ts
|
|
440
|
-
var
|
|
440
|
+
var import_sdk7 = require("@frontmcp/sdk");
|
|
441
|
+
|
|
442
|
+
// plugins/plugin-dashboard/src/auth/dashboard-access.ts
|
|
443
|
+
var import_sdk2 = require("@frontmcp/sdk");
|
|
441
444
|
|
|
442
445
|
// libs/utils/dist/esm/index.mjs
|
|
443
446
|
var import_module = require("module");
|
|
@@ -2620,6 +2623,9 @@ function randomBytes(length) {
|
|
|
2620
2623
|
function sha256(data) {
|
|
2621
2624
|
return getCrypto().sha256(data);
|
|
2622
2625
|
}
|
|
2626
|
+
function hmacSha256(key, data) {
|
|
2627
|
+
return getCrypto().hmacSha256(key, data);
|
|
2628
|
+
}
|
|
2623
2629
|
function encryptAesGcm(key, plaintext, iv) {
|
|
2624
2630
|
if (key.length !== 32) {
|
|
2625
2631
|
throw new Error(`AES-256-GCM requires a 32-byte key, got ${key.length} bytes`);
|
|
@@ -3810,6 +3816,8 @@ init_crypto();
|
|
|
3810
3816
|
init_runtime();
|
|
3811
3817
|
|
|
3812
3818
|
// plugins/plugin-dashboard/src/auth/dashboard-auth.ts
|
|
3819
|
+
var DASHBOARD_TOKEN_HEADER = "x-frontmcp-dashboard-token";
|
|
3820
|
+
var DASHBOARD_SESSION_COOKIE = "frontmcp_dashboard";
|
|
3813
3821
|
var AUTHORIZED = { authorized: true };
|
|
3814
3822
|
function readHeader(headers, name) {
|
|
3815
3823
|
if (!headers) return void 0;
|
|
@@ -3825,6 +3833,19 @@ function tokenMatches(presented, expected) {
|
|
|
3825
3833
|
if (presented.length === 0 || expected.length === 0) return false;
|
|
3826
3834
|
return timingSafeEqual(sha256(presented), sha256(expected));
|
|
3827
3835
|
}
|
|
3836
|
+
var encoder = new TextEncoder();
|
|
3837
|
+
function deriveDashboardCookieValue(token) {
|
|
3838
|
+
return base64urlEncode(hmacSha256(encoder.encode(token), encoder.encode("frontmcp-dashboard-session:v1")));
|
|
3839
|
+
}
|
|
3840
|
+
function readCookie(header, name) {
|
|
3841
|
+
if (!header) return void 0;
|
|
3842
|
+
for (const part of header.split(";")) {
|
|
3843
|
+
const separator = part.indexOf("=");
|
|
3844
|
+
if (separator === -1) continue;
|
|
3845
|
+
if (part.slice(0, separator).trim() === name) return part.slice(separator + 1).trim();
|
|
3846
|
+
}
|
|
3847
|
+
return void 0;
|
|
3848
|
+
}
|
|
3828
3849
|
function readBearerToken(header) {
|
|
3829
3850
|
if (!header) return void 0;
|
|
3830
3851
|
const separator = header.indexOf(" ");
|
|
@@ -3833,36 +3854,81 @@ function readBearerToken(header) {
|
|
|
3833
3854
|
const token = header.slice(separator + 1).trim();
|
|
3834
3855
|
return token.length > 0 ? token : void 0;
|
|
3835
3856
|
}
|
|
3836
|
-
function createDashboardAuthValidator(auth) {
|
|
3857
|
+
function createDashboardAuthValidator(auth, surface = "page") {
|
|
3837
3858
|
if (!auth?.enabled) return void 0;
|
|
3838
3859
|
const expected = auth.token;
|
|
3860
|
+
const expectedCookie = expected ? deriveDashboardCookieValue(expected) : void 0;
|
|
3839
3861
|
return (req) => {
|
|
3840
|
-
if (!expected) {
|
|
3862
|
+
if (!expected || !expectedCookie) {
|
|
3841
3863
|
return { authorized: false, status: 401, message: "Dashboard authentication is misconfigured" };
|
|
3842
3864
|
}
|
|
3843
3865
|
const bearer = readBearerToken(readHeader(req.headers, "authorization"));
|
|
3844
3866
|
if (bearer && tokenMatches(bearer, expected)) return AUTHORIZED;
|
|
3845
|
-
|
|
3846
|
-
|
|
3867
|
+
if (surface === "mcp") {
|
|
3868
|
+
const headerToken = readHeader(req.headers, DASHBOARD_TOKEN_HEADER);
|
|
3869
|
+
if (headerToken && tokenMatches(headerToken, expected)) return AUTHORIZED;
|
|
3870
|
+
} else {
|
|
3871
|
+
const queryToken = readQuery(req.query, "token");
|
|
3872
|
+
if (queryToken && tokenMatches(queryToken, expected)) return AUTHORIZED;
|
|
3873
|
+
}
|
|
3874
|
+
const cookie = readCookie(readHeader(req.headers, "cookie"), DASHBOARD_SESSION_COOKIE);
|
|
3875
|
+
if (cookie && tokenMatches(cookie, expectedCookie)) return AUTHORIZED;
|
|
3847
3876
|
return { authorized: false, status: 401, message: "Unauthorized" };
|
|
3848
3877
|
};
|
|
3849
3878
|
}
|
|
3850
3879
|
|
|
3880
|
+
// plugins/plugin-dashboard/src/auth/dashboard-access.ts
|
|
3881
|
+
var GATE_PASSED = /* @__PURE__ */ Symbol("frontmcp:dashboard:gate-passed");
|
|
3882
|
+
function markDashboardGatePassed(ctx) {
|
|
3883
|
+
ctx?.set(GATE_PASSED, true);
|
|
3884
|
+
}
|
|
3885
|
+
var DashboardDisabledError = class extends import_sdk2.PublicMcpError {
|
|
3886
|
+
mcpErrorCode = import_sdk2.MCP_ERROR_CODES.FORBIDDEN;
|
|
3887
|
+
constructor() {
|
|
3888
|
+
super("The FrontMCP dashboard is disabled.", "DASHBOARD_DISABLED", 404);
|
|
3889
|
+
}
|
|
3890
|
+
};
|
|
3891
|
+
var DashboardUnauthorizedError = class extends import_sdk2.PublicMcpError {
|
|
3892
|
+
mcpErrorCode = import_sdk2.MCP_ERROR_CODES.UNAUTHORIZED;
|
|
3893
|
+
constructor() {
|
|
3894
|
+
super(
|
|
3895
|
+
`The dashboard token is required. Send it in the ${DASHBOARD_TOKEN_HEADER} header.`,
|
|
3896
|
+
"DASHBOARD_UNAUTHORIZED",
|
|
3897
|
+
401,
|
|
3898
|
+
'Bearer realm="frontmcp-dashboard"'
|
|
3899
|
+
);
|
|
3900
|
+
}
|
|
3901
|
+
};
|
|
3902
|
+
function assertDashboardAccess(options, ctx) {
|
|
3903
|
+
if (!isDashboardEnabled(options)) {
|
|
3904
|
+
throw new DashboardDisabledError();
|
|
3905
|
+
}
|
|
3906
|
+
if (!options.auth?.enabled) return;
|
|
3907
|
+
if (ctx?.get(GATE_PASSED) === true) return;
|
|
3908
|
+
const expected = options.auth.token;
|
|
3909
|
+
const presented = ctx?.metadata?.customHeaders?.[DASHBOARD_TOKEN_HEADER];
|
|
3910
|
+
if (expected && presented && tokenMatches(presented, expected)) return;
|
|
3911
|
+
throw new DashboardUnauthorizedError();
|
|
3912
|
+
}
|
|
3913
|
+
function assertDashboardToolAccess(tool) {
|
|
3914
|
+
const configured = tool.tryGet(DashboardConfigToken);
|
|
3915
|
+
assertDashboardAccess(configured ?? resolveDashboardOptions(), tool.tryGetContext());
|
|
3916
|
+
}
|
|
3917
|
+
|
|
3851
3918
|
// plugins/plugin-dashboard/src/html/html.generator.ts
|
|
3852
3919
|
function escapeForJs(str) {
|
|
3853
3920
|
return str.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/"/g, '\\"').replace(/</g, "\\x3c").replace(/>/g, "\\x3e").replace(/\n/g, "\\n").replace(/\r/g, "\\r");
|
|
3854
3921
|
}
|
|
3855
|
-
function generateDashboardHtml(options) {
|
|
3856
|
-
|
|
3857
|
-
|
|
3858
|
-
if (cdn.entrypoint) {
|
|
3859
|
-
return generateExternalEntrypointHtml(options);
|
|
3922
|
+
function generateDashboardHtml(options, mcpPath = options.basePath) {
|
|
3923
|
+
if (options.cdn.entrypoint) {
|
|
3924
|
+
return generateExternalEntrypointHtml(options, mcpPath);
|
|
3860
3925
|
}
|
|
3861
|
-
return generateInlineDashboardHtml(options);
|
|
3926
|
+
return generateInlineDashboardHtml(options, mcpPath);
|
|
3862
3927
|
}
|
|
3863
|
-
function generateExternalEntrypointHtml(options) {
|
|
3928
|
+
function generateExternalEntrypointHtml(options, mcpPath) {
|
|
3864
3929
|
const { cdn } = options;
|
|
3865
3930
|
const safeBasePath = escapeForJs(options.basePath);
|
|
3931
|
+
const safeMcpPath = escapeForJs(mcpPath);
|
|
3866
3932
|
const safeReact = escapeForJs(cdn.react);
|
|
3867
3933
|
const safeReactDom = escapeForJs(cdn.reactDom);
|
|
3868
3934
|
const safeReactDomClient = escapeForJs(cdn.reactDomClient);
|
|
@@ -3902,7 +3968,8 @@ function generateExternalEntrypointHtml(options) {
|
|
|
3902
3968
|
// Dashboard configuration
|
|
3903
3969
|
window.__FRONTMCP_DASHBOARD__ = {
|
|
3904
3970
|
basePath: '${safeBasePath}',
|
|
3905
|
-
|
|
3971
|
+
mcpPath: '${safeMcpPath}',
|
|
3972
|
+
sseUrl: '${safeMcpPath}/sse',
|
|
3906
3973
|
};
|
|
3907
3974
|
|
|
3908
3975
|
// Load external dashboard UI
|
|
@@ -3918,10 +3985,11 @@ function generateExternalEntrypointHtml(options) {
|
|
|
3918
3985
|
</body>
|
|
3919
3986
|
</html>`;
|
|
3920
3987
|
}
|
|
3921
|
-
function generateInlineDashboardHtml(options) {
|
|
3988
|
+
function generateInlineDashboardHtml(options, mcpPath) {
|
|
3922
3989
|
const { cdn } = options;
|
|
3923
3990
|
const safeBasePath = escapeForJs(options.basePath);
|
|
3924
|
-
const
|
|
3991
|
+
const safeMcpPath = escapeForJs(mcpPath);
|
|
3992
|
+
const sseUrl = `${safeMcpPath}/sse`;
|
|
3925
3993
|
const safeReact = escapeForJs(cdn.react);
|
|
3926
3994
|
const safeReactDom = escapeForJs(cdn.reactDom);
|
|
3927
3995
|
const safeReactDomClient = escapeForJs(cdn.reactDomClient);
|
|
@@ -4119,6 +4187,7 @@ function generateInlineDashboardHtml(options) {
|
|
|
4119
4187
|
// Configuration
|
|
4120
4188
|
const config = {
|
|
4121
4189
|
basePath: '${safeBasePath}',
|
|
4190
|
+
mcpPath: '${safeMcpPath}',
|
|
4122
4191
|
sseUrl: '${sseUrl}',
|
|
4123
4192
|
};
|
|
4124
4193
|
|
|
@@ -4316,7 +4385,7 @@ function generateInlineDashboardHtml(options) {
|
|
|
4316
4385
|
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
|
|
4317
4386
|
|
|
4318
4387
|
useEffect(() => {
|
|
4319
|
-
const client = new McpClient(config.sseUrl, config.
|
|
4388
|
+
const client = new McpClient(config.sseUrl, config.mcpPath);
|
|
4320
4389
|
|
|
4321
4390
|
async function init() {
|
|
4322
4391
|
try {
|
|
@@ -4433,7 +4502,7 @@ function generateInlineDashboardHtml(options) {
|
|
|
4433
4502
|
}
|
|
4434
4503
|
|
|
4435
4504
|
// plugins/plugin-dashboard/src/providers/graph-data.provider.ts
|
|
4436
|
-
var
|
|
4505
|
+
var import_sdk3 = require("@frontmcp/sdk");
|
|
4437
4506
|
var GraphDataProvider = class {
|
|
4438
4507
|
constructor(scope, serverName, serverVersion) {
|
|
4439
4508
|
this.scope = scope;
|
|
@@ -4693,20 +4762,21 @@ var GraphDataProvider = class {
|
|
|
4693
4762
|
}
|
|
4694
4763
|
};
|
|
4695
4764
|
GraphDataProvider = __decorateClass([
|
|
4696
|
-
(0,
|
|
4765
|
+
(0, import_sdk3.Provider)({
|
|
4697
4766
|
name: "graph-data-provider"
|
|
4698
4767
|
})
|
|
4699
4768
|
], GraphDataProvider);
|
|
4700
4769
|
|
|
4701
4770
|
// plugins/plugin-dashboard/src/tools/graph.tool.ts
|
|
4702
4771
|
var import_lazy_zod5 = require("@frontmcp/lazy-zod");
|
|
4703
|
-
var
|
|
4772
|
+
var import_sdk4 = require("@frontmcp/sdk");
|
|
4704
4773
|
var graphToolInputSchema = {
|
|
4705
4774
|
includeSchemas: import_lazy_zod5.z.boolean().optional().default(false).describe("Include full input/output schemas in the response"),
|
|
4706
4775
|
refresh: import_lazy_zod5.z.boolean().optional().default(false).describe("Force refresh the graph data (bypass cache)")
|
|
4707
4776
|
};
|
|
4708
|
-
var GraphTool = class extends
|
|
4777
|
+
var GraphTool = class extends import_sdk4.ToolContext {
|
|
4709
4778
|
async execute(input) {
|
|
4779
|
+
assertDashboardToolAccess(this);
|
|
4710
4780
|
const graphProvider = this.get(GraphDataProvider);
|
|
4711
4781
|
if (input.refresh) {
|
|
4712
4782
|
graphProvider.invalidateCache();
|
|
@@ -4729,7 +4799,7 @@ var GraphTool = class extends import_sdk3.ToolContext {
|
|
|
4729
4799
|
}
|
|
4730
4800
|
};
|
|
4731
4801
|
GraphTool = __decorateClass([
|
|
4732
|
-
(0,
|
|
4802
|
+
(0, import_sdk4.Tool)({
|
|
4733
4803
|
name: "dashboard:graph",
|
|
4734
4804
|
description: "Get the server graph showing all registered tools, resources, prompts, apps, and their relationships. Returns nodes and edges that can be visualized.",
|
|
4735
4805
|
inputSchema: graphToolInputSchema,
|
|
@@ -4741,7 +4811,7 @@ GraphTool = __decorateClass([
|
|
|
4741
4811
|
|
|
4742
4812
|
// plugins/plugin-dashboard/src/tools/list-resources.tool.ts
|
|
4743
4813
|
var import_lazy_zod6 = require("@frontmcp/lazy-zod");
|
|
4744
|
-
var
|
|
4814
|
+
var import_sdk5 = require("@frontmcp/sdk");
|
|
4745
4815
|
|
|
4746
4816
|
// plugins/plugin-dashboard/src/shared/safe-regex.ts
|
|
4747
4817
|
function safeRegex(pattern) {
|
|
@@ -4774,8 +4844,9 @@ var listResourcesOutputSchema = import_lazy_zod6.z.object({
|
|
|
4774
4844
|
),
|
|
4775
4845
|
count: import_lazy_zod6.z.number()
|
|
4776
4846
|
});
|
|
4777
|
-
var ListResourcesTool = class extends
|
|
4847
|
+
var ListResourcesTool = class extends import_sdk5.ToolContext {
|
|
4778
4848
|
async execute(input) {
|
|
4849
|
+
assertDashboardToolAccess(this);
|
|
4779
4850
|
const parentScope = this.tryGet(ParentScopeToken);
|
|
4780
4851
|
const targetScope = parentScope || this.scope;
|
|
4781
4852
|
const results = [];
|
|
@@ -4821,7 +4892,7 @@ var ListResourcesTool = class extends import_sdk4.ToolContext {
|
|
|
4821
4892
|
}
|
|
4822
4893
|
};
|
|
4823
4894
|
ListResourcesTool = __decorateClass([
|
|
4824
|
-
(0,
|
|
4895
|
+
(0, import_sdk5.Tool)({
|
|
4825
4896
|
name: "dashboard:list-resources",
|
|
4826
4897
|
description: "List all resources and resource templates registered in the monitored FrontMCP server.",
|
|
4827
4898
|
inputSchema: listResourcesInputSchema,
|
|
@@ -4834,7 +4905,7 @@ ListResourcesTool = __decorateClass([
|
|
|
4834
4905
|
|
|
4835
4906
|
// plugins/plugin-dashboard/src/tools/list-tools.tool.ts
|
|
4836
4907
|
var import_lazy_zod7 = require("@frontmcp/lazy-zod");
|
|
4837
|
-
var
|
|
4908
|
+
var import_sdk6 = require("@frontmcp/sdk");
|
|
4838
4909
|
var listToolsInputSchema = {
|
|
4839
4910
|
filter: import_lazy_zod7.z.string().optional().describe("Filter tools by name pattern (regex supported)"),
|
|
4840
4911
|
includePlugins: import_lazy_zod7.z.boolean().default(true).describe("Include tools from plugins"),
|
|
@@ -4853,8 +4924,9 @@ var listToolsOutputSchema = import_lazy_zod7.z.object({
|
|
|
4853
4924
|
),
|
|
4854
4925
|
count: import_lazy_zod7.z.number()
|
|
4855
4926
|
});
|
|
4856
|
-
var ListToolsTool = class extends
|
|
4927
|
+
var ListToolsTool = class extends import_sdk6.ToolContext {
|
|
4857
4928
|
async execute(input) {
|
|
4929
|
+
assertDashboardToolAccess(this);
|
|
4858
4930
|
const parentScope = this.tryGet(ParentScopeToken);
|
|
4859
4931
|
const targetScope = parentScope || this.scope;
|
|
4860
4932
|
let allTools = [];
|
|
@@ -4885,7 +4957,7 @@ var ListToolsTool = class extends import_sdk5.ToolContext {
|
|
|
4885
4957
|
}
|
|
4886
4958
|
};
|
|
4887
4959
|
ListToolsTool = __decorateClass([
|
|
4888
|
-
(0,
|
|
4960
|
+
(0, import_sdk6.Tool)({
|
|
4889
4961
|
name: "dashboard:list-tools",
|
|
4890
4962
|
description: "List all tools registered in the monitored FrontMCP server. Returns tool names, descriptions, and optionally schemas.",
|
|
4891
4963
|
inputSchema: listToolsInputSchema,
|
|
@@ -4898,9 +4970,23 @@ ListToolsTool = __decorateClass([
|
|
|
4898
4970
|
|
|
4899
4971
|
// plugins/plugin-dashboard/src/app/dashboard.app.ts
|
|
4900
4972
|
var DashboardMiddlewareToken = /* @__PURE__ */ Symbol("dashboard:middleware");
|
|
4901
|
-
function
|
|
4902
|
-
const
|
|
4903
|
-
|
|
4973
|
+
function sessionCookies(options, paths, secure) {
|
|
4974
|
+
const token = options.auth.token;
|
|
4975
|
+
if (!options.auth.enabled || !token) return [];
|
|
4976
|
+
const value = deriveDashboardCookieValue(token);
|
|
4977
|
+
return paths.map(
|
|
4978
|
+
(path) => `${DASHBOARD_SESSION_COOKIE}=${value}; Path=${path}; HttpOnly; SameSite=Strict${secure ? "; Secure" : ""}`
|
|
4979
|
+
);
|
|
4980
|
+
}
|
|
4981
|
+
function isHttps(req) {
|
|
4982
|
+
const socket = req.socket;
|
|
4983
|
+
const forwarded = req.headers?.["x-forwarded-proto"];
|
|
4984
|
+
return socket?.encrypted === true || typeof forwarded === "string" && forwarded.split(",")[0]?.trim() === "https";
|
|
4985
|
+
}
|
|
4986
|
+
function createDashboardMiddleware(options, mcpPath) {
|
|
4987
|
+
const html = generateDashboardHtml(options, mcpPath);
|
|
4988
|
+
const authorize = createDashboardAuthValidator(options.auth, "page");
|
|
4989
|
+
const cookiePaths = [.../* @__PURE__ */ new Set([mcpPath, options.basePath])];
|
|
4904
4990
|
return async (req, res, next) => {
|
|
4905
4991
|
if (!isDashboardEnabled(options)) {
|
|
4906
4992
|
return next();
|
|
@@ -4908,7 +4994,10 @@ function createDashboardMiddleware(options) {
|
|
|
4908
4994
|
const urlPath = req.path || req.url || "/";
|
|
4909
4995
|
const method = (req.method || "GET").toUpperCase();
|
|
4910
4996
|
const isPageRequest = method === "GET" && (urlPath === "/" || urlPath === "");
|
|
4911
|
-
if (isPageRequest
|
|
4997
|
+
if (!isPageRequest) {
|
|
4998
|
+
return next();
|
|
4999
|
+
}
|
|
5000
|
+
if (authorize) {
|
|
4912
5001
|
const result = authorize({
|
|
4913
5002
|
headers: req.headers,
|
|
4914
5003
|
query: req.query
|
|
@@ -4918,18 +5007,14 @@ function createDashboardMiddleware(options) {
|
|
|
4918
5007
|
return;
|
|
4919
5008
|
}
|
|
4920
5009
|
}
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
|
|
4925
|
-
|
|
4926
|
-
res.status(200).send(html);
|
|
4927
|
-
return;
|
|
4928
|
-
}
|
|
4929
|
-
return next();
|
|
5010
|
+
const writer = res;
|
|
5011
|
+
writer.setHeader?.("Content-Type", "text/html");
|
|
5012
|
+
const cookies = sessionCookies(options, cookiePaths, isHttps(req));
|
|
5013
|
+
if (cookies.length > 0) writer.setHeader?.("Set-Cookie", cookies);
|
|
5014
|
+
res.status(200).send(html);
|
|
4930
5015
|
};
|
|
4931
5016
|
}
|
|
4932
|
-
var DashboardHttpPlugin = class extends
|
|
5017
|
+
var DashboardHttpPlugin = class extends import_sdk7.DynamicPlugin {
|
|
4933
5018
|
options;
|
|
4934
5019
|
constructor(options = {}) {
|
|
4935
5020
|
super();
|
|
@@ -4938,8 +5023,33 @@ var DashboardHttpPlugin = class extends import_sdk6.DynamicPlugin {
|
|
|
4938
5023
|
...options
|
|
4939
5024
|
});
|
|
4940
5025
|
}
|
|
5026
|
+
async gateDashboardMcp(flowCtx) {
|
|
5027
|
+
const options = this.resolve(DashboardConfigToken) ?? resolveDashboardOptions();
|
|
5028
|
+
if (!isDashboardEnabled(options)) {
|
|
5029
|
+
throw new DashboardDisabledError();
|
|
5030
|
+
}
|
|
5031
|
+
const authorize = createDashboardAuthValidator(options.auth, "mcp");
|
|
5032
|
+
if (!authorize) return;
|
|
5033
|
+
const request = flowCtx.rawInput.request;
|
|
5034
|
+
const result = authorize({
|
|
5035
|
+
headers: request.headers,
|
|
5036
|
+
query: request.query
|
|
5037
|
+
});
|
|
5038
|
+
if (!result.authorized) {
|
|
5039
|
+
throw new DashboardUnauthorizedError();
|
|
5040
|
+
}
|
|
5041
|
+
markDashboardGatePassed(this.resolve(import_sdk7.FrontMcpContextStorage)?.getStore());
|
|
5042
|
+
}
|
|
5043
|
+
/** `this.get`, or `undefined` when the token isn't provided. */
|
|
5044
|
+
resolve(token) {
|
|
5045
|
+
try {
|
|
5046
|
+
return this.get(token);
|
|
5047
|
+
} catch {
|
|
5048
|
+
return void 0;
|
|
5049
|
+
}
|
|
5050
|
+
}
|
|
4941
5051
|
/**
|
|
4942
|
-
* Provide the dashboard config and middleware registration via DI.
|
|
5052
|
+
* Provide the dashboard config and page middleware registration via DI.
|
|
4943
5053
|
*/
|
|
4944
5054
|
static dynamicProviders(options) {
|
|
4945
5055
|
return [
|
|
@@ -4953,19 +5063,22 @@ var DashboardHttpPlugin = class extends import_sdk6.DynamicPlugin {
|
|
|
4953
5063
|
{
|
|
4954
5064
|
name: "dashboard:middleware",
|
|
4955
5065
|
provide: DashboardMiddlewareToken,
|
|
4956
|
-
inject: () => [
|
|
4957
|
-
useFactory: (server) => {
|
|
5066
|
+
inject: () => [import_sdk7.FrontMcpServer, import_sdk7.ScopeEntry],
|
|
5067
|
+
useFactory: (server, scope) => {
|
|
4958
5068
|
const effectiveOptions = resolveDashboardOptions(options);
|
|
4959
|
-
const
|
|
4960
|
-
server.registerMiddleware(effectiveOptions.basePath,
|
|
4961
|
-
return { registered: true };
|
|
5069
|
+
const mcpPath = scope.fullPath;
|
|
5070
|
+
server.registerMiddleware(effectiveOptions.basePath, createDashboardMiddleware(effectiveOptions, mcpPath));
|
|
5071
|
+
return { registered: true, mcpPath };
|
|
4962
5072
|
}
|
|
4963
5073
|
}
|
|
4964
5074
|
];
|
|
4965
5075
|
}
|
|
4966
5076
|
};
|
|
5077
|
+
__decorateClass([
|
|
5078
|
+
import_sdk7.HttpHook.Will("handleMcp2026", { priority: 1e3 })
|
|
5079
|
+
], DashboardHttpPlugin.prototype, "gateDashboardMcp", 1);
|
|
4967
5080
|
DashboardHttpPlugin = __decorateClass([
|
|
4968
|
-
(0,
|
|
5081
|
+
(0, import_sdk7.Plugin)({
|
|
4969
5082
|
name: "dashboard:http",
|
|
4970
5083
|
description: "Dashboard HTTP handler for serving UI HTML"
|
|
4971
5084
|
})
|
|
@@ -4973,7 +5086,7 @@ DashboardHttpPlugin = __decorateClass([
|
|
|
4973
5086
|
var DashboardApp = class {
|
|
4974
5087
|
};
|
|
4975
5088
|
DashboardApp = __decorateClass([
|
|
4976
|
-
(0,
|
|
5089
|
+
(0, import_sdk7.App)({
|
|
4977
5090
|
name: "dashboard",
|
|
4978
5091
|
description: "FrontMCP Dashboard for visualization and monitoring",
|
|
4979
5092
|
providers: [
|
|
@@ -4981,7 +5094,7 @@ DashboardApp = __decorateClass([
|
|
|
4981
5094
|
{
|
|
4982
5095
|
name: "dashboard:parent-scope",
|
|
4983
5096
|
provide: ParentScopeToken,
|
|
4984
|
-
inject: () => [
|
|
5097
|
+
inject: () => [import_sdk7.ScopeEntry],
|
|
4985
5098
|
useFactory: (scope) => {
|
|
4986
5099
|
return scope;
|
|
4987
5100
|
}
|
|
@@ -4990,7 +5103,7 @@ DashboardApp = __decorateClass([
|
|
|
4990
5103
|
{
|
|
4991
5104
|
name: "dashboard:graph-data",
|
|
4992
5105
|
provide: GraphDataProvider,
|
|
4993
|
-
inject: () => [
|
|
5106
|
+
inject: () => [import_sdk7.ScopeEntry, import_sdk7.FrontMcpConfig],
|
|
4994
5107
|
useFactory: (scope, config) => {
|
|
4995
5108
|
const serverName = config.info?.name || "FrontMCP Server";
|
|
4996
5109
|
const serverVersion = config.info?.version;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frontmcp/plugin-dashboard",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.4",
|
|
4
4
|
"description": "Dashboard plugin for FrontMCP - visual monitoring and introspection of MCP servers",
|
|
5
5
|
"author": "AgentFront <info@agentfront.dev>",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -47,12 +47,12 @@
|
|
|
47
47
|
}
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@frontmcp/sdk": "1.8.
|
|
50
|
+
"@frontmcp/sdk": "1.8.4"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"reflect-metadata": "^0.2.2"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
|
-
"@frontmcp/lazy-zod": "1.8.
|
|
56
|
+
"@frontmcp/lazy-zod": "1.8.4"
|
|
57
57
|
}
|
|
58
58
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graph.tool.d.ts","sourceRoot":"","sources":["../../src/tools/graph.tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,oBAAoB,CAAC;AACvC,OAAO,EAAQ,WAAW,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"graph.tool.d.ts","sourceRoot":"","sources":["../../src/tools/graph.tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,oBAAoB,CAAC;AACvC,OAAO,EAAQ,WAAW,EAAE,MAAM,eAAe,CAAC;AAIlD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD;;GAEG;AACH,eAAO,MAAM,oBAAoB;;;CAGhC,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,OAAO,oBAAoB,CAAC,CAAC,CAAC;AAE/E;;GAEG;AAUH,MAAM,CAAC,OAAO,OAAO,SAAU,SAAQ,WAAW;IAC1C,OAAO,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,SAAS,CAAC;CA6BzD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"list-resources.tool.d.ts","sourceRoot":"","sources":["../../src/tools/list-resources.tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,oBAAoB,CAAC;AACvC,OAAO,EAAQ,WAAW,EAAmB,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"list-resources.tool.d.ts","sourceRoot":"","sources":["../../src/tools/list-resources.tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,oBAAoB,CAAC;AACvC,OAAO,EAAQ,WAAW,EAAmB,MAAM,eAAe,CAAC;AAMnE;;GAEG;AACH,eAAO,MAAM,wBAAwB;;;CAGpC,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,OAAO,wBAAwB,CAAC,CAAC,CAAC;AAExF;;GAEG;AACH,eAAO,MAAM,yBAAyB;;;;;;;;;gCAWpC,CAAC;AAEH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAE7E;;GAEG;AAUH,MAAM,CAAC,OAAO,OAAO,iBAAkB,SAAQ,WAAW;IAClD,OAAO,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,CAAC;CA0DvE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"list-tools.tool.d.ts","sourceRoot":"","sources":["../../src/tools/list-tools.tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,oBAAoB,CAAC;AACvC,OAAO,EAAQ,WAAW,EAAmB,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"list-tools.tool.d.ts","sourceRoot":"","sources":["../../src/tools/list-tools.tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,oBAAoB,CAAC;AACvC,OAAO,EAAQ,WAAW,EAAmB,MAAM,eAAe,CAAC;AAMnE;;GAEG;AACH,eAAO,MAAM,oBAAoB;;;;CAIhC,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,OAAO,oBAAoB,CAAC,CAAC,CAAC;AAEhF;;GAEG;AACH,eAAO,MAAM,qBAAqB;;;;;;;;;;gCAYhC,CAAC;AAEH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAErE;;GAEG;AAWH,MAAM,CAAC,OAAO,OAAO,aAAc,SAAQ,WAAW;IAC9C,OAAO,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC;CAkD/D"}
|