11#!/usr/bin/env node
2- "use strict" ;
3- var __awaiter = ( this && this . __awaiter ) || function ( thisArg , _arguments , P , generator ) {
4- function adopt ( value ) { return value instanceof P ? value : new P ( function ( resolve ) { resolve ( value ) ; } ) ; }
5- return new ( P || ( P = Promise ) ) ( function ( resolve , reject ) {
6- function fulfilled ( value ) { try { step ( generator . next ( value ) ) ; } catch ( e ) { reject ( e ) ; } }
7- function rejected ( value ) { try { step ( generator [ "throw" ] ( value ) ) ; } catch ( e ) { reject ( e ) ; } }
8- function step ( result ) { result . done ? resolve ( result . value ) : adopt ( result . value ) . then ( fulfilled , rejected ) ; }
9- step ( ( generator = generator . apply ( thisArg , _arguments || [ ] ) ) . next ( ) ) ;
10- } ) ;
11- } ;
12- Object . defineProperty ( exports , "__esModule" , { value : true } ) ;
13- const mcp_js_1 = require ( "@modelcontextprotocol/sdk/server/mcp.js" ) ;
14- const stdio_js_1 = require ( "@modelcontextprotocol/sdk/server/stdio.js" ) ;
2+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js" ;
3+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js" ;
4+ import express from "express" ; // Fixing type imports
5+ import dotenv from "dotenv" ;
6+ dotenv . config ( ) ;
7+ // Replace require() with dynamic import for node-fetch
8+ const fetchDynamic = async ( ) => ( await import ( "node-fetch" ) ) . default ;
159function wait ( milliseconds ) {
1610 return new Promise ( resolve => setTimeout ( resolve , milliseconds ) ) ;
1711}
18- const server = new mcp_js_1 . McpServer ( {
12+ const server = new McpServer ( {
1913 name : "MongoDB Atlas" ,
2014 version : "1.0.0"
2115} ) ;
16+ // Move clientId to a state variable
2217var state = {
2318 auth : false ,
19+ token : "" , // Added token property
20+ deviceCode : "" ,
21+ verificationUri : "" ,
22+ userCode : "" ,
23+ clientId : process . env . CLIENT_ID || "0oabtxactgS3gHIR0297" , // Moved clientId to state
2424} ;
25- server . tool ( "auth" , "Authenticate to atlas" , ( _a ) => __awaiter ( void 0 , [ _a ] , void 0 , function * ( { } ) {
26- yield wait ( 1000 ) ;
27- setTimeout ( ( ) => {
28- console . error ( "Authenticated" ) ;
29- state . auth = true ;
30- } , 10000 ) ;
25+ const app = express ( ) ;
26+ let authCode = "" ;
27+ app . get ( "/callback" , ( req , res ) => {
28+ authCode = req . query . code ;
29+ res . send ( "Authentication successful! You can close this tab." ) ;
30+ } ) ;
31+ // Update the device code request to align with the Atlas Go SDK
32+ server . tool ( "auth" , "Authenticate to Atlas" , async ( { } ) => {
33+ console . log ( "Starting authentication process..." ) ;
34+ const authUrl = "https://cloud.mongodb.com/api/private/unauth/account/device/authorize" ; // Updated endpoint
35+ console . log ( "Client ID:" , state . clientId ) ;
36+ // Step 1: Request a device code
37+ const deviceCodeResponse = await ( await fetchDynamic ( ) ) ( authUrl , {
38+ method : "POST" ,
39+ headers : {
40+ "Content-Type" : "application/x-www-form-urlencoded" ,
41+ } ,
42+ body : new URLSearchParams ( {
43+ client_id : state . clientId , // Use state.clientId
44+ scope : "openid" ,
45+ } ) . toString ( ) ,
46+ } ) ;
47+ const responseText = await deviceCodeResponse . text ( ) ; // Capture the full response body
48+ console . log ( "Device Code Response Body:" , responseText ) ;
49+ if ( ! deviceCodeResponse . ok ) {
50+ console . error ( "Failed to initiate authentication:" , deviceCodeResponse . statusText ) ;
51+ return {
52+ content : [ { type : "text" , text : `Failed to initiate authentication: ${ deviceCodeResponse . statusText } ` } ] ,
53+ } ;
54+ }
55+ const deviceCodeData = JSON . parse ( responseText ) ; // Parse the response body
56+ console . log ( `Please authenticate by visiting the following URL: ${ deviceCodeData . verification_uri } ` ) ;
57+ console . log ( `Enter the code: ${ deviceCodeData . user_code } ` ) ;
58+ // Store the device code data for further use
59+ state . deviceCode = deviceCodeData . device_code ;
60+ state . verificationUri = deviceCodeData . verification_uri ;
61+ state . userCode = deviceCodeData . user_code ;
3162 return {
32- content : [ { type : "text" , text : "Navigate to http://cloud.mongodb.com and input code A56T88." } ] ,
63+ content : [
64+ { type : "text" , text : `Please authenticate by visiting ${ deviceCodeData . verification_uri } and entering the code ${ deviceCodeData . user_code } ` } ,
65+ ] ,
3366 } ;
34- } ) ) ;
35- server . tool ( "list-clusters" , "Lists clusters" , ( _a ) => __awaiter ( void 0 , [ _a ] , void 0 , function * ( { } ) {
36- yield wait ( 1000 ) ;
67+ } ) ;
68+ // Add PollToken functionality to the auth tool
69+ server . tool ( "poll-token" , "Poll for Access Token" , async ( { } ) => {
70+ console . log ( "Starting token polling process..." ) ;
71+ if ( ! state . deviceCode ) {
72+ console . error ( "Device code not found. Please initiate authentication first." ) ;
73+ return {
74+ content : [ { type : "text" , text : "Device code not found. Please initiate authentication first." } ] ,
75+ } ;
76+ }
77+ const tokenEndpoint = "https://cloud.mongodb.com/api/private/unauth/account/device/token" ;
78+ const interval = 5 * 1000 ; // Default polling interval in milliseconds
79+ const expiresAt = Date . now ( ) + 15 * 60 * 1000 ; // Assume 15 minutes expiration for the device code
80+ while ( Date . now ( ) < expiresAt ) {
81+ await wait ( interval ) ;
82+ try {
83+ const tokenResponse = await ( await fetchDynamic ( ) ) ( tokenEndpoint , {
84+ method : "POST" ,
85+ headers : {
86+ "Content-Type" : "application/x-www-form-urlencoded" ,
87+ } ,
88+ body : new URLSearchParams ( {
89+ client_id : state . clientId , // Use state.clientId
90+ device_code : state . deviceCode ,
91+ grant_type : "urn:ietf:params:oauth:grant-type:device_code" ,
92+ } ) . toString ( ) ,
93+ } ) ;
94+ const responseText = await tokenResponse . text ( ) ;
95+ console . log ( "Token Response Body:" , responseText ) ;
96+ if ( tokenResponse . ok ) {
97+ const tokenData = JSON . parse ( responseText ) ;
98+ console . log ( "Authentication successful. Token received:" , tokenData . access_token ) ;
99+ // Store the token
100+ state . auth = true ;
101+ state . token = tokenData . access_token ;
102+ return {
103+ content : [ { type : "text" , text : "Authentication successful! You are now logged in." } ] ,
104+ } ;
105+ }
106+ else {
107+ console . error ( "Token Response Error:" , responseText ) ;
108+ const errorResponse = JSON . parse ( responseText ) ;
109+ if ( errorResponse . error === "authorization_pending" ) {
110+ console . log ( "Authorization pending. Retrying..." ) ;
111+ continue ;
112+ }
113+ else if ( errorResponse . error === "expired_token" ) {
114+ console . error ( "Device code expired. Please restart the authentication process." ) ;
115+ return {
116+ content : [ { type : "text" , text : "Device code expired. Please restart the authentication process." } ] ,
117+ } ;
118+ }
119+ else {
120+ console . error ( "Failed to authenticate:" , errorResponse . error_description || "Unknown error" ) ;
121+ return {
122+ content : [ { type : "text" , text : `Failed to authenticate: ${ errorResponse . error_description || "Unknown error" } ` } ] ,
123+ } ;
124+ }
125+ }
126+ }
127+ catch ( error ) {
128+ if ( error instanceof Error ) {
129+ console . error ( "Unexpected error during token polling:" , error ) ;
130+ return {
131+ content : [ { type : "text" , text : `Unexpected error during token polling: ${ error . message } ` } ] ,
132+ } ;
133+ }
134+ else {
135+ console . error ( "Unexpected non-Error object during token polling:" , error ) ;
136+ return {
137+ content : [ { type : "text" , text : "Unexpected error during token polling." } ] ,
138+ } ;
139+ }
140+ }
141+ }
142+ console . error ( "Authentication timed out. Please restart the process." ) ;
143+ return {
144+ content : [ { type : "text" , text : "Authentication timed out. Please restart the process." } ] ,
145+ } ;
146+ } ) ;
147+ server . tool ( "list-clusters" , "Lists clusters" , async ( { } ) => {
148+ await wait ( 1000 ) ;
37149 if ( ! state . auth ) {
38150 return {
39151 content : [ { type : "text" , text : "Not authenticated" } ] ,
@@ -50,12 +162,10 @@ server.tool("list-clusters", "Lists clusters", (_a) => __awaiter(void 0, [_a], v
50162 return {
51163 content : [ { type : "text" , text } ] ,
52164 } ;
53- } ) ) ;
54- function runServer ( ) {
55- return __awaiter ( this , void 0 , void 0 , function * ( ) {
56- const transport = new stdio_js_1 . StdioServerTransport ( ) ;
57- yield server . connect ( transport ) ;
58- } ) ;
165+ } ) ;
166+ async function runServer ( ) {
167+ const transport = new StdioServerTransport ( ) ;
168+ await server . connect ( transport ) ;
59169}
60170runServer ( ) . catch ( ( error ) => {
61171 console . error ( "Fatal error running server:" , error ) ;
0 commit comments