File tree Expand file tree Collapse file tree
apps/dashboard/server/mcp Expand file tree Collapse file tree Original file line number Diff line number Diff line change 1+ import { mcpError } from "./errors"
2+
3+ export interface McpRequestContext {
4+ userId : string
5+ clientId : string
6+ scopes : string [ ]
7+ }
8+
9+ interface VerifiedJwt {
10+ sub ?: unknown
11+ client_id ?: unknown
12+ azp ?: unknown
13+ scope ?: unknown
14+ scopes ?: unknown
15+ }
16+
17+ /**
18+ * Build the per-request context from a JWT that mcpHandler has already
19+ * cryptographically verified. We still validate the *shape* of the claims —
20+ * a malformed JWT should never occur (we sign these ourselves) but if it
21+ * does we want a clean INVALID_INPUT rather than an undefined-property
22+ * crash deep in a tool handler.
23+ */
24+ export function buildContextFromJwt ( payload : VerifiedJwt ) : McpRequestContext {
25+ if ( typeof payload . sub !== "string" || payload . sub . length === 0 ) {
26+ throw mcpError ( "INVALID_INPUT" , "JWT missing 'sub' claim" )
27+ }
28+ const clientId =
29+ typeof payload . client_id === "string"
30+ ? payload . client_id
31+ : typeof payload . azp === "string"
32+ ? payload . azp
33+ : null
34+ if ( ! clientId ) {
35+ throw mcpError ( "INVALID_INPUT" , "JWT missing 'client_id' / 'azp' claim" )
36+ }
37+ const scopes =
38+ typeof payload . scope === "string"
39+ ? payload . scope . split ( / \s + / ) . filter ( Boolean )
40+ : Array . isArray ( payload . scopes )
41+ ? payload . scopes . filter ( ( s ) : s is string => typeof s === "string" )
42+ : [ ]
43+ return { userId : payload . sub , clientId, scopes }
44+ }
You can’t perform that action at this time.
0 commit comments