purmemo-mcp 2.0.6 → 2.1.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "purmemo-mcp",
3
- "version": "2.0.6",
3
+ "version": "2.1.0",
4
4
  "description": "Official Model Context Protocol (MCP) server for Purmemo - Seamless OAuth authentication for your AI-powered second brain",
5
5
  "main": "src/server-oauth.js",
6
6
  "type": "module",
@@ -6,13 +6,10 @@
6
6
  import crypto from 'crypto';
7
7
  import express from 'express';
8
8
  import open from 'open';
9
- import { spawn, exec } from 'child_process';
10
- import { promisify } from 'util';
9
+ import { exec } from 'child_process';
11
10
  import os from 'os';
12
11
  import TokenStore from './token-store.js';
13
12
 
14
- const execAsync = promisify(exec);
15
-
16
13
  class OAuthManager {
17
14
  constructor(config = {}) {
18
15
  this.apiUrl = config.apiUrl || process.env.PUO_MEMO_API_URL || 'https://api.purmemo.ai';
@@ -26,140 +23,8 @@ class OAuthManager {
26
23
  this.platform = os.platform();
27
24
  }
28
25
 
29
- /**
30
- * Robust browser opening with multiple fallback strategies
31
- * Handles macOS security restrictions and provides user-friendly alternatives
32
- */
33
- async openBrowserRobustly(url) {
34
- console.log(`🌐 Opening OAuth URL...`);
35
-
36
- const strategies = [
37
- () => this.tryOpenPackage(url),
38
- () => this.tryDirectCommand(url),
39
- () => this.tryAlternativeCommands(url),
40
- () => this.tryAppleScript(url),
41
- () => this.provideFallbackInstructions(url)
42
- ];
43
-
44
- for (let i = 0; i < strategies.length; i++) {
45
- try {
46
- const result = await strategies[i]();
47
-
48
- if (result.success) {
49
- if (result.requiresManualAction) {
50
- // User needs to manually open the URL
51
- return { opened: false, manualRequired: true, url };
52
- }
53
- console.log(`āœ… Browser opened successfully`);
54
- return { opened: true, manualRequired: false };
55
- }
56
- } catch (error) {
57
- console.log(`Strategy ${i + 1} failed, trying next...`);
58
- }
59
- }
60
-
61
- return { opened: false, manualRequired: true, url };
62
- }
63
-
64
- async tryOpenPackage(url) {
65
- try {
66
- await open(url, { wait: false });
67
- await new Promise(resolve => setTimeout(resolve, 1000));
68
- return { success: true };
69
- } catch (error) {
70
- throw new Error(`open package failed: ${error.message}`);
71
- }
72
- }
73
-
74
- async tryDirectCommand(url) {
75
- try {
76
- let command;
77
-
78
- switch (this.platform) {
79
- case 'darwin':
80
- command = `open "${url}"`;
81
- break;
82
- case 'win32':
83
- command = `start "" "${url}"`;
84
- break;
85
- default:
86
- command = `xdg-open "${url}"`;
87
- }
88
-
89
- await execAsync(command);
90
- await new Promise(resolve => setTimeout(resolve, 1000));
91
- return { success: true };
92
- } catch (error) {
93
- throw new Error(`direct command failed: ${error.message}`);
94
- }
95
- }
96
-
97
- async tryAlternativeCommands(url) {
98
- if (this.platform !== 'darwin') {
99
- throw new Error('Not macOS');
100
- }
101
-
102
- const commands = [
103
- `open -a Safari "${url}"`,
104
- `open -a "Google Chrome" "${url}"`,
105
- `open -a Firefox "${url}"`,
106
- `/usr/bin/open "${url}"`
107
- ];
108
-
109
- for (const command of commands) {
110
- try {
111
- await execAsync(command);
112
- await new Promise(resolve => setTimeout(resolve, 500));
113
- return { success: true };
114
- } catch (error) {
115
- continue;
116
- }
117
- }
118
-
119
- throw new Error('All alternative commands failed');
120
- }
121
-
122
- async tryAppleScript(url) {
123
- if (this.platform !== 'darwin') {
124
- throw new Error('Not macOS');
125
- }
126
-
127
- try {
128
- const script = `tell application "Safari" to open location "${url}"`;
129
- await execAsync(`osascript -e '${script}'`);
130
- await new Promise(resolve => setTimeout(resolve, 1000));
131
- return { success: true };
132
- } catch (error) {
133
- throw new Error(`AppleScript failed: ${error.message}`);
134
- }
135
- }
136
-
137
- async provideFallbackInstructions(url) {
138
- console.log('\n' + '━'.repeat(60));
139
- console.log('🚨 BROWSER OPENING BLOCKED BY SECURITY');
140
- console.log('━'.repeat(60));
141
- console.log('');
142
- console.log('Please manually copy and paste this URL into your browser:');
143
- console.log('');
144
- console.log('šŸ“‹ COPY THIS URL:');
145
- console.log('⬇'.repeat(40));
146
- console.log(url);
147
- console.log('⬆'.repeat(40));
148
- console.log('');
149
- console.log('šŸ“± QUICK STEPS:');
150
- console.log('1. Copy the URL above');
151
- console.log('2. Open your browser');
152
- console.log('3. Paste and press Enter');
153
- console.log('4. Sign in with Google/GitHub');
154
- console.log('');
155
- console.log('ā° Waiting for OAuth callback...');
156
- console.log('━'.repeat(60));
157
-
158
- return {
159
- success: true,
160
- requiresManualAction: true
161
- };
162
- }
26
+ // Removed complex browser opening strategies - going manual-first
27
+ // The performOAuthFlow method now handles everything clearly
163
28
 
164
29
  /**
165
30
  * Get current authentication token
@@ -229,8 +94,6 @@ class OAuthManager {
229
94
  * Perform the actual OAuth flow
230
95
  */
231
96
  async performOAuthFlow() {
232
- console.log('\nšŸ” Starting Purmemo authentication...\n');
233
-
234
97
  // Generate PKCE challenge
235
98
  const codeVerifier = this.generateCodeVerifier();
236
99
  const codeChallenge = this.generateCodeChallenge(codeVerifier);
@@ -246,15 +109,50 @@ class OAuthManager {
246
109
  authUrl.searchParams.append('code_challenge', codeChallenge);
247
110
  authUrl.searchParams.append('code_challenge_method', 'S256');
248
111
 
249
- // Start local server for callback
112
+ // Start local server for callback FIRST
250
113
  const authCode = await this.startCallbackServer(state);
251
114
 
252
- // Open browser for authentication with robust fallback
253
- const browserResult = await this.openBrowserRobustly(authUrl.toString());
115
+ // MANUAL-FIRST: Always show the URL clearly
116
+ console.log('\n' + '═'.repeat(70));
117
+ console.log('šŸ” AUTHENTICATION REQUIRED');
118
+ console.log('═'.repeat(70));
119
+ console.log('');
120
+ console.log('Please complete authentication in your browser:');
121
+ console.log('');
122
+ console.log('šŸ“‹ COPY THIS URL:');
123
+ console.log('─'.repeat(70));
124
+ console.log(authUrl.toString());
125
+ console.log('─'.repeat(70));
126
+ console.log('');
127
+ console.log('šŸ“± STEPS:');
128
+ console.log(' 1. Copy the URL above');
129
+ console.log(' 2. Open your browser');
130
+ console.log(' 3. Paste and press Enter');
131
+ console.log(' 4. Sign in with Google, GitHub, or Email');
132
+ console.log(' 5. You\'ll be redirected back automatically');
133
+ console.log('');
134
+ console.log('ā³ Waiting for you to sign in...');
135
+ console.log('═'.repeat(70));
136
+ console.log('');
254
137
 
255
- if (!browserResult.opened && browserResult.manualRequired) {
256
- // The robust method already displayed instructions
257
- console.log(''); // Just add some spacing
138
+ // Try to open browser quietly in background (might work, might not)
139
+ try {
140
+ if (this.platform === 'darwin') {
141
+ // On macOS, try using 'open' directly without the npm context
142
+ exec(`open "${authUrl.toString()}"`, (error) => {
143
+ if (!error) {
144
+ console.log('✨ Browser opened automatically - check your browser tabs');
145
+ }
146
+ // If it fails, that's fine - user has manual instructions
147
+ });
148
+ } else {
149
+ // Try other platforms
150
+ await open(authUrl.toString(), { wait: false }).catch(() => {
151
+ // Silently fail - manual instructions are already shown
152
+ });
153
+ }
154
+ } catch {
155
+ // Silent fail - manual instructions are primary
258
156
  }
259
157
 
260
158
  // Wait for callback with auth code
package/src/setup.js CHANGED
@@ -71,18 +71,9 @@ program
71
71
  }
72
72
 
73
73
  console.log(chalk.yellow('\nšŸ“± Starting authentication process...\n'));
74
- console.log(chalk.gray('You will be redirected to your browser to sign in.'));
75
- console.log(chalk.gray('Choose your preferred method: Google, GitHub, or Email.\n'));
76
-
77
- const spinner = ora('Preparing authentication...').start();
74
+ console.log(chalk.gray('Follow the instructions below to sign in.\n'));
78
75
 
79
76
  try {
80
- await new Promise(resolve => setTimeout(resolve, 1000)); // Brief pause
81
- spinner.stop();
82
-
83
- // Ensure manual instructions are visible if browser fails
84
- console.log(chalk.cyan('\nšŸ” Starting Purmemo authentication...\n'));
85
-
86
77
  const token = await authManager.authenticate();
87
78
 
88
79
  if (token) {