6
6
OAuthTokens ,
7
7
OAuthTokensSchema ,
8
8
} from '@modelcontextprotocol/sdk/shared/auth.js'
9
- import type { OAuthProviderOptions } from './types'
9
+ import type { OAuthProviderOptions , StaticOAuthClientMetadata } from './types'
10
10
import { readJsonFile , writeJsonFile , readTextFile , writeTextFile } from './mcp-auth-config'
11
+ import { StaticOAuthClientInformationFull } from './types'
11
12
import { getServerUrlHash , log , debugLog , DEBUG , MCP_REMOTE_VERSION } from './utils'
12
13
13
14
/**
@@ -21,6 +22,8 @@ export class NodeOAuthClientProvider implements OAuthClientProvider {
21
22
private clientUri : string
22
23
private softwareId : string
23
24
private softwareVersion : string
25
+ private staticOAuthClientMetadata : StaticOAuthClientMetadata
26
+ private staticOAuthClientInfo : StaticOAuthClientInformationFull
24
27
25
28
/**
26
29
* Creates a new NodeOAuthClientProvider
@@ -33,6 +36,8 @@ export class NodeOAuthClientProvider implements OAuthClientProvider {
33
36
this . clientUri = options . clientUri || 'https://github.com/modelcontextprotocol/mcp-cli'
34
37
this . softwareId = options . softwareId || '2e6dc280-f3c3-4e01-99a7-8181dbd1d23d'
35
38
this . softwareVersion = options . softwareVersion || MCP_REMOTE_VERSION
39
+ this . staticOAuthClientMetadata = options . staticOAuthClientMetadata
40
+ this . staticOAuthClientInfo = options . staticOAuthClientInfo
36
41
}
37
42
38
43
get redirectUrl ( ) : string {
@@ -49,6 +54,7 @@ export class NodeOAuthClientProvider implements OAuthClientProvider {
49
54
client_uri : this . clientUri ,
50
55
software_id : this . softwareId ,
51
56
software_version : this . softwareVersion ,
57
+ ...this . staticOAuthClientMetadata ,
52
58
}
53
59
}
54
60
@@ -58,6 +64,10 @@ export class NodeOAuthClientProvider implements OAuthClientProvider {
58
64
*/
59
65
async clientInformation ( ) : Promise < OAuthClientInformationFull | undefined > {
60
66
if ( DEBUG ) await debugLog ( this . serverUrlHash , 'Reading client info' )
67
+ if ( this . staticOAuthClientInfo ) {
68
+ if ( DEBUG ) await debugLog ( this . serverUrlHash , 'Returning static client info' )
69
+ return this . staticOAuthClientInfo
70
+ }
61
71
const clientInfo = await readJsonFile < OAuthClientInformationFull > ( this . serverUrlHash , 'client_info.json' , OAuthClientInformationFullSchema )
62
72
if ( DEBUG ) await debugLog ( this . serverUrlHash , 'Client info result:' , clientInfo ? 'Found' : 'Not found' )
63
73
return clientInfo
@@ -81,16 +91,16 @@ export class NodeOAuthClientProvider implements OAuthClientProvider {
81
91
await debugLog ( this . serverUrlHash , 'Reading OAuth tokens' )
82
92
await debugLog ( this . serverUrlHash , 'Token request stack trace:' , new Error ( ) . stack )
83
93
}
84
-
94
+
85
95
const tokens = await readJsonFile < OAuthTokens > ( this . serverUrlHash , 'tokens.json' , OAuthTokensSchema )
86
-
96
+
87
97
if ( DEBUG ) {
88
98
if ( tokens ) {
89
99
const expiresAt = new Date ( tokens . expires_at )
90
100
const now = new Date ( )
91
101
const expiresAtTime = expiresAt . getTime ( )
92
102
const timeLeft = ! isNaN ( expiresAtTime ) ? Math . round ( ( expiresAtTime - now . getTime ( ) ) / 1000 ) : 0
93
-
103
+
94
104
// Alert if expires_at produces an invalid date
95
105
if ( isNaN ( expiresAtTime ) ) {
96
106
await debugLog ( this . serverUrlHash , '⚠️ WARNING: Invalid expires_at detected while reading tokens ⚠️' , {
@@ -99,8 +109,8 @@ export class NodeOAuthClientProvider implements OAuthClientProvider {
99
109
stack : new Error ( 'Invalid expires_at timestamp' ) . stack
100
110
} )
101
111
}
102
-
103
- await debugLog ( this . serverUrlHash , 'Token result:' , {
112
+
113
+ await debugLog ( this . serverUrlHash , 'Token result:' , {
104
114
found : true ,
105
115
hasAccessToken : ! ! tokens . access_token ,
106
116
hasRefreshToken : ! ! tokens . refresh_token ,
@@ -112,7 +122,7 @@ export class NodeOAuthClientProvider implements OAuthClientProvider {
112
122
await debugLog ( this . serverUrlHash , 'Token result: Not found' )
113
123
}
114
124
}
115
-
125
+
116
126
return tokens
117
127
}
118
128
@@ -126,7 +136,7 @@ export class NodeOAuthClientProvider implements OAuthClientProvider {
126
136
const now = new Date ( )
127
137
const expiresAtTime = expiresAt . getTime ( )
128
138
const timeLeft = ! isNaN ( expiresAtTime ) ? Math . round ( ( expiresAtTime - now . getTime ( ) ) / 1000 ) : 0
129
-
139
+
130
140
// Alert if expires_at produces an invalid date
131
141
if ( isNaN ( expiresAtTime ) ) {
132
142
await debugLog ( this . serverUrlHash , '⚠️ WARNING: Invalid expires_at detected in tokens ⚠️' , {
@@ -135,15 +145,15 @@ export class NodeOAuthClientProvider implements OAuthClientProvider {
135
145
stack : new Error ( 'Invalid expires_at timestamp' ) . stack
136
146
} )
137
147
}
138
-
139
- await debugLog ( this . serverUrlHash , 'Saving tokens' , {
148
+
149
+ await debugLog ( this . serverUrlHash , 'Saving tokens' , {
140
150
hasAccessToken : ! ! tokens . access_token ,
141
151
hasRefreshToken : ! ! tokens . refresh_token ,
142
152
expiresIn : `${ timeLeft } seconds` ,
143
153
expiresAt : tokens . expires_at
144
154
} )
145
155
}
146
-
156
+
147
157
await writeJsonFile ( this . serverUrlHash , 'tokens.json' , tokens )
148
158
}
149
159
@@ -153,9 +163,9 @@ export class NodeOAuthClientProvider implements OAuthClientProvider {
153
163
*/
154
164
async redirectToAuthorization ( authorizationUrl : URL ) : Promise < void > {
155
165
log ( `\nPlease authorize this client by visiting:\n${ authorizationUrl . toString ( ) } \n` )
156
-
166
+
157
167
if ( DEBUG ) await debugLog ( this . serverUrlHash , 'Redirecting to authorization URL' , authorizationUrl . toString ( ) )
158
-
168
+
159
169
try {
160
170
await open ( authorizationUrl . toString ( ) )
161
171
log ( 'Browser opened automatically.' )
0 commit comments