@frontmcp/testing 1.3.0 → 1.4.1
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/auth/token-factory.d.ts +28 -2
- package/auth/token-factory.d.ts.map +1 -1
- package/esm/fixtures/index.mjs +190 -161
- package/esm/index.mjs +66 -16
- package/esm/package.json +11 -11
- package/esm/perf/index.mjs +40 -12
- package/fixtures/index.js +187 -158
- package/fixtures/test-fixture.d.ts.map +1 -1
- package/index.d.ts +1 -1
- package/index.d.ts.map +1 -1
- package/index.js +63 -12
- package/package.json +11 -11
- package/perf/index.js +39 -11
- package/raw-client/index.d.ts +1 -0
- package/raw-client/index.d.ts.map +1 -1
- package/raw-client/port-probe.d.ts +15 -0
- package/raw-client/port-probe.d.ts.map +1 -0
- package/server/port-registry.d.ts +4 -0
- package/server/port-registry.d.ts.map +1 -1
package/esm/perf/index.mjs
CHANGED
|
@@ -2841,7 +2841,7 @@ var ProgressCollector = class {
|
|
|
2841
2841
|
};
|
|
2842
2842
|
|
|
2843
2843
|
// libs/testing/src/auth/token-factory.ts
|
|
2844
|
-
import {
|
|
2844
|
+
import { exportJWK, generateKeyPair, SignJWT } from "jose";
|
|
2845
2845
|
var TestTokenFactory = class {
|
|
2846
2846
|
issuer;
|
|
2847
2847
|
audience;
|
|
@@ -2849,15 +2849,39 @@ var TestTokenFactory = class {
|
|
|
2849
2849
|
publicKey = null;
|
|
2850
2850
|
jwk = null;
|
|
2851
2851
|
keyId;
|
|
2852
|
+
/** HS256 shared secret (gateway mode). Null → RS256 + JWKS (default). */
|
|
2853
|
+
hmacSecret;
|
|
2852
2854
|
constructor(options = {}) {
|
|
2853
2855
|
this.issuer = options.issuer ?? "https://test.frontmcp.local";
|
|
2854
2856
|
this.audience = options.audience ?? "frontmcp-test";
|
|
2855
2857
|
this.keyId = `test-key-${Date.now()}`;
|
|
2858
|
+
this.hmacSecret = options.hmacSecret ? new TextEncoder().encode(options.hmacSecret) : null;
|
|
2859
|
+
}
|
|
2860
|
+
/** True when this factory signs HS256 with a shared secret (gateway mode). */
|
|
2861
|
+
get isHmac() {
|
|
2862
|
+
return this.hmacSecret !== null;
|
|
2863
|
+
}
|
|
2864
|
+
/** The JWS `alg` this factory signs with. */
|
|
2865
|
+
get alg() {
|
|
2866
|
+
return this.isHmac ? "HS256" : "RS256";
|
|
2867
|
+
}
|
|
2868
|
+
/**
|
|
2869
|
+
* The signing key: the shared HMAC secret in gateway mode, otherwise the
|
|
2870
|
+
* generated RSA private key (lazily created via ensureKeys()).
|
|
2871
|
+
*/
|
|
2872
|
+
signingKey() {
|
|
2873
|
+
if (this.hmacSecret) return this.hmacSecret;
|
|
2874
|
+
if (!this.privateKey) {
|
|
2875
|
+
throw new Error("Private key not initialized");
|
|
2876
|
+
}
|
|
2877
|
+
return this.privateKey;
|
|
2856
2878
|
}
|
|
2857
2879
|
/**
|
|
2858
|
-
* Initialize the key pair (called automatically on first use)
|
|
2880
|
+
* Initialize the key pair (called automatically on first use). No-op in HS256
|
|
2881
|
+
* mode — there is no asymmetric keypair, just the shared secret.
|
|
2859
2882
|
*/
|
|
2860
2883
|
async ensureKeys() {
|
|
2884
|
+
if (this.isHmac) return;
|
|
2861
2885
|
if (this.privateKey && this.publicKey) return;
|
|
2862
2886
|
const { publicKey, privateKey } = await generateKeyPair("RS256", {
|
|
2863
2887
|
extractable: true
|
|
@@ -2885,10 +2909,8 @@ var TestTokenFactory = class {
|
|
|
2885
2909
|
scope: options.scopes?.join(" "),
|
|
2886
2910
|
...options.claims
|
|
2887
2911
|
};
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
}
|
|
2891
|
-
const token = await new SignJWT(payload).setProtectedHeader({ alg: "RS256", kid: this.keyId }).sign(this.privateKey);
|
|
2912
|
+
const header = this.isHmac ? { alg: this.alg, typ: "JWT" } : { alg: this.alg, kid: this.keyId };
|
|
2913
|
+
const token = await new SignJWT(payload).setProtectedHeader(header).sign(this.signingKey());
|
|
2892
2914
|
return token;
|
|
2893
2915
|
}
|
|
2894
2916
|
/**
|
|
@@ -2947,10 +2969,8 @@ var TestTokenFactory = class {
|
|
|
2947
2969
|
exp: now - 3600
|
|
2948
2970
|
// Expired 1 hour ago
|
|
2949
2971
|
};
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
}
|
|
2953
|
-
const token = await new SignJWT(payload).setProtectedHeader({ alg: "RS256", kid: this.keyId }).sign(this.privateKey);
|
|
2972
|
+
const header = this.isHmac ? { alg: this.alg, typ: "JWT" } : { alg: this.alg, kid: this.keyId };
|
|
2973
|
+
const token = await new SignJWT(payload).setProtectedHeader(header).sign(this.signingKey());
|
|
2954
2974
|
return token;
|
|
2955
2975
|
}
|
|
2956
2976
|
/**
|
|
@@ -2958,7 +2978,8 @@ var TestTokenFactory = class {
|
|
|
2958
2978
|
*/
|
|
2959
2979
|
createTokenWithInvalidSignature(options) {
|
|
2960
2980
|
const now = Math.floor(Date.now() / 1e3);
|
|
2961
|
-
const
|
|
2981
|
+
const headerObj = this.isHmac ? { alg: this.alg, typ: "JWT" } : { alg: this.alg, kid: this.keyId };
|
|
2982
|
+
const header = Buffer.from(JSON.stringify(headerObj)).toString("base64url");
|
|
2962
2983
|
const payload = Buffer.from(
|
|
2963
2984
|
JSON.stringify({
|
|
2964
2985
|
iss: this.issuer,
|
|
@@ -2972,9 +2993,15 @@ var TestTokenFactory = class {
|
|
|
2972
2993
|
return `${header}.${payload}.${signature}`;
|
|
2973
2994
|
}
|
|
2974
2995
|
/**
|
|
2975
|
-
* Get the public JWKS for verifying tokens
|
|
2996
|
+
* Get the public JWKS for verifying tokens.
|
|
2997
|
+
*
|
|
2998
|
+
* Only meaningful in RS256 mode — HS256 (gateway) tokens are verified against
|
|
2999
|
+
* the shared secret, not a published public key, so there is no JWKS to serve.
|
|
2976
3000
|
*/
|
|
2977
3001
|
async getPublicJwks() {
|
|
3002
|
+
if (this.isHmac) {
|
|
3003
|
+
throw new Error("getPublicJwks() is not available in HS256 mode (gateway tokens use a shared secret)");
|
|
3004
|
+
}
|
|
2978
3005
|
await this.ensureKeys();
|
|
2979
3006
|
if (!this.jwk) {
|
|
2980
3007
|
throw new Error("JWK not initialized");
|
|
@@ -3036,6 +3063,7 @@ var E2E_PORT_RANGES = {
|
|
|
3036
3063
|
"demo-e2e-orchestrated": { start: 50100, size: 10 },
|
|
3037
3064
|
"demo-e2e-transparent": { start: 50110, size: 10 },
|
|
3038
3065
|
"demo-e2e-cimd": { start: 50120, size: 10 },
|
|
3066
|
+
"demo-e2e-remote-proxy": { start: 50130, size: 10 },
|
|
3039
3067
|
// Feature E2E tests (50200-50299)
|
|
3040
3068
|
"demo-e2e-skills": { start: 50200, size: 10 },
|
|
3041
3069
|
"demo-e2e-remote": { start: 50210, size: 10 },
|
package/fixtures/index.js
CHANGED
|
@@ -31,6 +31,190 @@ module.exports = __toCommonJS(fixtures_exports);
|
|
|
31
31
|
// libs/testing/src/fixtures/test-fixture.ts
|
|
32
32
|
var import_globals = require("@jest/globals");
|
|
33
33
|
|
|
34
|
+
// libs/testing/src/auth/token-factory.ts
|
|
35
|
+
var import_jose = require("jose");
|
|
36
|
+
var TestTokenFactory = class {
|
|
37
|
+
issuer;
|
|
38
|
+
audience;
|
|
39
|
+
privateKey = null;
|
|
40
|
+
publicKey = null;
|
|
41
|
+
jwk = null;
|
|
42
|
+
keyId;
|
|
43
|
+
/** HS256 shared secret (gateway mode). Null → RS256 + JWKS (default). */
|
|
44
|
+
hmacSecret;
|
|
45
|
+
constructor(options = {}) {
|
|
46
|
+
this.issuer = options.issuer ?? "https://test.frontmcp.local";
|
|
47
|
+
this.audience = options.audience ?? "frontmcp-test";
|
|
48
|
+
this.keyId = `test-key-${Date.now()}`;
|
|
49
|
+
this.hmacSecret = options.hmacSecret ? new TextEncoder().encode(options.hmacSecret) : null;
|
|
50
|
+
}
|
|
51
|
+
/** True when this factory signs HS256 with a shared secret (gateway mode). */
|
|
52
|
+
get isHmac() {
|
|
53
|
+
return this.hmacSecret !== null;
|
|
54
|
+
}
|
|
55
|
+
/** The JWS `alg` this factory signs with. */
|
|
56
|
+
get alg() {
|
|
57
|
+
return this.isHmac ? "HS256" : "RS256";
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* The signing key: the shared HMAC secret in gateway mode, otherwise the
|
|
61
|
+
* generated RSA private key (lazily created via ensureKeys()).
|
|
62
|
+
*/
|
|
63
|
+
signingKey() {
|
|
64
|
+
if (this.hmacSecret) return this.hmacSecret;
|
|
65
|
+
if (!this.privateKey) {
|
|
66
|
+
throw new Error("Private key not initialized");
|
|
67
|
+
}
|
|
68
|
+
return this.privateKey;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Initialize the key pair (called automatically on first use). No-op in HS256
|
|
72
|
+
* mode — there is no asymmetric keypair, just the shared secret.
|
|
73
|
+
*/
|
|
74
|
+
async ensureKeys() {
|
|
75
|
+
if (this.isHmac) return;
|
|
76
|
+
if (this.privateKey && this.publicKey) return;
|
|
77
|
+
const { publicKey, privateKey } = await (0, import_jose.generateKeyPair)("RS256", {
|
|
78
|
+
extractable: true
|
|
79
|
+
});
|
|
80
|
+
this.privateKey = privateKey;
|
|
81
|
+
this.publicKey = publicKey;
|
|
82
|
+
this.jwk = await (0, import_jose.exportJWK)(publicKey);
|
|
83
|
+
this.jwk.kid = this.keyId;
|
|
84
|
+
this.jwk.use = "sig";
|
|
85
|
+
this.jwk.alg = "RS256";
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Create a JWT token with the specified claims
|
|
89
|
+
*/
|
|
90
|
+
async createTestToken(options) {
|
|
91
|
+
await this.ensureKeys();
|
|
92
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
93
|
+
const exp = options.exp ?? 3600;
|
|
94
|
+
const payload = {
|
|
95
|
+
iss: options.iss ?? this.issuer,
|
|
96
|
+
sub: options.sub,
|
|
97
|
+
aud: options.aud ?? this.audience,
|
|
98
|
+
iat: now,
|
|
99
|
+
exp: now + exp,
|
|
100
|
+
scope: options.scopes?.join(" "),
|
|
101
|
+
...options.claims
|
|
102
|
+
};
|
|
103
|
+
const header = this.isHmac ? { alg: this.alg, typ: "JWT" } : { alg: this.alg, kid: this.keyId };
|
|
104
|
+
const token = await new import_jose.SignJWT(payload).setProtectedHeader(header).sign(this.signingKey());
|
|
105
|
+
return token;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Create an admin token with full access
|
|
109
|
+
*/
|
|
110
|
+
async createAdminToken(sub = "admin-001") {
|
|
111
|
+
return this.createTestToken({
|
|
112
|
+
sub,
|
|
113
|
+
scopes: ["admin:*", "read", "write", "delete"],
|
|
114
|
+
claims: {
|
|
115
|
+
email: "admin@test.local",
|
|
116
|
+
name: "Test Admin",
|
|
117
|
+
role: "admin"
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Create a regular user token
|
|
123
|
+
*/
|
|
124
|
+
async createUserToken(sub = "user-001", scopes = ["read", "write"]) {
|
|
125
|
+
return this.createTestToken({
|
|
126
|
+
sub,
|
|
127
|
+
scopes,
|
|
128
|
+
claims: {
|
|
129
|
+
email: "user@test.local",
|
|
130
|
+
name: "Test User",
|
|
131
|
+
role: "user"
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Create an anonymous user token
|
|
137
|
+
*/
|
|
138
|
+
async createAnonymousToken() {
|
|
139
|
+
return this.createTestToken({
|
|
140
|
+
sub: `anon:${Date.now()}`,
|
|
141
|
+
scopes: ["anonymous"],
|
|
142
|
+
claims: {
|
|
143
|
+
name: "Anonymous",
|
|
144
|
+
role: "anonymous"
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Create an expired token (for testing token expiration)
|
|
150
|
+
*/
|
|
151
|
+
async createExpiredToken(options) {
|
|
152
|
+
await this.ensureKeys();
|
|
153
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
154
|
+
const payload = {
|
|
155
|
+
iss: this.issuer,
|
|
156
|
+
sub: options.sub,
|
|
157
|
+
aud: this.audience,
|
|
158
|
+
iat: now - 7200,
|
|
159
|
+
// 2 hours ago
|
|
160
|
+
exp: now - 3600
|
|
161
|
+
// Expired 1 hour ago
|
|
162
|
+
};
|
|
163
|
+
const header = this.isHmac ? { alg: this.alg, typ: "JWT" } : { alg: this.alg, kid: this.keyId };
|
|
164
|
+
const token = await new import_jose.SignJWT(payload).setProtectedHeader(header).sign(this.signingKey());
|
|
165
|
+
return token;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Create a token with an invalid signature (for testing signature validation)
|
|
169
|
+
*/
|
|
170
|
+
createTokenWithInvalidSignature(options) {
|
|
171
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
172
|
+
const headerObj = this.isHmac ? { alg: this.alg, typ: "JWT" } : { alg: this.alg, kid: this.keyId };
|
|
173
|
+
const header = Buffer.from(JSON.stringify(headerObj)).toString("base64url");
|
|
174
|
+
const payload = Buffer.from(
|
|
175
|
+
JSON.stringify({
|
|
176
|
+
iss: this.issuer,
|
|
177
|
+
sub: options.sub,
|
|
178
|
+
aud: this.audience,
|
|
179
|
+
iat: now,
|
|
180
|
+
exp: now + 3600
|
|
181
|
+
})
|
|
182
|
+
).toString("base64url");
|
|
183
|
+
const signature = Buffer.from("invalid-signature-" + Date.now()).toString("base64url");
|
|
184
|
+
return `${header}.${payload}.${signature}`;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Get the public JWKS for verifying tokens.
|
|
188
|
+
*
|
|
189
|
+
* Only meaningful in RS256 mode — HS256 (gateway) tokens are verified against
|
|
190
|
+
* the shared secret, not a published public key, so there is no JWKS to serve.
|
|
191
|
+
*/
|
|
192
|
+
async getPublicJwks() {
|
|
193
|
+
if (this.isHmac) {
|
|
194
|
+
throw new Error("getPublicJwks() is not available in HS256 mode (gateway tokens use a shared secret)");
|
|
195
|
+
}
|
|
196
|
+
await this.ensureKeys();
|
|
197
|
+
if (!this.jwk) {
|
|
198
|
+
throw new Error("JWK not initialized");
|
|
199
|
+
}
|
|
200
|
+
return {
|
|
201
|
+
keys: [this.jwk]
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Get the issuer URL
|
|
206
|
+
*/
|
|
207
|
+
getIssuer() {
|
|
208
|
+
return this.issuer;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Get the audience
|
|
212
|
+
*/
|
|
213
|
+
getAudience() {
|
|
214
|
+
return this.audience;
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
|
|
34
218
|
// libs/testing/src/platform/platform-client-info.ts
|
|
35
219
|
var MCP_APPS_EXTENSION_KEY = "io.modelcontextprotocol/ui";
|
|
36
220
|
function getPlatformClientInfo(platform) {
|
|
@@ -2101,163 +2285,6 @@ var ProgressCollector = class {
|
|
|
2101
2285
|
}
|
|
2102
2286
|
};
|
|
2103
2287
|
|
|
2104
|
-
// libs/testing/src/auth/token-factory.ts
|
|
2105
|
-
var import_jose = require("jose");
|
|
2106
|
-
var TestTokenFactory = class {
|
|
2107
|
-
issuer;
|
|
2108
|
-
audience;
|
|
2109
|
-
privateKey = null;
|
|
2110
|
-
publicKey = null;
|
|
2111
|
-
jwk = null;
|
|
2112
|
-
keyId;
|
|
2113
|
-
constructor(options = {}) {
|
|
2114
|
-
this.issuer = options.issuer ?? "https://test.frontmcp.local";
|
|
2115
|
-
this.audience = options.audience ?? "frontmcp-test";
|
|
2116
|
-
this.keyId = `test-key-${Date.now()}`;
|
|
2117
|
-
}
|
|
2118
|
-
/**
|
|
2119
|
-
* Initialize the key pair (called automatically on first use)
|
|
2120
|
-
*/
|
|
2121
|
-
async ensureKeys() {
|
|
2122
|
-
if (this.privateKey && this.publicKey) return;
|
|
2123
|
-
const { publicKey, privateKey } = await (0, import_jose.generateKeyPair)("RS256", {
|
|
2124
|
-
extractable: true
|
|
2125
|
-
});
|
|
2126
|
-
this.privateKey = privateKey;
|
|
2127
|
-
this.publicKey = publicKey;
|
|
2128
|
-
this.jwk = await (0, import_jose.exportJWK)(publicKey);
|
|
2129
|
-
this.jwk.kid = this.keyId;
|
|
2130
|
-
this.jwk.use = "sig";
|
|
2131
|
-
this.jwk.alg = "RS256";
|
|
2132
|
-
}
|
|
2133
|
-
/**
|
|
2134
|
-
* Create a JWT token with the specified claims
|
|
2135
|
-
*/
|
|
2136
|
-
async createTestToken(options) {
|
|
2137
|
-
await this.ensureKeys();
|
|
2138
|
-
const now = Math.floor(Date.now() / 1e3);
|
|
2139
|
-
const exp = options.exp ?? 3600;
|
|
2140
|
-
const payload = {
|
|
2141
|
-
iss: options.iss ?? this.issuer,
|
|
2142
|
-
sub: options.sub,
|
|
2143
|
-
aud: options.aud ?? this.audience,
|
|
2144
|
-
iat: now,
|
|
2145
|
-
exp: now + exp,
|
|
2146
|
-
scope: options.scopes?.join(" "),
|
|
2147
|
-
...options.claims
|
|
2148
|
-
};
|
|
2149
|
-
if (!this.privateKey) {
|
|
2150
|
-
throw new Error("Private key not initialized");
|
|
2151
|
-
}
|
|
2152
|
-
const token = await new import_jose.SignJWT(payload).setProtectedHeader({ alg: "RS256", kid: this.keyId }).sign(this.privateKey);
|
|
2153
|
-
return token;
|
|
2154
|
-
}
|
|
2155
|
-
/**
|
|
2156
|
-
* Create an admin token with full access
|
|
2157
|
-
*/
|
|
2158
|
-
async createAdminToken(sub = "admin-001") {
|
|
2159
|
-
return this.createTestToken({
|
|
2160
|
-
sub,
|
|
2161
|
-
scopes: ["admin:*", "read", "write", "delete"],
|
|
2162
|
-
claims: {
|
|
2163
|
-
email: "admin@test.local",
|
|
2164
|
-
name: "Test Admin",
|
|
2165
|
-
role: "admin"
|
|
2166
|
-
}
|
|
2167
|
-
});
|
|
2168
|
-
}
|
|
2169
|
-
/**
|
|
2170
|
-
* Create a regular user token
|
|
2171
|
-
*/
|
|
2172
|
-
async createUserToken(sub = "user-001", scopes = ["read", "write"]) {
|
|
2173
|
-
return this.createTestToken({
|
|
2174
|
-
sub,
|
|
2175
|
-
scopes,
|
|
2176
|
-
claims: {
|
|
2177
|
-
email: "user@test.local",
|
|
2178
|
-
name: "Test User",
|
|
2179
|
-
role: "user"
|
|
2180
|
-
}
|
|
2181
|
-
});
|
|
2182
|
-
}
|
|
2183
|
-
/**
|
|
2184
|
-
* Create an anonymous user token
|
|
2185
|
-
*/
|
|
2186
|
-
async createAnonymousToken() {
|
|
2187
|
-
return this.createTestToken({
|
|
2188
|
-
sub: `anon:${Date.now()}`,
|
|
2189
|
-
scopes: ["anonymous"],
|
|
2190
|
-
claims: {
|
|
2191
|
-
name: "Anonymous",
|
|
2192
|
-
role: "anonymous"
|
|
2193
|
-
}
|
|
2194
|
-
});
|
|
2195
|
-
}
|
|
2196
|
-
/**
|
|
2197
|
-
* Create an expired token (for testing token expiration)
|
|
2198
|
-
*/
|
|
2199
|
-
async createExpiredToken(options) {
|
|
2200
|
-
await this.ensureKeys();
|
|
2201
|
-
const now = Math.floor(Date.now() / 1e3);
|
|
2202
|
-
const payload = {
|
|
2203
|
-
iss: this.issuer,
|
|
2204
|
-
sub: options.sub,
|
|
2205
|
-
aud: this.audience,
|
|
2206
|
-
iat: now - 7200,
|
|
2207
|
-
// 2 hours ago
|
|
2208
|
-
exp: now - 3600
|
|
2209
|
-
// Expired 1 hour ago
|
|
2210
|
-
};
|
|
2211
|
-
if (!this.privateKey) {
|
|
2212
|
-
throw new Error("Private key not initialized");
|
|
2213
|
-
}
|
|
2214
|
-
const token = await new import_jose.SignJWT(payload).setProtectedHeader({ alg: "RS256", kid: this.keyId }).sign(this.privateKey);
|
|
2215
|
-
return token;
|
|
2216
|
-
}
|
|
2217
|
-
/**
|
|
2218
|
-
* Create a token with an invalid signature (for testing signature validation)
|
|
2219
|
-
*/
|
|
2220
|
-
createTokenWithInvalidSignature(options) {
|
|
2221
|
-
const now = Math.floor(Date.now() / 1e3);
|
|
2222
|
-
const header = Buffer.from(JSON.stringify({ alg: "RS256", kid: this.keyId })).toString("base64url");
|
|
2223
|
-
const payload = Buffer.from(
|
|
2224
|
-
JSON.stringify({
|
|
2225
|
-
iss: this.issuer,
|
|
2226
|
-
sub: options.sub,
|
|
2227
|
-
aud: this.audience,
|
|
2228
|
-
iat: now,
|
|
2229
|
-
exp: now + 3600
|
|
2230
|
-
})
|
|
2231
|
-
).toString("base64url");
|
|
2232
|
-
const signature = Buffer.from("invalid-signature-" + Date.now()).toString("base64url");
|
|
2233
|
-
return `${header}.${payload}.${signature}`;
|
|
2234
|
-
}
|
|
2235
|
-
/**
|
|
2236
|
-
* Get the public JWKS for verifying tokens
|
|
2237
|
-
*/
|
|
2238
|
-
async getPublicJwks() {
|
|
2239
|
-
await this.ensureKeys();
|
|
2240
|
-
if (!this.jwk) {
|
|
2241
|
-
throw new Error("JWK not initialized");
|
|
2242
|
-
}
|
|
2243
|
-
return {
|
|
2244
|
-
keys: [this.jwk]
|
|
2245
|
-
};
|
|
2246
|
-
}
|
|
2247
|
-
/**
|
|
2248
|
-
* Get the issuer URL
|
|
2249
|
-
*/
|
|
2250
|
-
getIssuer() {
|
|
2251
|
-
return this.issuer;
|
|
2252
|
-
}
|
|
2253
|
-
/**
|
|
2254
|
-
* Get the audience
|
|
2255
|
-
*/
|
|
2256
|
-
getAudience() {
|
|
2257
|
-
return this.audience;
|
|
2258
|
-
}
|
|
2259
|
-
};
|
|
2260
|
-
|
|
2261
2288
|
// libs/testing/src/server/test-server.ts
|
|
2262
2289
|
var import_child_process = require("child_process");
|
|
2263
2290
|
var import_utils = require("@frontmcp/utils");
|
|
@@ -2297,6 +2324,7 @@ var E2E_PORT_RANGES = {
|
|
|
2297
2324
|
"demo-e2e-orchestrated": { start: 50100, size: 10 },
|
|
2298
2325
|
"demo-e2e-transparent": { start: 50110, size: 10 },
|
|
2299
2326
|
"demo-e2e-cimd": { start: 50120, size: 10 },
|
|
2327
|
+
"demo-e2e-remote-proxy": { start: 50130, size: 10 },
|
|
2300
2328
|
// Feature E2E tests (50200-50299)
|
|
2301
2329
|
"demo-e2e-skills": { start: 50200, size: 10 },
|
|
2302
2330
|
"demo-e2e-remote": { start: 50210, size: 10 },
|
|
@@ -2971,7 +2999,8 @@ var tokenFactory = null;
|
|
|
2971
2999
|
var serverStartedByUs = false;
|
|
2972
3000
|
async function initializeSharedResources() {
|
|
2973
3001
|
if (!tokenFactory) {
|
|
2974
|
-
|
|
3002
|
+
const sharedSecret = currentConfig.env?.["JWT_SECRET"];
|
|
3003
|
+
tokenFactory = new TestTokenFactory(sharedSecret ? { hmacSecret: sharedSecret } : {});
|
|
2975
3004
|
}
|
|
2976
3005
|
if (!serverInstance) {
|
|
2977
3006
|
if (currentConfig.baseUrl) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-fixture.d.ts","sourceRoot":"","sources":["../../src/fixtures/test-fixture.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;
|
|
1
|
+
{"version":3,"file":"test-fixture.d.ts","sourceRoot":"","sources":["../../src/fixtures/test-fixture.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAeH,OAAO,KAAK,EAIV,YAAY,EAGZ,gBAAgB,EACjB,MAAM,iBAAiB,CAAC;AA+BzB;;GAEG;AACH,iBAAe,yBAAyB,IAAI,OAAO,CAAC,IAAI,CAAC,CA6DxD;AAED;;GAEG;AACH,iBAAe,kBAAkB,IAAI,OAAO,CAAC,YAAY,CAAC,CA6BzD;AAED;;;;GAIG;AACH,iBAAe,mBAAmB,CAAC,QAAQ,EAAE,YAAY,EAAE,UAAU,UAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAoB5F;AAED;;GAEG;AACH,iBAAe,sBAAsB,IAAI,OAAO,CAAC,IAAI,CAAC,CAQrD;AAmMD,QAAA,MAAM,IAAI,EAAuB,gBAAgB,CAAC;AAqBlD,OAAO,EAAE,IAAI,EAAE,CAAC;AAGhB,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,yBAAyB,EAAE,sBAAsB,EAAE,CAAC"}
|
package/index.d.ts
CHANGED
|
@@ -73,6 +73,6 @@ export type { TestPlatformType, PlatformMetaNamespace, TestClientInfo } from './
|
|
|
73
73
|
export { getPlatformMetaNamespace, getPlatformMimeType, isOpenAIPlatform, isExtAppsPlatform, isUiPlatform, getToolsListMetaPrefixes, getToolCallMetaPrefixes, getForbiddenMetaPrefixes, getPlatformClientInfo, buildUserAgent, getPlatformUserAgent, PLATFORM_DETECTION_PATTERNS, } from './platform';
|
|
74
74
|
export { BASIC_UI_TOOL_CONFIG, FULL_UI_TOOL_CONFIG, basicUIToolInputSchema, basicUIToolOutputSchema, fullUIToolInputSchema, fullUIToolOutputSchema, generateBasicUIToolOutput, generateFullUIToolOutput, EXPECTED_OPENAI_TOOLS_LIST_META_KEYS, EXPECTED_OPENAI_TOOL_CALL_META_KEYS, EXPECTED_EXTAPPS_TOOLS_LIST_META_KEYS, EXPECTED_EXTAPPS_TOOL_CALL_META_KEYS, EXPECTED_GENERIC_TOOLS_LIST_META_KEYS, EXPECTED_GENERIC_TOOL_CALL_META_KEYS, } from './example-tools';
|
|
75
75
|
export { perfTest, MetricsCollector, isGcAvailable, forceGc, forceFullGc, formatBytes, formatMicroseconds, formatDuration, LeakDetector, assertNoLeak, createLeakDetector, createPerfFixtures, PerfFixturesImpl, getGlobalMeasurements, clearGlobalMeasurements, BaselineStore, parseBaselineFromComment, formatBaselineAsComment, getBaselineStore, RegressionDetector, summarizeRegressions, getRegressionDetector, ReportGenerator, saveReports, createReportGenerator, } from './perf';
|
|
76
|
-
export { McpClient, McpStdioClientTransport } from './raw-client';
|
|
76
|
+
export { McpClient, McpStdioClientTransport, isTcpPortListening } from './raw-client';
|
|
77
77
|
export type { MemoryMetrics, CpuMetrics, PerfSnapshot, PerfMeasurement, PerfIssue, PerfThresholds, LeakDetectionOptions, LeakDetectionResult, MetricBaseline, TestBaseline, PerfBaseline, RegressionConfig, MetricRegression, RegressionResult, PerfTestSummary, ProjectSummary, PerfReport, PerfFixtures, PerfTestFixtures, PerfTestConfig, PerfTestFn, PerfTestWithFixtures, } from './perf';
|
|
78
78
|
//# sourceMappingURL=index.d.ts.map
|
package/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/index.js
CHANGED
|
@@ -111,6 +111,7 @@ __export(index_exports, {
|
|
|
111
111
|
isGcAvailable: () => isGcAvailable,
|
|
112
112
|
isOpenAIPlatform: () => isOpenAIPlatform,
|
|
113
113
|
isSuccessful: () => isSuccessful,
|
|
114
|
+
isTcpPortListening: () => isTcpPortListening,
|
|
114
115
|
isUiPlatform: () => isUiPlatform,
|
|
115
116
|
mcpMatchers: () => mcpMatchers,
|
|
116
117
|
mockResponse: () => mockResponse,
|
|
@@ -2285,15 +2286,39 @@ var TestTokenFactory = class {
|
|
|
2285
2286
|
publicKey = null;
|
|
2286
2287
|
jwk = null;
|
|
2287
2288
|
keyId;
|
|
2289
|
+
/** HS256 shared secret (gateway mode). Null → RS256 + JWKS (default). */
|
|
2290
|
+
hmacSecret;
|
|
2288
2291
|
constructor(options = {}) {
|
|
2289
2292
|
this.issuer = options.issuer ?? "https://test.frontmcp.local";
|
|
2290
2293
|
this.audience = options.audience ?? "frontmcp-test";
|
|
2291
2294
|
this.keyId = `test-key-${Date.now()}`;
|
|
2295
|
+
this.hmacSecret = options.hmacSecret ? new TextEncoder().encode(options.hmacSecret) : null;
|
|
2296
|
+
}
|
|
2297
|
+
/** True when this factory signs HS256 with a shared secret (gateway mode). */
|
|
2298
|
+
get isHmac() {
|
|
2299
|
+
return this.hmacSecret !== null;
|
|
2300
|
+
}
|
|
2301
|
+
/** The JWS `alg` this factory signs with. */
|
|
2302
|
+
get alg() {
|
|
2303
|
+
return this.isHmac ? "HS256" : "RS256";
|
|
2292
2304
|
}
|
|
2293
2305
|
/**
|
|
2294
|
-
*
|
|
2306
|
+
* The signing key: the shared HMAC secret in gateway mode, otherwise the
|
|
2307
|
+
* generated RSA private key (lazily created via ensureKeys()).
|
|
2308
|
+
*/
|
|
2309
|
+
signingKey() {
|
|
2310
|
+
if (this.hmacSecret) return this.hmacSecret;
|
|
2311
|
+
if (!this.privateKey) {
|
|
2312
|
+
throw new Error("Private key not initialized");
|
|
2313
|
+
}
|
|
2314
|
+
return this.privateKey;
|
|
2315
|
+
}
|
|
2316
|
+
/**
|
|
2317
|
+
* Initialize the key pair (called automatically on first use). No-op in HS256
|
|
2318
|
+
* mode — there is no asymmetric keypair, just the shared secret.
|
|
2295
2319
|
*/
|
|
2296
2320
|
async ensureKeys() {
|
|
2321
|
+
if (this.isHmac) return;
|
|
2297
2322
|
if (this.privateKey && this.publicKey) return;
|
|
2298
2323
|
const { publicKey, privateKey } = await (0, import_jose.generateKeyPair)("RS256", {
|
|
2299
2324
|
extractable: true
|
|
@@ -2321,10 +2346,8 @@ var TestTokenFactory = class {
|
|
|
2321
2346
|
scope: options.scopes?.join(" "),
|
|
2322
2347
|
...options.claims
|
|
2323
2348
|
};
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
}
|
|
2327
|
-
const token = await new import_jose.SignJWT(payload).setProtectedHeader({ alg: "RS256", kid: this.keyId }).sign(this.privateKey);
|
|
2349
|
+
const header = this.isHmac ? { alg: this.alg, typ: "JWT" } : { alg: this.alg, kid: this.keyId };
|
|
2350
|
+
const token = await new import_jose.SignJWT(payload).setProtectedHeader(header).sign(this.signingKey());
|
|
2328
2351
|
return token;
|
|
2329
2352
|
}
|
|
2330
2353
|
/**
|
|
@@ -2383,10 +2406,8 @@ var TestTokenFactory = class {
|
|
|
2383
2406
|
exp: now - 3600
|
|
2384
2407
|
// Expired 1 hour ago
|
|
2385
2408
|
};
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
}
|
|
2389
|
-
const token = await new import_jose.SignJWT(payload).setProtectedHeader({ alg: "RS256", kid: this.keyId }).sign(this.privateKey);
|
|
2409
|
+
const header = this.isHmac ? { alg: this.alg, typ: "JWT" } : { alg: this.alg, kid: this.keyId };
|
|
2410
|
+
const token = await new import_jose.SignJWT(payload).setProtectedHeader(header).sign(this.signingKey());
|
|
2390
2411
|
return token;
|
|
2391
2412
|
}
|
|
2392
2413
|
/**
|
|
@@ -2394,7 +2415,8 @@ var TestTokenFactory = class {
|
|
|
2394
2415
|
*/
|
|
2395
2416
|
createTokenWithInvalidSignature(options) {
|
|
2396
2417
|
const now = Math.floor(Date.now() / 1e3);
|
|
2397
|
-
const
|
|
2418
|
+
const headerObj = this.isHmac ? { alg: this.alg, typ: "JWT" } : { alg: this.alg, kid: this.keyId };
|
|
2419
|
+
const header = Buffer.from(JSON.stringify(headerObj)).toString("base64url");
|
|
2398
2420
|
const payload = Buffer.from(
|
|
2399
2421
|
JSON.stringify({
|
|
2400
2422
|
iss: this.issuer,
|
|
@@ -2408,9 +2430,15 @@ var TestTokenFactory = class {
|
|
|
2408
2430
|
return `${header}.${payload}.${signature}`;
|
|
2409
2431
|
}
|
|
2410
2432
|
/**
|
|
2411
|
-
* Get the public JWKS for verifying tokens
|
|
2433
|
+
* Get the public JWKS for verifying tokens.
|
|
2434
|
+
*
|
|
2435
|
+
* Only meaningful in RS256 mode — HS256 (gateway) tokens are verified against
|
|
2436
|
+
* the shared secret, not a published public key, so there is no JWKS to serve.
|
|
2412
2437
|
*/
|
|
2413
2438
|
async getPublicJwks() {
|
|
2439
|
+
if (this.isHmac) {
|
|
2440
|
+
throw new Error("getPublicJwks() is not available in HS256 mode (gateway tokens use a shared secret)");
|
|
2441
|
+
}
|
|
2414
2442
|
await this.ensureKeys();
|
|
2415
2443
|
if (!this.jwk) {
|
|
2416
2444
|
throw new Error("JWK not initialized");
|
|
@@ -4006,6 +4034,7 @@ var E2E_PORT_RANGES = {
|
|
|
4006
4034
|
"demo-e2e-orchestrated": { start: 50100, size: 10 },
|
|
4007
4035
|
"demo-e2e-transparent": { start: 50110, size: 10 },
|
|
4008
4036
|
"demo-e2e-cimd": { start: 50120, size: 10 },
|
|
4037
|
+
"demo-e2e-remote-proxy": { start: 50130, size: 10 },
|
|
4009
4038
|
// Feature E2E tests (50200-50299)
|
|
4010
4039
|
"demo-e2e-skills": { start: 50200, size: 10 },
|
|
4011
4040
|
"demo-e2e-remote": { start: 50210, size: 10 },
|
|
@@ -4846,7 +4875,8 @@ var tokenFactory = null;
|
|
|
4846
4875
|
var serverStartedByUs = false;
|
|
4847
4876
|
async function initializeSharedResources() {
|
|
4848
4877
|
if (!tokenFactory) {
|
|
4849
|
-
|
|
4878
|
+
const sharedSecret = currentConfig.env?.["JWT_SECRET"];
|
|
4879
|
+
tokenFactory = new TestTokenFactory(sharedSecret ? { hmacSecret: sharedSecret } : {});
|
|
4850
4880
|
}
|
|
4851
4881
|
if (!serverInstance) {
|
|
4852
4882
|
if (currentConfig.baseUrl) {
|
|
@@ -8092,6 +8122,26 @@ function createReportGenerator() {
|
|
|
8092
8122
|
// libs/testing/src/raw-client/index.ts
|
|
8093
8123
|
var import_protocol = require("@frontmcp/protocol");
|
|
8094
8124
|
var import_protocol2 = require("@frontmcp/protocol");
|
|
8125
|
+
|
|
8126
|
+
// libs/testing/src/raw-client/port-probe.ts
|
|
8127
|
+
var import_node_net = __toESM(require("node:net"));
|
|
8128
|
+
function isTcpPortListening(port, host = "127.0.0.1", timeoutMs = 1500) {
|
|
8129
|
+
return new Promise((resolve) => {
|
|
8130
|
+
const socket = import_node_net.default.connect({ port, host });
|
|
8131
|
+
socket.unref();
|
|
8132
|
+
socket.setTimeout(timeoutMs);
|
|
8133
|
+
let settled = false;
|
|
8134
|
+
const finish = (listening) => {
|
|
8135
|
+
if (settled) return;
|
|
8136
|
+
settled = true;
|
|
8137
|
+
socket.destroy();
|
|
8138
|
+
resolve(listening);
|
|
8139
|
+
};
|
|
8140
|
+
socket.once("connect", () => finish(true));
|
|
8141
|
+
socket.once("error", () => finish(false));
|
|
8142
|
+
socket.once("timeout", () => finish(false));
|
|
8143
|
+
});
|
|
8144
|
+
}
|
|
8095
8145
|
// Annotate the CommonJS export names for ESM import in node:
|
|
8096
8146
|
0 && (module.exports = {
|
|
8097
8147
|
AssertionError,
|
|
@@ -8175,6 +8225,7 @@ var import_protocol2 = require("@frontmcp/protocol");
|
|
|
8175
8225
|
isGcAvailable,
|
|
8176
8226
|
isOpenAIPlatform,
|
|
8177
8227
|
isSuccessful,
|
|
8228
|
+
isTcpPortListening,
|
|
8178
8229
|
isUiPlatform,
|
|
8179
8230
|
mcpMatchers,
|
|
8180
8231
|
mockResponse,
|