mcp-use
Version:
Opinionated MCP Framework for TypeScript (@modelcontextprotocol/sdk compatible) - Build MCP Agents, Clients and Servers with support for ChatGPT Apps, Code Mode, OAuth, Notifications, Sampling, Observability and more.
543 lines (541 loc) • 15.4 kB
JavaScript
import {
ElicitationDeclinedError,
ElicitationTimeoutError,
ElicitationValidationError
} from "./chunk-KUEVOU4M.js";
import {
createReadableStreamFromGenerator,
streamEventsToAISDK,
streamEventsToAISDKWithTools
} from "./chunk-LGDFGYRL.js";
import "./chunk-GXNAXUDI.js";
import {
PROMPTS
} from "./chunk-DTHLI4WJ.js";
import {
AcquireActiveMCPServerTool,
AddMCPServerFromConfigTool,
ConnectMCPServerTool,
ListMCPServersTool,
MCPAgent,
ObservabilityManager,
ReleaseMCPServerConnectionTool,
RemoteAgent,
ServerManager,
createLLMFromString,
getSupportedProviders,
isValidLLMString,
parseLLMString
} from "./chunk-FDT46IKB.js";
import "./chunk-JRGQRPTN.js";
import {
BaseCodeExecutor,
E2BCodeExecutor,
MCPClient,
StdioConnector,
VMCodeExecutor,
isVMAvailable,
loadConfigFile
} from "./chunk-T7EN7JPJ.js";
import {
BaseAdapter
} from "./chunk-MFSO5PUW.js";
import "./chunk-JQKKMUCT.js";
import {
ErrorBoundary,
Image,
McpUseProvider,
ThemeProvider,
WidgetControls,
useMcp,
useWidget,
useWidgetProps,
useWidgetState,
useWidgetTheme
} from "./chunk-UNSBX4AO.js";
import "./chunk-MUC4N6UU.js";
import {
BaseConnector,
HttpConnector,
MCPSession
} from "./chunk-XSB7YRNQ.js";
import {
Tel,
Telemetry,
VERSION,
getPackageVersion,
setTelemetrySource
} from "./chunk-CN263ZGG.js";
import {
Logger,
logger
} from "./chunk-FRUZDWXH.js";
import {
BrowserOAuthClientProvider,
onMcpAuthorization
} from "./chunk-J75I2C26.js";
import {
__name
} from "./chunk-3GQAWCBQ.js";
// src/oauth-helper.ts
var OAuthHelper = class {
static {
__name(this, "OAuthHelper");
}
config;
discovery;
state;
clientRegistration;
constructor(config) {
this.config = config;
this.state = {
isRequired: false,
isAuthenticated: false,
isAuthenticating: false,
isCompletingOAuth: false,
authError: null,
oauthTokens: null
};
}
/**
* Get current OAuth state
*/
getState() {
return { ...this.state };
}
/**
* Check if a server requires authentication by pinging the URL
*/
async checkAuthRequired(serverUrl) {
console.log("\u{1F50D} [OAuthHelper] Checking auth requirement for:", serverUrl);
try {
const response = await fetch(serverUrl, {
method: "GET",
headers: {
Accept: "text/event-stream",
"Cache-Control": "no-cache"
},
redirect: "manual",
signal: AbortSignal.timeout(1e4)
// 10 second timeout
});
console.log("\u{1F50D} [OAuthHelper] Auth check response:", {
status: response.status,
statusText: response.statusText,
url: serverUrl
});
if (response.status === 401 || response.status === 403 || response.status === 400) {
console.log("\u{1F510} [OAuthHelper] Authentication required for:", serverUrl);
return true;
}
console.log(
"\u2705 [OAuthHelper] No authentication required for:",
serverUrl
);
return false;
} catch (error) {
const err = error;
console.warn(
"\u26A0\uFE0F [OAuthHelper] Could not check auth requirement for:",
serverUrl,
error
);
if (err.name === "TypeError" && (err.message?.includes("CORS") || err.message?.includes("Failed to fetch"))) {
console.log(
"\u{1F50D} [OAuthHelper] CORS blocked direct check, using heuristics for:",
serverUrl
);
return this.checkAuthByHeuristics(serverUrl);
}
if (err.name === "AbortError") {
console.log(
"\u23F0 [OAuthHelper] Request timeout, assuming no auth required for:",
serverUrl
);
return false;
}
return this.checkAuthByHeuristics(serverUrl);
}
}
/**
* Fallback heuristics for determining auth requirements when direct checking fails
*/
checkAuthByHeuristics(serverUrl) {
console.log(
"\u{1F50D} [OAuthHelper] Using heuristics to determine auth for:",
serverUrl
);
const authRequiredPatterns = [
/api\.githubcopilot\.com/i,
// GitHub Copilot
/api\.github\.com/i,
// GitHub API
/[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.googleapis\.com/i,
// Google APIs (DNS-safe, max 63 chars per label)
/api\.openai\.com/i,
// OpenAI
/api\.anthropic\.com/i,
// Anthropic
/[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.atlassian\.net/i,
// Atlassian (Jira, Confluence)
/[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.slack\.com/i,
// Slack
/api\.notion\.com/i,
// Notion
/api\.linear\.app/i
// Linear
];
const noAuthPatterns = [
/localhost/i,
// Local development
/127\.0\.0\.1/,
// Local development
/\.local/i,
// Local development
/mcp\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.com/i
// Generic MCP server pattern (often public)
];
for (const pattern of noAuthPatterns) {
if (pattern.test(serverUrl)) {
console.log(
"\u2705 [OAuthHelper] Heuristic: No auth required (matches no-auth pattern):",
serverUrl
);
return false;
}
}
for (const pattern of authRequiredPatterns) {
if (pattern.test(serverUrl)) {
console.log(
"\u{1F510} [OAuthHelper] Heuristic: Auth required (matches auth pattern):",
serverUrl
);
return true;
}
}
console.log(
"\u2753 [OAuthHelper] Heuristic: Unknown pattern, assuming no auth required:",
serverUrl
);
return false;
}
/**
* Discover OAuth configuration from a server
*/
async discoverOAuthConfig(serverUrl) {
try {
const discoveryUrl = `${serverUrl}/.well-known/oauth-authorization-server`;
console.log(
"\u{1F50D} [OAuthHelper] Attempting OAuth discovery at:",
discoveryUrl
);
const response = await fetch(discoveryUrl);
if (!response.ok) {
console.error("\u274C [OAuthHelper] OAuth discovery failed:", {
status: response.status,
statusText: response.statusText,
url: discoveryUrl
});
throw new Error(
`OAuth discovery failed: ${response.status} ${response.statusText}`
);
}
this.discovery = await response.json();
console.log("\u2705 [OAuthHelper] OAuth discovery successful:", {
authorization_endpoint: this.discovery?.authorization_endpoint,
token_endpoint: this.discovery?.token_endpoint,
registration_endpoint: this.discovery?.registration_endpoint
});
return this.discovery;
} catch (error) {
console.error("\u274C [OAuthHelper] OAuth discovery error:", error);
throw new Error(`Failed to discover OAuth configuration: ${error}`);
}
}
/**
* Register a new OAuth client dynamically
*/
async registerClient(_serverUrl) {
if (!this.discovery) {
throw new Error(
"OAuth discovery not performed. Call discoverOAuthConfig first."
);
}
if (!this.discovery.registration_endpoint) {
throw new Error("Server does not support dynamic client registration");
}
try {
const registrationData = {
client_name: this.config.clientName || "MCP Use Example",
redirect_uris: [this.config.redirectUri],
grant_types: ["authorization_code"],
response_types: ["code"],
token_endpoint_auth_method: "none",
// Use public client (no secret)
scope: this.config.scope || "read write"
};
console.log("\u{1F510} [OAuthHelper] Registering OAuth client dynamically:", {
registration_endpoint: this.discovery.registration_endpoint,
client_name: registrationData.client_name,
redirect_uri: this.config.redirectUri
});
const response = await fetch(this.discovery.registration_endpoint, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(registrationData)
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(
`Client registration failed: ${response.status} ${response.statusText} - ${errorText}`
);
}
this.clientRegistration = await response.json();
console.log("\u2705 [OAuthHelper] Client registered successfully:", {
client_id: this.clientRegistration?.client_id,
client_secret: this.clientRegistration?.client_secret ? "***" : "none"
});
return this.clientRegistration;
} catch (error) {
console.error("\u274C [OAuthHelper] Client registration failed:", error);
throw new Error(`Failed to register OAuth client: ${error}`);
}
}
/**
* Generate authorization URL for OAuth flow
*/
generateAuthUrl(serverUrl, additionalParams) {
if (!this.discovery) {
throw new Error(
"OAuth discovery not performed. Call discoverOAuthConfig first."
);
}
if (!this.clientRegistration) {
throw new Error("Client not registered. Call registerClient first.");
}
const params = new URLSearchParams({
client_id: this.clientRegistration.client_id,
redirect_uri: this.config.redirectUri,
response_type: "code",
scope: this.config.scope || "read",
state: this.config.state || this.generateState(),
...additionalParams
});
return `${this.discovery.authorization_endpoint}?${params.toString()}`;
}
/**
* Exchange authorization code for access token
*/
async exchangeCodeForToken(serverUrl, code, codeVerifier) {
if (!this.discovery) {
throw new Error(
"OAuth discovery not performed. Call discoverOAuthConfig first."
);
}
if (!this.clientRegistration) {
throw new Error("Client not registered. Call registerClient first.");
}
const body = new URLSearchParams({
grant_type: "authorization_code",
client_id: this.clientRegistration.client_id,
code,
redirect_uri: this.config.redirectUri
});
if (codeVerifier) {
body.append("code_verifier", codeVerifier);
}
const response = await fetch(this.discovery.token_endpoint, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
body: body.toString()
});
if (!response.ok) {
const error = await response.text();
throw new Error(
`Token exchange failed: ${response.status} ${response.statusText} - ${error}`
);
}
return await response.json();
}
/**
* Handle OAuth callback and extract authorization code
*/
handleCallback() {
const urlParams = new URLSearchParams(window.location.search);
const code = urlParams.get("code");
const state = urlParams.get("state");
if (!code || !state) {
return null;
}
const url = new URL(window.location.href);
url.searchParams.delete("code");
url.searchParams.delete("state");
window.history.replaceState({}, "", url.toString());
return { code, state };
}
/**
* Start OAuth flow by opening popup window (similar to your implementation)
*/
async startOAuthFlow(serverUrl) {
this.setState({
isAuthenticating: true,
authError: null
});
try {
await this.discoverOAuthConfig(serverUrl);
await this.registerClient(serverUrl);
const authUrl = this.generateAuthUrl(serverUrl);
const authWindow = window.open(
authUrl,
"mcp-oauth",
"width=500,height=600,scrollbars=yes,resizable=yes,status=yes,location=yes"
);
if (!authWindow) {
throw new Error(
"Failed to open authentication window. Please allow popups for this site and try again."
);
}
console.log("\u2705 [OAuthHelper] OAuth popup opened successfully");
} catch (error) {
console.error("\u274C [OAuthHelper] Failed to start OAuth flow:", error);
this.setState({
isAuthenticating: false,
authError: error instanceof Error ? error.message : "Failed to start authentication"
});
throw error;
}
}
/**
* Complete OAuth flow by exchanging code for token
*/
async completeOAuthFlow(serverUrl, code) {
this.setState({
isCompletingOAuth: true,
authError: null
});
try {
const tokenResponse = await this.exchangeCodeForToken(serverUrl, code);
this.setState({
isAuthenticating: false,
isAuthenticated: true,
isCompletingOAuth: false,
authError: null,
oauthTokens: tokenResponse
});
console.log("\u2705 [OAuthHelper] OAuth flow completed successfully");
return tokenResponse;
} catch (error) {
console.error("\u274C [OAuthHelper] Failed to complete OAuth flow:", error);
this.setState({
isAuthenticating: false,
isCompletingOAuth: false,
authError: error instanceof Error ? error.message : "Failed to complete authentication"
});
throw error;
}
}
/**
* Reset authentication state
*/
resetAuth() {
this.setState({
isRequired: false,
isAuthenticated: false,
isAuthenticating: false,
isCompletingOAuth: false,
authError: null,
oauthTokens: null
});
}
/**
* Set OAuth state (internal method)
*/
setState(newState) {
this.state = { ...this.state, ...newState };
}
/**
* Generate a random state parameter for CSRF protection
*/
generateState() {
return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
}
};
var LINEAR_OAUTH_CONFIG = {
// No clientId needed - will use dynamic client registration
redirectUri: typeof window !== "undefined" ? window.location.origin + window.location.pathname : "http://localhost:5173",
scope: "read write",
clientName: "MCP Use Example"
};
function createOAuthMCPConfig(serverUrl, accessToken) {
return {
mcpServers: {
linear: {
url: serverUrl,
authToken: accessToken,
transport: "sse"
}
}
};
}
__name(createOAuthMCPConfig, "createOAuthMCPConfig");
export {
AcquireActiveMCPServerTool,
AddMCPServerFromConfigTool,
BaseAdapter,
BaseCodeExecutor,
BaseConnector,
BrowserOAuthClientProvider,
Tel as BrowserTelemetry,
ConnectMCPServerTool,
E2BCodeExecutor,
ElicitationDeclinedError,
ElicitationTimeoutError,
ElicitationValidationError,
ErrorBoundary,
HttpConnector,
Image,
LINEAR_OAUTH_CONFIG,
ListMCPServersTool,
Logger,
MCPAgent,
MCPClient,
MCPSession,
McpUseProvider,
OAuthHelper,
ObservabilityManager,
PROMPTS,
ReleaseMCPServerConnectionTool,
RemoteAgent,
ServerManager,
StdioConnector,
Tel,
Telemetry,
ThemeProvider,
VERSION,
VMCodeExecutor,
WidgetControls,
createLLMFromString,
createOAuthMCPConfig,
createReadableStreamFromGenerator,
getPackageVersion,
getSupportedProviders,
isVMAvailable,
isValidLLMString,
loadConfigFile,
logger,
onMcpAuthorization,
parseLLMString,
setTelemetrySource as setBrowserTelemetrySource,
setTelemetrySource,
streamEventsToAISDK,
streamEventsToAISDKWithTools,
useMcp,
useWidget,
useWidgetProps,
useWidgetState,
useWidgetTheme
};