bluelamp 5.2.0 → 5.3.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.
@@ -6,13 +6,6 @@ import { google } from 'googleapis';
6
6
  import * as fs from 'node:fs';
7
7
  import * as path from 'node:path';
8
8
 
9
- // むすひ認証チェック
10
- const TOKEN_FILE_PATH = path.join(process.env.HOME || '/tmp', '.musuhi', 'portal-token.enc');
11
- if (!fs.existsSync(TOKEN_FILE_PATH)) {
12
- console.error('❌ むすひ認証が必要です。musuhiコマンドでログインしてください。');
13
- process.exit(1);
14
- }
15
-
16
9
  // --- Config ---
17
10
  const CONFIG_DIR = path.join(process.env.HOME || '/tmp', '.config', 'gmail-mcp');
18
11
  const TOKENS_DIR = path.join(CONFIG_DIR, 'tokens');
@@ -20,7 +13,11 @@ const CREDENTIALS_PATH = path.join(CONFIG_DIR, 'credentials.json');
20
13
  const ACCOUNTS_PATH = path.join(CONFIG_DIR, 'accounts.json');
21
14
  // Legacy single-token path (for migration)
22
15
  const LEGACY_TOKEN_PATH = path.join(CONFIG_DIR, 'token.json');
23
- const SCOPES = ['https://www.googleapis.com/auth/gmail.readonly'];
16
+ const SCOPES = [
17
+ 'https://www.googleapis.com/auth/gmail.readonly',
18
+ 'https://www.googleapis.com/auth/gmail.send',
19
+ 'https://www.googleapis.com/auth/gmail.modify'
20
+ ];
24
21
  const DEFAULT_TEXT_BODY_LIMIT = 10000;
25
22
  // --- Account discovery ---
26
23
  function getAccounts() {
@@ -269,6 +266,38 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
269
266
  required: ['account', 'message_id', 'save_path'],
270
267
  },
271
268
  },
269
+ {
270
+ name: 'gmail_create_draft',
271
+ description: 'Create a Gmail draft message',
272
+ inputSchema: {
273
+ type: 'object',
274
+ properties: {
275
+ account: { type: 'string', description: 'Email account to send from' },
276
+ to: { type: 'string', description: 'Recipient email address' },
277
+ subject: { type: 'string', description: 'Email subject' },
278
+ body: { type: 'string', description: 'Email body (plain text)' },
279
+ cc: { type: 'string', description: 'CC email addresses (optional)' },
280
+ bcc: { type: 'string', description: 'BCC email addresses (optional)' },
281
+ },
282
+ required: ['account', 'to', 'subject', 'body'],
283
+ },
284
+ },
285
+ {
286
+ name: 'gmail_send',
287
+ description: 'Send an email via Gmail',
288
+ inputSchema: {
289
+ type: 'object',
290
+ properties: {
291
+ account: { type: 'string', description: 'Email account to send from' },
292
+ to: { type: 'string', description: 'Recipient email address' },
293
+ subject: { type: 'string', description: 'Email subject' },
294
+ body: { type: 'string', description: 'Email body (plain text)' },
295
+ cc: { type: 'string', description: 'CC email addresses (optional)' },
296
+ bcc: { type: 'string', description: 'BCC email addresses (optional)' },
297
+ },
298
+ required: ['account', 'to', 'subject', 'body'],
299
+ },
300
+ },
272
301
  {
273
302
  name: 'batch_search_receipts',
274
303
  description: 'Search for multiple receipts at once. Provide vendor/date/amount items and search all accounts.',
@@ -467,6 +496,60 @@ ${htmlBody}
467
496
  fs.writeFileSync(save_path, fullHtml, 'utf-8');
468
497
  return { content: [{ type: 'text', text: `Saved email HTML (${fullHtml.length} chars) to ${save_path}` }] };
469
498
  }
499
+ case 'gmail_create_draft': {
500
+ const { account, to, subject, body, cc, bcc } = args;
501
+ const auth = getAuthenticatedClient(account);
502
+ if (!auth) {
503
+ return {
504
+ content: [{ type: 'text', text: `Account ${account} not authenticated. Use authorize_account first.` }],
505
+ isError: true,
506
+ };
507
+ }
508
+ const gmail = getGmail(auth);
509
+ const lines = [
510
+ `To: ${to}`,
511
+ ...(cc ? [`Cc: ${cc}`] : []),
512
+ ...(bcc ? [`Bcc: ${bcc}`] : []),
513
+ `Subject: ${subject}`,
514
+ 'Content-Type: text/plain; charset=utf-8',
515
+ '',
516
+ body,
517
+ ];
518
+ const raw = Buffer.from(lines.join('\r\n')).toString('base64')
519
+ .replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
520
+ const draft = await gmail.users.drafts.create({
521
+ userId: 'me',
522
+ requestBody: { message: { raw } },
523
+ });
524
+ return { content: [{ type: 'text', text: `Draft created. ID: ${draft.data.id}` }] };
525
+ }
526
+ case 'gmail_send': {
527
+ const { account, to, subject, body, cc, bcc } = args;
528
+ const auth = getAuthenticatedClient(account);
529
+ if (!auth) {
530
+ return {
531
+ content: [{ type: 'text', text: `Account ${account} not authenticated. Use authorize_account first.` }],
532
+ isError: true,
533
+ };
534
+ }
535
+ const gmail = getGmail(auth);
536
+ const lines = [
537
+ `To: ${to}`,
538
+ ...(cc ? [`Cc: ${cc}`] : []),
539
+ ...(bcc ? [`Bcc: ${bcc}`] : []),
540
+ `Subject: ${subject}`,
541
+ 'Content-Type: text/plain; charset=utf-8',
542
+ '',
543
+ body,
544
+ ];
545
+ const raw = Buffer.from(lines.join('\r\n')).toString('base64')
546
+ .replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
547
+ const sent = await gmail.users.messages.send({
548
+ userId: 'me',
549
+ requestBody: { raw },
550
+ });
551
+ return { content: [{ type: 'text', text: `Email sent. Message ID: ${sent.data.id}` }] };
552
+ }
470
553
  case 'batch_search_receipts': {
471
554
  const { items, date_tolerance_days = 7 } = args;
472
555
  const authenticatedAccounts = getAuthenticatedAccounts();