Textwell MCP Server
by worldnine
- scripts
- google-token
package main
import (
"context"
"encoding/json"
"flag"
"fmt"
"log"
"net/http"
"os"
"os/exec"
"path/filepath"
"runtime"
"github.com/nguyenvanduocit/all-in-one-model-context-protocol/services"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
"google.golang.org/api/gmail/v1"
"google.golang.org/api/option"
)
func main() {
// Define command line flags
credentialsPath := flag.String("credentials", "", "Path to Google credentials JSON file")
tokenPath := flag.String("token", "", "Path to save/load Google token JSON file")
flag.Parse()
// Validate required flags
if *credentialsPath == "" || *tokenPath == "" {
flag.PrintDefaults()
log.Fatal("Both -credentials and -token flags are required")
}
// try to delete the token file if it exists
if _, err := os.Stat(*tokenPath); err == nil {
os.Remove(*tokenPath)
}
ctx := context.Background()
b, err := os.ReadFile(*credentialsPath)
if err != nil {
log.Fatalf("Unable to read client secret file: %v", err)
}
config, err := google.ConfigFromJSON(b, services.ListGoogleScopes()...)
if err != nil {
log.Fatalf("Unable to parse client secret file to config: %v", err)
}
client := getClient(config, *tokenPath)
srv, err := gmail.NewService(ctx, option.WithHTTPClient(client))
if err != nil {
log.Fatalf("Unable to retrieve Gmail client: %v", err)
}
// Test the connection
user := "me"
_, err = srv.Users.Labels.List(user).Do()
if err != nil {
log.Fatalf("Unable to retrieve labels: %v", err)
}
tokenFileAbsPath, err := filepath.Abs(*tokenPath)
if err != nil {
log.Fatalf("Unable to get absolute path of token.json: %v", err)
}
fmt.Println("It works! Token file is saved at:")
fmt.Println(tokenFileAbsPath)
}
// Update getClient to accept tokenPath parameter
func getClient(config *oauth2.Config, tokenPath string) *http.Client {
tok, err := tokenFromFile(tokenPath)
if err != nil {
tok = getTokenFromWeb(config)
saveToken(tokenPath, tok)
}
return config.Client(context.Background(), tok)
}
// Request a token from the web, then returns the retrieved token.
func getTokenFromWeb(config *oauth2.Config) *oauth2.Token {
// Create a channel to receive the authorization code
codeChan := make(chan string)
// Start a local HTTP server to handle the redirect
http.HandleFunc("/oauth2/callback", func(w http.ResponseWriter, r *http.Request) {
err := r.ParseForm()
if err != nil {
http.Error(w, "Failed to parse form", http.StatusBadRequest)
return
}
code := r.Form.Get("code")
if code == "" {
http.Error(w, "Authorization code not found", http.StatusBadRequest)
return
}
// Send the code to the channel
codeChan <- code
// Inform the user that the process is complete
fmt.Fprintln(w, "<h1>Authentication successful!</h1><p>You can close this window.</p>")
})
// Determine the port for the local server
port := "8080"
redirectURL := fmt.Sprintf("http://localhost:%s/oauth2/callback", port)
// Update the configuration with the redirect URL
config.RedirectURL = redirectURL
authURL := config.AuthCodeURL("state-token", oauth2.AccessTypeOffline)
// Open the URL in the default browser
err := openBrowser(authURL)
if err != nil {
log.Printf("Could not open browser automatically: %v", err)
fmt.Printf("Please open the following URL in your browser:\n\n%v\n\n", authURL)
} else {
fmt.Println("Opening your browser to authenticate...")
}
// Start the HTTP server in a goroutine
server := &http.Server{Addr: ":" + port}
go func() {
if err := server.ListenAndServe(); err != http.ErrServerClosed {
log.Fatalf("Failed to start server: %v", err)
}
}()
// Wait for the authorization code
code := <-codeChan
// Shut down the server
if err := server.Shutdown(context.Background()); err != nil {
log.Printf("Failed to shut down server: %v", err)
}
tok, err := config.Exchange(context.TODO(), code)
if err != nil {
log.Fatalf("Unable to retrieve token from web: %v", err)
}
return tok
}
// Retrieves a token from a local file.
func tokenFromFile(file string) (*oauth2.Token, error) {
f, err := os.Open(file)
if err != nil {
return nil, err
}
defer f.Close()
tok := &oauth2.Token{}
err = json.NewDecoder(f).Decode(tok)
return tok, err
}
// Saves a token to a file path.
func saveToken(path string, token *oauth2.Token) {
fmt.Printf("Saving credential file to: %s\n", path)
f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
log.Fatalf("Unable to cache oauth token: %v", err)
}
defer f.Close()
json.NewEncoder(f).Encode(token)
}
func openBrowser(url string) error {
var err error
switch runtime.GOOS {
case "linux":
err = exec.Command("xdg-open", url).Start()
case "windows":
err = exec.Command("cmd", "/c", "start", url).Start()
case "darwin":
err = exec.Command("open", url).Start()
default:
err = fmt.Errorf("unsupported platform")
}
return err
}