@vantageos/mcp-boilerplate 0.6.3 → 0.8.0
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/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +39 -0
- package/README.fr.md +146 -10
- package/README.md +141 -12
- package/dist/build-info.json +1 -0
- package/dist/buildIdentity.d.ts +16 -0
- package/dist/buildIdentity.js +39 -0
- package/dist/embedded.generated.d.ts +225 -0
- package/dist/embedded.generated.js +8 -0
- package/dist/hostVerification.d.ts +15 -0
- package/dist/hostVerification.js +32 -0
- package/dist/http.js +9 -4
- package/dist/httpApp.d.ts +15 -0
- package/dist/httpApp.js +119 -9
- package/dist/i18n.js +6 -16
- package/dist/landingPage.d.ts +24 -0
- package/dist/landingPage.js +109 -0
- package/dist/listCursor.d.ts +10 -0
- package/dist/listCursor.js +37 -0
- package/dist/mcpServerFactory.d.ts +22 -2
- package/dist/mcpServerFactory.js +125 -22
- package/dist/oauth/authorizationServer.js +33 -12
- package/dist/oauth/picker.js +1 -0
- package/dist/oauth/settings.d.ts +2 -0
- package/dist/oauth/settings.js +2 -0
- package/dist/oauth/tokenIssuer.d.ts +0 -1
- package/dist/oauth/tokenIssuer.js +1 -18
- package/dist/pagination.d.ts +2 -1
- package/dist/pagination.js +2 -1
- package/dist/rateLimit.d.ts +33 -0
- package/dist/rateLimit.js +48 -0
- package/dist/servedToolRecords.d.ts +6 -0
- package/dist/servedToolRecords.js +11 -1
- package/dist/serverCard.d.ts +68 -0
- package/dist/serverCard.js +68 -0
- package/dist/serverIcon.d.ts +17 -0
- package/dist/serverIcon.js +26 -0
- package/dist/serverIdentity.d.ts +83 -0
- package/dist/serverIdentity.js +130 -0
- package/dist/serverManifest.d.ts +9 -1
- package/dist/serverManifest.js +10 -1
- package/dist/signedCursor.js +6 -1
- package/dist/stdio.js +2 -2
- package/dist/tenancy.d.ts +4 -14
- package/dist/tenancy.js +7 -18
- package/dist/toolRecord.d.ts +35 -2
- package/dist/toolRecord.js +44 -2
- package/dist/toolRoleGate.d.ts +41 -0
- package/dist/toolRoleGate.js +94 -0
- package/dist/toolRoles.d.ts +57 -0
- package/dist/toolRoles.js +113 -0
- package/dist/ui/sandboxDomain.d.ts +13 -0
- package/dist/ui/sandboxDomain.js +23 -0
- package/dist/ui/uiResource.d.ts +14 -1
- package/dist/ui/uiResource.js +11 -1
- package/dist/upstream/paginatedClient.js +1 -0
- package/dist/verifiedPerson.d.ts +6 -0
- package/dist/verifiedPerson.js +9 -0
- package/dist/version.js +6 -5
- package/docs/manufact-gap.md +200 -0
- package/docs/releasing.md +19 -0
- package/mcp-server.json +3 -2
- package/package.json +11 -3
- package/plugin.json +2 -4
- package/scripts/plugin-manifest.source.json +19 -0
- package/scripts/readme-fr.mjs +11 -4
- package/dist/locales/en.json +0 -77
- package/dist/locales/fr.json +0 -77
- package/dist/oauth/clientAssertion.d.ts +0 -28
- package/dist/oauth/clientAssertion.js +0 -106
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { type CimdOptions } from "./clientRegistry.js";
|
|
2
|
-
export declare const JWT_BEARER_ASSERTION_TYPE = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer";
|
|
3
|
-
export declare const ASSERTION_SIGNING_ALGS: readonly ["RS256"];
|
|
4
|
-
export type AssertionResult = {
|
|
5
|
-
ok: true;
|
|
6
|
-
clientId: string;
|
|
7
|
-
} | {
|
|
8
|
-
ok: false;
|
|
9
|
-
reason: string;
|
|
10
|
-
};
|
|
11
|
-
/** Bounded set of accepted jti values, each held until its assertion expires. */
|
|
12
|
-
export declare function createJtiStore(maxEntries?: number): {
|
|
13
|
-
/** True when the jti is new (and is now recorded); false on a replay or when full. */
|
|
14
|
-
accept(clientId: string, jti: string, exp: number, nowSeconds: number): boolean;
|
|
15
|
-
};
|
|
16
|
-
export interface AssertionVerifierOptions extends CimdOptions {
|
|
17
|
-
now?: () => number;
|
|
18
|
-
jtiStore?: ReturnType<typeof createJtiStore>;
|
|
19
|
-
}
|
|
20
|
-
export declare function createClientAssertionVerifier(options?: AssertionVerifierOptions): {
|
|
21
|
-
verify(input: {
|
|
22
|
-
assertionType: string | undefined;
|
|
23
|
-
assertion: string | undefined;
|
|
24
|
-
/** The `client_id` request parameter, when sent. */
|
|
25
|
-
clientId: string | undefined;
|
|
26
|
-
audiences: string[];
|
|
27
|
-
}): Promise<AssertionResult>;
|
|
28
|
-
};
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* private_key_jwt client authentication at `/token` (RFC 7523 / OIDC Core 9).
|
|
3
|
-
*
|
|
4
|
-
* A presented assertion is VERIFIED, never ignored and never trusted unverified:
|
|
5
|
-
* - the key set comes from the `jwks_uri` of the client's CIMD document, fetched through the
|
|
6
|
-
* same SSRF guard as the document itself;
|
|
7
|
-
* - alg is pinned to RS256 (a document declaring another signing alg is refused), so `none`
|
|
8
|
-
* and HMAC assertions cannot verify;
|
|
9
|
-
* - `iss` and `sub` equal the client_id; `aud` is this token endpoint or this issuer; `exp`
|
|
10
|
-
* is required (30 s skew); `jti` is required and accepted ONCE.
|
|
11
|
-
*/
|
|
12
|
-
import { createLocalJWKSet, decodeJwt, jwtVerify } from "jose";
|
|
13
|
-
import { fetchGuardedJson, isUrlShapedClientId, resolveCimdDocument, } from "./clientRegistry.js";
|
|
14
|
-
export const JWT_BEARER_ASSERTION_TYPE = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer";
|
|
15
|
-
export const ASSERTION_SIGNING_ALGS = ["RS256"];
|
|
16
|
-
const SKEW_SECONDS = 30;
|
|
17
|
-
const MAX_JWKS_BYTES = 64 * 1024;
|
|
18
|
-
/** Bounded set of accepted jti values, each held until its assertion expires. */
|
|
19
|
-
export function createJtiStore(maxEntries = 10_000) {
|
|
20
|
-
const seen = new Map(); // `${clientId}\n${jti}` -> exp (epoch seconds)
|
|
21
|
-
return {
|
|
22
|
-
/** True when the jti is new (and is now recorded); false on a replay or when full. */
|
|
23
|
-
accept(clientId, jti, exp, nowSeconds) {
|
|
24
|
-
for (const [k, e] of seen)
|
|
25
|
-
if (e + SKEW_SECONDS <= nowSeconds)
|
|
26
|
-
seen.delete(k);
|
|
27
|
-
const key = `${clientId}\n${jti}`;
|
|
28
|
-
if (seen.has(key))
|
|
29
|
-
return false;
|
|
30
|
-
if (seen.size >= maxEntries)
|
|
31
|
-
return false; // fail closed: never evict a live jti
|
|
32
|
-
seen.set(key, exp);
|
|
33
|
-
return true;
|
|
34
|
-
},
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
export function createClientAssertionVerifier(options = {}) {
|
|
38
|
-
const now = options.now ?? (() => Date.now());
|
|
39
|
-
const jtiStore = options.jtiStore ?? createJtiStore();
|
|
40
|
-
const fetchOpts = {
|
|
41
|
-
fetchImpl: options.fetchImpl,
|
|
42
|
-
resolveHost: options.resolveHost,
|
|
43
|
-
maxBytes: MAX_JWKS_BYTES,
|
|
44
|
-
...(options.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {}),
|
|
45
|
-
};
|
|
46
|
-
return {
|
|
47
|
-
async verify(input) {
|
|
48
|
-
const fail = (reason) => ({ ok: false, reason });
|
|
49
|
-
if (input.assertionType !== JWT_BEARER_ASSERTION_TYPE || !input.assertion) {
|
|
50
|
-
return fail("unsupported client assertion");
|
|
51
|
-
}
|
|
52
|
-
// The unverified iss only LOCATES the key set; it is re-checked against the verified claims.
|
|
53
|
-
let claimedIss;
|
|
54
|
-
try {
|
|
55
|
-
claimedIss = decodeJwt(input.assertion).iss;
|
|
56
|
-
}
|
|
57
|
-
catch {
|
|
58
|
-
return fail("malformed client assertion");
|
|
59
|
-
}
|
|
60
|
-
if (!claimedIss || !isUrlShapedClientId(claimedIss))
|
|
61
|
-
return fail("unknown client");
|
|
62
|
-
if (input.clientId !== undefined && input.clientId !== claimedIss) {
|
|
63
|
-
return fail("client_id does not match the assertion issuer");
|
|
64
|
-
}
|
|
65
|
-
const clientId = claimedIss;
|
|
66
|
-
const cimd = await resolveCimdDocument(clientId, options);
|
|
67
|
-
if (!cimd?.jwksUri)
|
|
68
|
-
return fail("client has no verifiable key set");
|
|
69
|
-
if (cimd.signingAlg !== undefined &&
|
|
70
|
-
!ASSERTION_SIGNING_ALGS.includes(cimd.signingAlg)) {
|
|
71
|
-
return fail("unsupported signing algorithm");
|
|
72
|
-
}
|
|
73
|
-
let jwksUrl;
|
|
74
|
-
try {
|
|
75
|
-
jwksUrl = new URL(cimd.jwksUri);
|
|
76
|
-
}
|
|
77
|
-
catch {
|
|
78
|
-
return fail("invalid jwks_uri");
|
|
79
|
-
}
|
|
80
|
-
const jwks = (await fetchGuardedJson(jwksUrl, fetchOpts));
|
|
81
|
-
if (!jwks || !Array.isArray(jwks.keys))
|
|
82
|
-
return fail("key set unavailable");
|
|
83
|
-
let payload;
|
|
84
|
-
try {
|
|
85
|
-
const verified = await jwtVerify(input.assertion, createLocalJWKSet(jwks), {
|
|
86
|
-
algorithms: [...ASSERTION_SIGNING_ALGS],
|
|
87
|
-
issuer: clientId,
|
|
88
|
-
subject: clientId,
|
|
89
|
-
audience: input.audiences,
|
|
90
|
-
clockTolerance: SKEW_SECONDS,
|
|
91
|
-
currentDate: new Date(now()),
|
|
92
|
-
requiredClaims: ["iss", "sub", "aud", "exp", "jti"],
|
|
93
|
-
});
|
|
94
|
-
payload = verified.payload;
|
|
95
|
-
}
|
|
96
|
-
catch {
|
|
97
|
-
return fail("client assertion rejected");
|
|
98
|
-
}
|
|
99
|
-
const exp = payload.exp;
|
|
100
|
-
if (!payload.jti || !jtiStore.accept(clientId, payload.jti, exp, Math.floor(now() / 1000))) {
|
|
101
|
-
return fail("client assertion replayed");
|
|
102
|
-
}
|
|
103
|
-
return { ok: true, clientId };
|
|
104
|
-
},
|
|
105
|
-
};
|
|
106
|
-
}
|