mcp-use 0.1.20 → 0.2.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.
Files changed (56) hide show
  1. package/dist/src/browser.d.ts +49 -0
  2. package/dist/src/browser.d.ts.map +1 -0
  3. package/dist/src/browser.js +75 -0
  4. package/dist/src/client/base.d.ts +32 -0
  5. package/dist/src/client/base.d.ts.map +1 -0
  6. package/dist/src/client/base.js +119 -0
  7. package/dist/src/client.d.ts +19 -16
  8. package/dist/src/client.d.ts.map +1 -1
  9. package/dist/src/client.js +24 -107
  10. package/dist/src/logging.d.ts +1 -1
  11. package/dist/src/logging.d.ts.map +1 -1
  12. package/dist/src/logging.js +31 -16
  13. package/dist/src/managers/server_manager.js +1 -1
  14. package/dist/src/oauth-helper.d.ts +135 -0
  15. package/dist/src/oauth-helper.d.ts.map +1 -0
  16. package/dist/src/oauth-helper.js +427 -0
  17. package/package.json +6 -1
  18. package/dist/examples/add_server_tool.d.ts +0 -8
  19. package/dist/examples/add_server_tool.d.ts.map +0 -1
  20. package/dist/examples/add_server_tool.js +0 -79
  21. package/dist/examples/ai_sdk_example.d.ts +0 -23
  22. package/dist/examples/ai_sdk_example.d.ts.map +0 -1
  23. package/dist/examples/ai_sdk_example.js +0 -213
  24. package/dist/examples/airbnb_use.d.ts +0 -10
  25. package/dist/examples/airbnb_use.d.ts.map +0 -1
  26. package/dist/examples/airbnb_use.js +0 -43
  27. package/dist/examples/blender_use.d.ts +0 -15
  28. package/dist/examples/blender_use.d.ts.map +0 -1
  29. package/dist/examples/blender_use.js +0 -39
  30. package/dist/examples/browser_use.d.ts +0 -10
  31. package/dist/examples/browser_use.d.ts.map +0 -1
  32. package/dist/examples/browser_use.js +0 -46
  33. package/dist/examples/chat_example.d.ts +0 -10
  34. package/dist/examples/chat_example.d.ts.map +0 -1
  35. package/dist/examples/chat_example.js +0 -86
  36. package/dist/examples/filesystem_use.d.ts +0 -11
  37. package/dist/examples/filesystem_use.d.ts.map +0 -1
  38. package/dist/examples/filesystem_use.js +0 -43
  39. package/dist/examples/http_example.d.ts +0 -18
  40. package/dist/examples/http_example.d.ts.map +0 -1
  41. package/dist/examples/http_example.js +0 -37
  42. package/dist/examples/mcp_everything.d.ts +0 -6
  43. package/dist/examples/mcp_everything.d.ts.map +0 -1
  44. package/dist/examples/mcp_everything.js +0 -25
  45. package/dist/examples/multi_server_example.d.ts +0 -10
  46. package/dist/examples/multi_server_example.d.ts.map +0 -1
  47. package/dist/examples/multi_server_example.js +0 -51
  48. package/dist/examples/observability.d.ts +0 -6
  49. package/dist/examples/observability.d.ts.map +0 -1
  50. package/dist/examples/observability.js +0 -50
  51. package/dist/examples/stream_example.d.ts +0 -12
  52. package/dist/examples/stream_example.d.ts.map +0 -1
  53. package/dist/examples/stream_example.js +0 -198
  54. package/dist/examples/structured_output.d.ts +0 -9
  55. package/dist/examples/structured_output.d.ts.map +0 -1
  56. package/dist/examples/structured_output.js +0 -95
@@ -0,0 +1,135 @@
1
+ /**
2
+ * OAuth helper for browser-based MCP authentication
3
+ *
4
+ * This helper provides OAuth 2.0 authorization code flow support for MCP servers
5
+ * that require authentication, such as Linear's MCP server.
6
+ */
7
+ export interface OAuthConfig {
8
+ clientId?: string;
9
+ redirectUri: string;
10
+ scope?: string;
11
+ state?: string;
12
+ clientName?: string;
13
+ }
14
+ export interface OAuthDiscovery {
15
+ issuer: string;
16
+ authorization_endpoint: string;
17
+ token_endpoint: string;
18
+ registration_endpoint?: string;
19
+ response_types_supported: string[];
20
+ grant_types_supported: string[];
21
+ code_challenge_methods_supported: string[];
22
+ token_endpoint_auth_methods_supported?: string[];
23
+ }
24
+ export interface ClientRegistration {
25
+ client_id: string;
26
+ client_secret?: string;
27
+ registration_access_token?: string;
28
+ registration_client_uri?: string;
29
+ client_id_issued_at?: number;
30
+ client_secret_expires_at?: number;
31
+ }
32
+ export interface OAuthResult {
33
+ access_token: string;
34
+ token_type: string;
35
+ expires_at?: number | null;
36
+ refresh_token?: string | null;
37
+ scope?: string | null;
38
+ }
39
+ export interface OAuthState {
40
+ isRequired: boolean;
41
+ isAuthenticated: boolean;
42
+ isAuthenticating: boolean;
43
+ isCompletingOAuth: boolean;
44
+ authError: string | null;
45
+ oauthTokens: OAuthResult | null;
46
+ }
47
+ export declare class OAuthHelper {
48
+ private config;
49
+ private discovery?;
50
+ private state;
51
+ private clientRegistration?;
52
+ private serverUrl?;
53
+ private storageKey;
54
+ constructor(config: OAuthConfig);
55
+ /**
56
+ * Get current OAuth state
57
+ */
58
+ getState(): OAuthState;
59
+ /**
60
+ * Load client registration from localStorage
61
+ */
62
+ private loadClientRegistration;
63
+ /**
64
+ * Save client registration to localStorage
65
+ */
66
+ private saveClientRegistration;
67
+ /**
68
+ * Check if a server requires authentication by pinging the URL
69
+ */
70
+ checkAuthRequired(serverUrl: string): Promise<boolean>;
71
+ /**
72
+ * Fallback heuristics for determining auth requirements when direct checking fails
73
+ */
74
+ private checkAuthByHeuristics;
75
+ /**
76
+ * Discover OAuth configuration from a server
77
+ */
78
+ discoverOAuthConfig(serverUrl: string): Promise<OAuthDiscovery>;
79
+ /**
80
+ * Register a new OAuth client dynamically
81
+ */
82
+ registerClient(serverUrl: string): Promise<ClientRegistration>;
83
+ /**
84
+ * Generate authorization URL for OAuth flow
85
+ */
86
+ generateAuthUrl(serverUrl: string, additionalParams?: Record<string, string>): string;
87
+ /**
88
+ * Exchange authorization code for access token
89
+ */
90
+ exchangeCodeForToken(serverUrl: string, code: string, codeVerifier?: string): Promise<OAuthResult>;
91
+ /**
92
+ * Handle OAuth callback and extract authorization code
93
+ */
94
+ handleCallback(): {
95
+ code: string;
96
+ state: string;
97
+ } | null;
98
+ /**
99
+ * Start OAuth flow by opening popup window (similar to your implementation)
100
+ */
101
+ startOAuthFlow(serverUrl: string): Promise<void>;
102
+ /**
103
+ * Complete OAuth flow by exchanging code for token
104
+ */
105
+ completeOAuthFlow(serverUrl: string, code: string): Promise<OAuthResult>;
106
+ /**
107
+ * Reset authentication state
108
+ */
109
+ resetAuth(): void;
110
+ /**
111
+ * Set OAuth state (internal method)
112
+ */
113
+ private setState;
114
+ /**
115
+ * Generate a random state parameter for CSRF protection
116
+ */
117
+ private generateState;
118
+ }
119
+ /**
120
+ * Linear-specific OAuth configuration
121
+ */
122
+ export declare const LINEAR_OAUTH_CONFIG: OAuthConfig;
123
+ /**
124
+ * Helper function to create OAuth-enabled MCP configuration
125
+ */
126
+ export declare function createOAuthMCPConfig(serverUrl: string, accessToken: string): {
127
+ mcpServers: {
128
+ linear: {
129
+ url: string;
130
+ authToken: string;
131
+ transport: string;
132
+ };
133
+ };
134
+ };
135
+ //# sourceMappingURL=oauth-helper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth-helper.d.ts","sourceRoot":"","sources":["../../src/oauth-helper.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,427 @@
1
+ /**
2
+ * OAuth helper for browser-based MCP authentication
3
+ *
4
+ * This helper provides OAuth 2.0 authorization code flow support for MCP servers
5
+ * that require authentication, such as Linear's MCP server.
6
+ */
7
+ export class OAuthHelper {
8
+ config;
9
+ discovery;
10
+ state;
11
+ clientRegistration;
12
+ serverUrl;
13
+ storageKey;
14
+ constructor(config) {
15
+ this.config = config;
16
+ this.storageKey = `mcp-oauth-${btoa(config.redirectUri)}`;
17
+ this.state = {
18
+ isRequired: false,
19
+ isAuthenticated: false,
20
+ isAuthenticating: false,
21
+ isCompletingOAuth: false,
22
+ authError: null,
23
+ oauthTokens: null,
24
+ };
25
+ // Load persisted client registration
26
+ this.loadClientRegistration();
27
+ }
28
+ /**
29
+ * Get current OAuth state
30
+ */
31
+ getState() {
32
+ return { ...this.state };
33
+ }
34
+ /**
35
+ * Load client registration from localStorage
36
+ */
37
+ loadClientRegistration() {
38
+ try {
39
+ const stored = localStorage.getItem(this.storageKey);
40
+ if (stored) {
41
+ const data = JSON.parse(stored);
42
+ this.clientRegistration = data.clientRegistration;
43
+ this.serverUrl = data.serverUrl;
44
+ console.log('🔄 [OAuthHelper] Loaded persisted client registration:', {
45
+ client_id: this.clientRegistration?.client_id,
46
+ server: this.serverUrl,
47
+ });
48
+ }
49
+ }
50
+ catch (error) {
51
+ console.warn('⚠️ [OAuthHelper] Failed to load client registration:', error);
52
+ }
53
+ }
54
+ /**
55
+ * Save client registration to localStorage
56
+ */
57
+ saveClientRegistration() {
58
+ try {
59
+ const data = {
60
+ clientRegistration: this.clientRegistration,
61
+ serverUrl: this.serverUrl,
62
+ };
63
+ localStorage.setItem(this.storageKey, JSON.stringify(data));
64
+ console.log('💾 [OAuthHelper] Saved client registration to localStorage');
65
+ }
66
+ catch (error) {
67
+ console.warn('⚠️ [OAuthHelper] Failed to save client registration:', error);
68
+ }
69
+ }
70
+ /**
71
+ * Check if a server requires authentication by pinging the URL
72
+ */
73
+ async checkAuthRequired(serverUrl) {
74
+ console.log('🔍 [OAuthHelper] Checking auth requirement for:', serverUrl);
75
+ try {
76
+ const response = await fetch(serverUrl, {
77
+ method: 'GET',
78
+ headers: {
79
+ 'Accept': 'text/event-stream',
80
+ 'Cache-Control': 'no-cache',
81
+ },
82
+ redirect: 'manual',
83
+ signal: AbortSignal.timeout(10000), // 10 second timeout
84
+ });
85
+ console.log('🔍 [OAuthHelper] Auth check response:', {
86
+ status: response.status,
87
+ statusText: response.statusText,
88
+ url: serverUrl,
89
+ });
90
+ // 401 Unauthorized, 403 Forbidden, or 400 Bad Request means auth is required
91
+ if (response.status === 401 || response.status === 403 || response.status === 400) {
92
+ console.log('🔐 [OAuthHelper] Authentication required for:', serverUrl);
93
+ return true;
94
+ }
95
+ // Any other response (200, 404, 500, etc.) means no auth required
96
+ console.log('✅ [OAuthHelper] No authentication required for:', serverUrl);
97
+ return false;
98
+ }
99
+ catch (error) {
100
+ console.warn('⚠️ [OAuthHelper] Could not check auth requirement for:', serverUrl, error);
101
+ // Handle specific error types
102
+ if (error.name === 'TypeError'
103
+ && (error.message?.includes('CORS') || error.message?.includes('Failed to fetch'))) {
104
+ console.log('🔍 [OAuthHelper] CORS blocked direct check, using heuristics for:', serverUrl);
105
+ return this.checkAuthByHeuristics(serverUrl);
106
+ }
107
+ if (error.name === 'AbortError') {
108
+ console.log('⏰ [OAuthHelper] Request timeout, assuming no auth required for:', serverUrl);
109
+ return false;
110
+ }
111
+ // If we can't reach the server at all, try heuristics
112
+ return this.checkAuthByHeuristics(serverUrl);
113
+ }
114
+ }
115
+ /**
116
+ * Fallback heuristics for determining auth requirements when direct checking fails
117
+ */
118
+ checkAuthByHeuristics(serverUrl) {
119
+ console.log('🔍 [OAuthHelper] Using heuristics to determine auth for:', serverUrl);
120
+ // Known patterns that typically require auth
121
+ const authRequiredPatterns = [
122
+ /api\.githubcopilot\.com/i, // GitHub Copilot
123
+ /api\.github\.com/i, // GitHub API
124
+ /.*\.googleapis\.com/i, // Google APIs
125
+ /api\.openai\.com/i, // OpenAI
126
+ /api\.anthropic\.com/i, // Anthropic
127
+ /.*\.atlassian\.net/i, // Atlassian (Jira, Confluence)
128
+ /.*\.slack\.com/i, // Slack
129
+ /api\.notion\.com/i, // Notion
130
+ /api\.linear\.app/i, // Linear
131
+ ];
132
+ // Known patterns that typically don't require auth (public MCP servers)
133
+ const noAuthPatterns = [
134
+ /localhost/i, // Local development
135
+ /127\.0\.0\.1/, // Local development
136
+ /\.local/i, // Local development
137
+ /mcp\..*\.com/i, // Generic MCP server pattern (often public)
138
+ ];
139
+ // Check no-auth patterns first
140
+ for (const pattern of noAuthPatterns) {
141
+ if (pattern.test(serverUrl)) {
142
+ console.log('✅ [OAuthHelper] Heuristic: No auth required (matches no-auth pattern):', serverUrl);
143
+ return false;
144
+ }
145
+ }
146
+ // Check auth-required patterns
147
+ for (const pattern of authRequiredPatterns) {
148
+ if (pattern.test(serverUrl)) {
149
+ console.log('🔐 [OAuthHelper] Heuristic: Auth required (matches auth pattern):', serverUrl);
150
+ return true;
151
+ }
152
+ }
153
+ // Default: assume no auth required for unknown patterns
154
+ console.log('❓ [OAuthHelper] Heuristic: Unknown pattern, assuming no auth required:', serverUrl);
155
+ return false;
156
+ }
157
+ /**
158
+ * Discover OAuth configuration from a server
159
+ */
160
+ async discoverOAuthConfig(serverUrl) {
161
+ try {
162
+ const discoveryUrl = `${serverUrl}/.well-known/oauth-authorization-server`;
163
+ const response = await fetch(discoveryUrl);
164
+ if (!response.ok) {
165
+ throw new Error(`OAuth discovery failed: ${response.status} ${response.statusText}`);
166
+ }
167
+ this.discovery = await response.json();
168
+ return this.discovery;
169
+ }
170
+ catch (error) {
171
+ throw new Error(`Failed to discover OAuth configuration: ${error}`);
172
+ }
173
+ }
174
+ /**
175
+ * Register a new OAuth client dynamically
176
+ */
177
+ async registerClient(serverUrl) {
178
+ if (!this.discovery) {
179
+ throw new Error('OAuth discovery not performed. Call discoverOAuthConfig first.');
180
+ }
181
+ if (!this.discovery.registration_endpoint) {
182
+ throw new Error('Server does not support dynamic client registration');
183
+ }
184
+ try {
185
+ const registrationData = {
186
+ client_name: this.config.clientName || 'MCP Use Example',
187
+ redirect_uris: [this.config.redirectUri],
188
+ grant_types: ['authorization_code'],
189
+ response_types: ['code'],
190
+ token_endpoint_auth_method: 'none', // Use public client (no secret)
191
+ scope: this.config.scope || 'read write',
192
+ };
193
+ console.log('🔐 [OAuthHelper] Registering OAuth client dynamically:', {
194
+ registration_endpoint: this.discovery.registration_endpoint,
195
+ client_name: registrationData.client_name,
196
+ redirect_uri: this.config.redirectUri,
197
+ });
198
+ const response = await fetch(this.discovery.registration_endpoint, {
199
+ method: 'POST',
200
+ headers: {
201
+ 'Content-Type': 'application/json',
202
+ },
203
+ body: JSON.stringify(registrationData),
204
+ });
205
+ if (!response.ok) {
206
+ const errorText = await response.text();
207
+ throw new Error(`Client registration failed: ${response.status} ${response.statusText} - ${errorText}`);
208
+ }
209
+ this.clientRegistration = await response.json();
210
+ this.serverUrl = serverUrl;
211
+ this.saveClientRegistration();
212
+ console.log('✅ [OAuthHelper] Client registered successfully:', {
213
+ client_id: this.clientRegistration.client_id,
214
+ client_secret: this.clientRegistration.client_secret ? '***' : 'none',
215
+ });
216
+ return this.clientRegistration;
217
+ }
218
+ catch (error) {
219
+ console.error('❌ [OAuthHelper] Client registration failed:', error);
220
+ throw new Error(`Failed to register OAuth client: ${error}`);
221
+ }
222
+ }
223
+ /**
224
+ * Generate authorization URL for OAuth flow
225
+ */
226
+ generateAuthUrl(serverUrl, additionalParams) {
227
+ if (!this.discovery) {
228
+ throw new Error('OAuth discovery not performed. Call discoverOAuthConfig first.');
229
+ }
230
+ if (!this.clientRegistration) {
231
+ throw new Error('Client not registered. Call registerClient first.');
232
+ }
233
+ const params = new URLSearchParams({
234
+ client_id: this.clientRegistration.client_id,
235
+ redirect_uri: this.config.redirectUri,
236
+ response_type: 'code',
237
+ scope: this.config.scope || 'read',
238
+ state: this.config.state || this.generateState(),
239
+ ...additionalParams,
240
+ });
241
+ return `${this.discovery.authorization_endpoint}?${params.toString()}`;
242
+ }
243
+ /**
244
+ * Exchange authorization code for access token
245
+ */
246
+ async exchangeCodeForToken(serverUrl, code, codeVerifier) {
247
+ if (!this.discovery) {
248
+ throw new Error('OAuth discovery not performed. Call discoverOAuthConfig first.');
249
+ }
250
+ if (!this.clientRegistration) {
251
+ throw new Error('Client not registered. Call registerClient first.');
252
+ }
253
+ const body = new URLSearchParams({
254
+ grant_type: 'authorization_code',
255
+ client_id: this.clientRegistration.client_id,
256
+ code,
257
+ redirect_uri: this.config.redirectUri,
258
+ });
259
+ if (codeVerifier) {
260
+ body.append('code_verifier', codeVerifier);
261
+ }
262
+ const response = await fetch(this.discovery.token_endpoint, {
263
+ method: 'POST',
264
+ headers: {
265
+ 'Content-Type': 'application/x-www-form-urlencoded',
266
+ },
267
+ body: body.toString(),
268
+ });
269
+ if (!response.ok) {
270
+ const error = await response.text();
271
+ throw new Error(`Token exchange failed: ${response.status} ${response.statusText} - ${error}`);
272
+ }
273
+ return await response.json();
274
+ }
275
+ /**
276
+ * Handle OAuth callback and extract authorization code
277
+ */
278
+ handleCallback() {
279
+ const urlParams = new URLSearchParams(window.location.search);
280
+ const code = urlParams.get('code');
281
+ const state = urlParams.get('state');
282
+ if (!code || !state) {
283
+ return null;
284
+ }
285
+ // Clean up URL
286
+ const url = new URL(window.location.href);
287
+ url.searchParams.delete('code');
288
+ url.searchParams.delete('state');
289
+ window.history.replaceState({}, '', url.toString());
290
+ return { code, state };
291
+ }
292
+ /**
293
+ * Start OAuth flow by opening popup window (similar to your implementation)
294
+ */
295
+ async startOAuthFlow(serverUrl) {
296
+ this.setState({
297
+ isAuthenticating: true,
298
+ authError: null,
299
+ });
300
+ try {
301
+ // Step 1: Discover OAuth configuration
302
+ await this.discoverOAuthConfig(serverUrl);
303
+ // Step 2: Register client dynamically (if not already registered)
304
+ if (!this.clientRegistration) {
305
+ await this.registerClient(serverUrl);
306
+ }
307
+ // Step 3: Generate authorization URL
308
+ const authUrl = this.generateAuthUrl(serverUrl);
309
+ // Step 4: Open popup window for authentication
310
+ const authWindow = window.open(authUrl, 'mcp-oauth', 'width=500,height=600,scrollbars=yes,resizable=yes,status=yes,location=yes');
311
+ if (!authWindow) {
312
+ throw new Error('Failed to open authentication window. Please allow popups for this site and try again.');
313
+ }
314
+ console.log('✅ [OAuthHelper] OAuth popup opened successfully');
315
+ }
316
+ catch (error) {
317
+ console.error('❌ [OAuthHelper] Failed to start OAuth flow:', error);
318
+ this.setState({
319
+ isAuthenticating: false,
320
+ authError: error instanceof Error ? error.message : 'Failed to start authentication',
321
+ });
322
+ throw error;
323
+ }
324
+ }
325
+ /**
326
+ * Complete OAuth flow by exchanging code for token
327
+ */
328
+ async completeOAuthFlow(serverUrl, code) {
329
+ this.setState({
330
+ isCompletingOAuth: true,
331
+ authError: null,
332
+ });
333
+ try {
334
+ // If we don't have discovery data, re-discover it
335
+ if (!this.discovery) {
336
+ console.log('🔍 [OAuthHelper] Re-discovering OAuth configuration for callback');
337
+ await this.discoverOAuthConfig(serverUrl);
338
+ }
339
+ // Only re-register if we don't have a client registration for this server
340
+ if (!this.clientRegistration || this.serverUrl !== serverUrl) {
341
+ console.log('🔐 [OAuthHelper] Re-registering client for callback');
342
+ await this.registerClient(serverUrl);
343
+ }
344
+ else {
345
+ console.log('🔄 [OAuthHelper] Using existing client registration for callback');
346
+ }
347
+ const tokenResponse = await this.exchangeCodeForToken(serverUrl, code);
348
+ this.setState({
349
+ isAuthenticating: false,
350
+ isAuthenticated: true,
351
+ isCompletingOAuth: false,
352
+ authError: null,
353
+ oauthTokens: tokenResponse,
354
+ });
355
+ console.log('✅ [OAuthHelper] OAuth flow completed successfully');
356
+ return tokenResponse;
357
+ }
358
+ catch (error) {
359
+ console.error('❌ [OAuthHelper] Failed to complete OAuth flow:', error);
360
+ this.setState({
361
+ isAuthenticating: false,
362
+ isCompletingOAuth: false,
363
+ authError: error instanceof Error ? error.message : 'Failed to complete authentication',
364
+ });
365
+ throw error;
366
+ }
367
+ }
368
+ /**
369
+ * Reset authentication state
370
+ */
371
+ resetAuth() {
372
+ this.setState({
373
+ isRequired: false,
374
+ isAuthenticated: false,
375
+ isAuthenticating: false,
376
+ isCompletingOAuth: false,
377
+ authError: null,
378
+ oauthTokens: null,
379
+ });
380
+ // Clear stored client registration
381
+ this.clientRegistration = undefined;
382
+ this.serverUrl = undefined;
383
+ try {
384
+ localStorage.removeItem(this.storageKey);
385
+ console.log('🗑️ [OAuthHelper] Cleared stored client registration');
386
+ }
387
+ catch (error) {
388
+ console.warn('⚠️ [OAuthHelper] Failed to clear client registration:', error);
389
+ }
390
+ }
391
+ /**
392
+ * Set OAuth state (internal method)
393
+ */
394
+ setState(newState) {
395
+ this.state = { ...this.state, ...newState };
396
+ }
397
+ /**
398
+ * Generate a random state parameter for CSRF protection
399
+ */
400
+ generateState() {
401
+ return Math.random().toString(36).substring(2, 15)
402
+ + Math.random().toString(36).substring(2, 15);
403
+ }
404
+ }
405
+ /**
406
+ * Linear-specific OAuth configuration
407
+ */
408
+ export const LINEAR_OAUTH_CONFIG = {
409
+ // No clientId needed - will use dynamic client registration
410
+ redirectUri: typeof window !== 'undefined' ? window.location.origin + window.location.pathname : 'http://localhost:5174',
411
+ scope: 'read write',
412
+ clientName: 'MCP Use Example',
413
+ };
414
+ /**
415
+ * Helper function to create OAuth-enabled MCP configuration
416
+ */
417
+ export function createOAuthMCPConfig(serverUrl, accessToken) {
418
+ return {
419
+ mcpServers: {
420
+ linear: {
421
+ url: serverUrl,
422
+ authToken: accessToken,
423
+ transport: 'sse',
424
+ },
425
+ },
426
+ };
427
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mcp-use",
3
3
  "type": "module",
4
- "version": "0.1.20",
4
+ "version": "0.2.0",
5
5
  "packageManager": "pnpm@10.6.1",
6
6
  "description": "A utility library for integrating Model Context Protocol (MCP) with LangChain, Zod, and related tools. Provides helpers for schema conversion, event streaming, and SDK usage.",
7
7
  "author": "mcp-use, Inc.",
@@ -30,6 +30,10 @@
30
30
  ".": {
31
31
  "types": "./dist/index.d.ts",
32
32
  "import": "./dist/index.js"
33
+ },
34
+ "./browser": {
35
+ "types": "./dist/src/browser.d.ts",
36
+ "import": "./dist/src/browser.js"
33
37
  }
34
38
  },
35
39
  "main": "./dist/index.js",
@@ -103,6 +107,7 @@
103
107
  "posthog-node": "^5.1.1",
104
108
  "uuid": "^11.1.0",
105
109
  "winston": "^3.17.0",
110
+ "winston-transport-browserconsole": "^1.0.5",
106
111
  "ws": "^8.18.2",
107
112
  "zod": "^3.25.48",
108
113
  "zod-to-json-schema": "^3.24.6"
@@ -1,8 +0,0 @@
1
- /**
2
- * Dynamic server management example for mcp-use.
3
- *
4
- * This example demonstrates how to equip an MCPAgent with a tool
5
- * to dynamically add and connect to MCP servers during a run.
6
- */
7
- export {};
8
- //# sourceMappingURL=add_server_tool.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"add_server_tool.d.ts","sourceRoot":"","sources":["../../examples/add_server_tool.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
@@ -1,79 +0,0 @@
1
- /**
2
- * Dynamic server management example for mcp-use.
3
- *
4
- * This example demonstrates how to equip an MCPAgent with a tool
5
- * to dynamically add and connect to MCP servers during a run.
6
- */
7
- import { ChatOpenAI } from '@langchain/openai';
8
- import { config } from 'dotenv';
9
- import { MCPAgent, MCPClient } from '../index.js';
10
- import { LangChainAdapter } from '../src/adapters/langchain_adapter.js';
11
- import { ServerManager } from '../src/managers/server_manager.js';
12
- import { AddMCPServerFromConfigTool } from '../src/managers/tools/add_server_from_config.js';
13
- // Load environment variables from .env file
14
- config();
15
- async function main() {
16
- // Create an empty MCPClient. It has no servers to start with.
17
- const client = new MCPClient();
18
- // The LLM to power the agent
19
- const llm = new ChatOpenAI({ model: 'gpt-4o', temperature: 0 });
20
- const serverManager = new ServerManager(client, new LangChainAdapter());
21
- serverManager.setManagementTools([new AddMCPServerFromConfigTool(serverManager)]);
22
- // Create the agent, enabling the ServerManager
23
- const agent = new MCPAgent({
24
- llm,
25
- client,
26
- maxSteps: 30,
27
- autoInitialize: true,
28
- useServerManager: true,
29
- serverManagerFactory: () => serverManager,
30
- });
31
- // Define the server configuration that the agent will be asked to add.
32
- const serverConfigA = {
33
- command: 'npx',
34
- args: ['@playwright/mcp@latest', '--headless'],
35
- env: {
36
- DISPLAY: ':1',
37
- },
38
- };
39
- const serverConfigB = {
40
- command: 'npx',
41
- args: ['-y', '@openbnb/mcp-server-airbnb', '--ignore-robots-txt'],
42
- };
43
- // We'll pass the config as a JSON string in the prompt.
44
- const serverConfigStringA = JSON.stringify(serverConfigA, null, 2);
45
- const serverConfigStringB = JSON.stringify(serverConfigB, null, 2);
46
- const query = `I need to browse the web. To do this, please add and connect to a new MCP server for Playwright.
47
- The server name is 'playwright' and its configuration is:
48
- \`\`\`json
49
- ${serverConfigStringA}
50
- \`\`\`
51
- Once the server is ready, navigate to https://github.com/mcp-use/mcp-use, give a star to the project, and then provide a concise summary of the project's README.
52
-
53
- Then, please add and connect to a new MCP server for Airbnb.
54
- The server name is 'airbnb' and its configuration is:
55
- \`\`\`json
56
- ${serverConfigStringB}
57
- \`\`\`
58
- and give me a house in the location of the company mcp-use.
59
- `;
60
- // Run the agent. We call `stream()` to get the async generator.
61
- const stepIterator = agent.stream(query);
62
- let result;
63
- while (true) {
64
- const { done, value } = await stepIterator.next();
65
- if (done) {
66
- result = value;
67
- break;
68
- }
69
- // You can inspect the intermediate steps here.
70
- console.log('--- Agent Step ---');
71
- console.dir(value, { depth: 4 });
72
- }
73
- console.log(`\n✅ Final Result:\n${result}`);
74
- // Clean up the session created by the agent
75
- await client.closeAllSessions();
76
- }
77
- if (import.meta.url === `file://${process.argv[1]}`) {
78
- main().catch(console.error);
79
- }