eassist-mcp 1.0.3 → 1.0.4

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/build/auth.d.ts CHANGED
@@ -6,7 +6,6 @@ declare class AuthManager {
6
6
  private getApiUrl;
7
7
  getValidToken(): Promise<string>;
8
8
  triggerOAuthFlow(): Promise<void>;
9
- private findFreePort;
10
9
  getStoredName(): string;
11
10
  clearTokens(): void;
12
11
  }
package/build/auth.js CHANGED
@@ -1,8 +1,7 @@
1
1
  import fs from 'fs';
2
2
  import os from 'os';
3
3
  import path from 'path';
4
- import http from 'http';
5
- import { URL } from 'url';
4
+ import crypto from 'crypto';
6
5
  import axios from 'axios';
7
6
  const CONFIG_DIR = path.join(os.homedir(), '.eassist-mcp');
8
7
  const CONFIG_FILE = path.join(CONFIG_DIR, 'auth.json');
@@ -24,7 +23,6 @@ class AuthManager {
24
23
  this.data = data;
25
24
  }
26
25
  isExpired(expiresAt) {
27
- // Add 60s buffer so we refresh before actual expiry
28
26
  return new Date(expiresAt).getTime() - 60_000 < Date.now();
29
27
  }
30
28
  getApiUrl() {
@@ -33,11 +31,9 @@ class AuthManager {
33
31
  async getValidToken() {
34
32
  if (!this.data)
35
33
  this.data = this.load();
36
- // Valid access token
37
34
  if (this.data?.accessToken && !this.isExpired(this.data.expiresAt)) {
38
35
  return this.data.accessToken;
39
36
  }
40
- // Try refresh
41
37
  if (this.data?.refreshToken) {
42
38
  try {
43
39
  const apiUrl = this.getApiUrl();
@@ -50,82 +46,37 @@ class AuthManager {
50
46
  // Refresh failed — fall through to OAuth
51
47
  }
52
48
  }
53
- // Full OAuth flow
54
49
  await this.triggerOAuthFlow();
55
50
  return this.data.accessToken;
56
51
  }
57
52
  async triggerOAuthFlow() {
58
53
  const apiUrl = this.getApiUrl();
59
- const port = await this.findFreePort();
60
- return new Promise((resolve, reject) => {
61
- const server = http.createServer((req, res) => {
62
- try {
63
- const url = new URL(req.url ?? '/', `http://localhost:${port}`);
64
- if (url.pathname !== '/callback') {
65
- res.end();
66
- return;
67
- }
68
- const token = url.searchParams.get('token');
69
- const refreshToken = url.searchParams.get('refreshToken');
70
- const expiresAt = url.searchParams.get('expiresAt');
71
- const name = url.searchParams.get('name') ?? undefined;
72
- const email = url.searchParams.get('email') ?? undefined;
73
- if (!token || !refreshToken || !expiresAt) {
74
- res.end('Missing auth params');
75
- reject(new Error('OAuth callback missing params'));
76
- server.close();
77
- return;
78
- }
79
- this.store({ apiUrl, accessToken: token, refreshToken, expiresAt, name, email });
80
- const displayName = name ?? email ?? 'there';
81
- res.writeHead(200, { 'Content-Type': 'text/html' });
82
- res.end(`<!DOCTYPE html><html><head><title>EAssist — Authenticated</title>
83
- <style>body{font-family:sans-serif;display:flex;align-items:center;justify-content:center;min-height:100vh;margin:0;background:#070c1b;color:#e2e8f0;}
84
- .box{text-align:center;padding:40px;border-radius:16px;background:#0c1428;border:1px solid rgba(99,102,241,0.2);}
85
- h2{color:#34d399;margin-bottom:8px;}p{color:#64748b;margin:4px 0;}</style></head>
86
- <body><div class="box"><h2>&#x2713; Authenticated successfully</h2>
87
- <p>Welcome, ${displayName}.</p><p>You can close this tab and return to Claude.</p>
88
- </div></body></html>`);
89
- server.close();
90
- resolve();
91
- }
92
- catch (err) {
93
- server.close();
94
- reject(err);
95
- }
96
- });
97
- server.listen(port, '127.0.0.1', async () => {
98
- const authUrl = `${apiUrl}/api/auth/mcp?port=${port}`;
99
- console.error(`\n[eassist-mcp] Opening browser for authentication...\n${authUrl}\n`);
100
- try {
101
- const { default: open } = await import('open');
102
- await open(authUrl);
103
- }
104
- catch {
105
- console.error('[eassist-mcp] Could not open browser automatically. Please open the URL above manually.');
106
- }
107
- });
108
- server.on('error', reject);
109
- // Timeout after 5 minutes
110
- setTimeout(() => {
111
- server.close();
112
- reject(new Error('Authentication timed out. Please try again.'));
113
- }, 5 * 60 * 1000);
114
- });
115
- }
116
- findFreePort() {
117
- return new Promise((resolve, reject) => {
118
- const server = http.createServer();
119
- server.listen(0, '127.0.0.1', () => {
120
- const addr = server.address();
121
- server.close(() => {
122
- if (addr && typeof addr === 'object')
123
- resolve(addr.port);
124
- else
125
- reject(new Error('Could not find free port'));
126
- });
127
- });
128
- });
54
+ const sessionId = crypto.randomBytes(16).toString('hex');
55
+ const authUrl = `${apiUrl}/api/auth/mcp?session_id=${sessionId}`;
56
+ console.error(`\n[eassist-mcp] Opening browser for authentication...\n${authUrl}\n`);
57
+ try {
58
+ const { default: open } = await import('open');
59
+ await open(authUrl);
60
+ }
61
+ catch {
62
+ console.error('[eassist-mcp] Could not open browser automatically. Please visit the URL above manually.');
63
+ }
64
+ // Poll the backend until it has the token (user completes OAuth in browser)
65
+ const pollUrl = `${apiUrl}/api/auth/mcp/result?session_id=${sessionId}`;
66
+ const deadline = Date.now() + 5 * 60 * 1000;
67
+ while (Date.now() < deadline) {
68
+ await new Promise(r => setTimeout(r, 2000));
69
+ try {
70
+ const res = await axios.get(pollUrl);
71
+ const { token, refreshToken, expiresAt, name, email } = res.data;
72
+ this.store({ apiUrl, accessToken: token, refreshToken, expiresAt, name, email });
73
+ return;
74
+ }
75
+ catch {
76
+ // 404 = not ready yet, any other error = retry
77
+ }
78
+ }
79
+ throw new Error('Authentication timed out. Please try again.');
129
80
  }
130
81
  getStoredName() {
131
82
  return this.data?.name ?? this.data?.email ?? 'Unknown';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eassist-mcp",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "MCP server for EAssist — query and manage actions & initiatives via Claude",
5
5
  "type": "module",
6
6
  "bin": {