#!/usr/bin/env node "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const node_http_1 = require("node:http"); const node_readline_1 = require("node:readline"); const node_url_1 = require("node:url"); const node_child_process_1 = require("node:child_process"); const index_js_1 = require("../../client/index.js"); const streamableHttp_js_1 = require("../../client/streamableHttp.js"); const types_js_1 = require("../../types.js"); const auth_js_1 = require("../../client/auth.js"); // Configuration const DEFAULT_SERVER_URL = 'http://localhost:3000/mcp'; const CALLBACK_PORT = 8090; // Use different port than auth server (3001) const CALLBACK_URL = `http://localhost:${CALLBACK_PORT}/callback`; /** * In-memory OAuth client provider for demonstration purposes * In production, you should persist tokens securely */ class InMemoryOAuthClientProvider { constructor(_redirectUrl, _clientMetadata, onRedirect) { this._redirectUrl = _redirectUrl; this._clientMetadata = _clientMetadata; this._onRedirect = onRedirect || ((url) => { console.log(`Redirect to: ${url.toString()}`); }); } get redirectUrl() { return this._redirectUrl; } get clientMetadata() { return this._clientMetadata; } clientInformation() { return this._clientInformation; } saveClientInformation(clientInformation) { this._clientInformation = clientInformation; } tokens() { return this._tokens; } saveTokens(tokens) { this._tokens = tokens; } redirectToAuthorization(authorizationUrl) { this._onRedirect(authorizationUrl); } saveCodeVerifier(codeVerifier) { this._codeVerifier = codeVerifier; } codeVerifier() { if (!this._codeVerifier) { throw new Error('No code verifier saved'); } return this._codeVerifier; } } /** * Interactive MCP client with OAuth authentication * Demonstrates the complete OAuth flow with browser-based authorization */ class InteractiveOAuthClient { constructor(serverUrl) { this.serverUrl = serverUrl; this.client = null; this.rl = (0, node_readline_1.createInterface)({ input: process.stdin, output: process.stdout, }); } /** * Prompts user for input via readline */ async question(query) { return new Promise((resolve) => { this.rl.question(query, resolve); }); } /** * Opens the authorization URL in the user's default browser */ async openBrowser(url) { console.log(`š Opening browser for authorization: ${url}`); const command = `open "${url}"`; (0, node_child_process_1.exec)(command, (error) => { if (error) { console.error(`Failed to open browser: ${error.message}`); console.log(`Please manually open: ${url}`); } }); } /** * Example OAuth callback handler - in production, use a more robust approach * for handling callbacks and storing tokens */ /** * Starts a temporary HTTP server to receive the OAuth callback */ async waitForOAuthCallback() { return new Promise((resolve, reject) => { const server = (0, node_http_1.createServer)((req, res) => { // Ignore favicon requests if (req.url === '/favicon.ico') { res.writeHead(404); res.end(); return; } console.log(`š„ Received callback: ${req.url}`); const parsedUrl = new node_url_1.URL(req.url || '', 'http://localhost'); const code = parsedUrl.searchParams.get('code'); const error = parsedUrl.searchParams.get('error'); if (code) { console.log(`ā Authorization code received: ${code === null || code === void 0 ? void 0 : code.substring(0, 10)}...`); res.writeHead(200, { 'Content-Type': 'text/html' }); res.end(`
You can close this window and return to the terminal.
`); resolve(code); setTimeout(() => server.close(), 3000); } else if (error) { console.log(`ā Authorization error: ${error}`); res.writeHead(400, { 'Content-Type': 'text/html' }); res.end(`Error: ${error}
`); reject(new Error(`OAuth authorization failed: ${error}`)); } else { console.log(`ā No authorization code or error in callback`); res.writeHead(400); res.end('Bad request'); reject(new Error('No authorization code provided')); } }); server.listen(CALLBACK_PORT, () => { console.log(`OAuth callback server started on http://localhost:${CALLBACK_PORT}`); }); }); } async attemptConnection(oauthProvider) { console.log('š¢ Creating transport with OAuth provider...'); const baseUrl = new node_url_1.URL(this.serverUrl); const transport = new streamableHttp_js_1.StreamableHTTPClientTransport(baseUrl, { authProvider: oauthProvider }); console.log('š¢ Transport created'); try { console.log('š Attempting connection (this will trigger OAuth redirect)...'); await this.client.connect(transport); console.log('ā Connected successfully'); } catch (error) { if (error instanceof auth_js_1.UnauthorizedError) { console.log('š OAuth required - waiting for authorization...'); const callbackPromise = this.waitForOAuthCallback(); const authCode = await callbackPromise; await transport.finishAuth(authCode); console.log('š Authorization code received:', authCode); console.log('š Reconnecting with authenticated transport...'); await this.attemptConnection(oauthProvider); } else { console.error('ā Connection failed with non-auth error:', error); throw error; } } } /** * Establishes connection to the MCP server with OAuth authentication */ async connect() { console.log(`š Attempting to connect to ${this.serverUrl}...`); const clientMetadata = { client_name: 'Simple OAuth MCP Client', redirect_uris: [CALLBACK_URL], grant_types: ['authorization_code', 'refresh_token'], response_types: ['code'], token_endpoint_auth_method: 'client_secret_post', scope: 'mcp:tools' }; console.log('š Creating OAuth provider...'); const oauthProvider = new InMemoryOAuthClientProvider(CALLBACK_URL, clientMetadata, (redirectUrl) => { console.log(`š OAuth redirect handler called - opening browser`); console.log(`Opening browser to: ${redirectUrl.toString()}`); this.openBrowser(redirectUrl.toString()); }); console.log('š OAuth provider created'); console.log('š¤ Creating MCP client...'); this.client = new index_js_1.Client({ name: 'simple-oauth-client', version: '1.0.0', }, { capabilities: {} }); console.log('š¤ Client created'); console.log('š Starting OAuth flow...'); await this.attemptConnection(oauthProvider); // Start interactive loop await this.interactiveLoop(); } /** * Main interactive loop for user commands */ async interactiveLoop() { console.log('\nšÆ Interactive MCP Client with OAuth'); console.log('Commands:'); console.log(' list - List available tools'); console.log(' call