dear-claude 1.0.1 → 1.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.
Files changed (3) hide show
  1. package/README.md +20 -54
  2. package/dist/index.js +14 -276
  3. package/package.json +2 -4
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  <p align="center">
8
8
  <strong>MCP server that triggers local Claude Code instances from external platforms.</strong><br>
9
- Say "Dear Claude" in Linear, GitHub, Jira, GitLab, Notion, Obsidian, or Gmail — and a Claude Code instance spins up to handle it.
9
+ Say "Dear Claude" in Linear, GitHub, Jira, GitLab, Notion, or Obsidian — and a Claude Code instance spins up to handle it.
10
10
  </p>
11
11
 
12
12
  <p align="center">
@@ -36,7 +36,6 @@ Dear Claude is an MCP (Model Context Protocol) server that watches your project
36
36
  | GitLab | Yes | Yes | Yes | Yes | - | Yes |
37
37
  | Notion | Yes | Yes | Yes | - | - | - |
38
38
  | Obsidian | Yes | - | Yes | - | - | - |
39
- | Gmail | Yes | - | Yes | - | - | - |
40
39
 
41
40
  ## Cross-Platform Orchestration
42
41
 
@@ -49,28 +48,30 @@ Instances from **any** platform get API access to **all** configured platforms.
49
48
 
50
49
  ## Quick Start
51
50
 
51
+ ### Install in one line
52
+
53
+ ```bash
54
+ claude mcp add dear-claude -- bunx dear-claude start --mcp
55
+ ```
56
+
57
+ That's it. Start Claude Code and Dear Claude is ready.
58
+
52
59
  ### Prerequisites
53
60
 
54
- - [Bun](https://bun.sh) runtime
55
61
  - [Claude Code](https://claude.ai/claude-code) CLI installed (`claude` command available)
62
+ - [Bun](https://bun.sh) runtime (for `bunx`)
56
63
  - [Tailscale](https://tailscale.com/download) with Funnel enabled (for webhooks from external platforms)
57
64
 
58
- ### 1. Install
65
+ ### Manual setup (alternative)
59
66
 
60
- ```bash
61
- bun install
62
- ```
63
-
64
- ### 2. Add to Claude Code as MCP Server
65
-
66
- Add to `~/.claude.json` under `mcpServers`:
67
+ If you prefer manual configuration, add to `~/.claude.json` under `mcpServers`:
67
68
 
68
69
  ```json
69
70
  {
70
71
  "mcpServers": {
71
72
  "dear-claude": {
72
- "command": "bun",
73
- "args": ["run", "/path/to/dear-claude/src/index.ts", "start", "--mcp"],
73
+ "command": "bunx",
74
+ "args": ["dear-claude", "start", "--mcp"],
74
75
  "env": {
75
76
  "DEAR_CLAUDE_PORT": "3334",
76
77
  "GITHUB_CLIENT_ID": "...",
@@ -85,7 +86,7 @@ Add to `~/.claude.json` under `mcpServers`:
85
86
  }
86
87
  ```
87
88
 
88
- ### 3. Start Claude Code
89
+ Then start Claude Code:
89
90
 
90
91
  ```bash
91
92
  claude
@@ -312,33 +313,6 @@ Claude's response appears as a callout block appended to the same note. The fron
312
313
 
313
314
  ---
314
315
 
315
- ### Gmail
316
-
317
- 1. **Create OAuth credentials** in [Google Cloud Console](https://console.cloud.google.com/apis/credentials):
318
- - Create a project (or use existing)
319
- - Enable the **Gmail API**
320
- - Create **OAuth 2.0 Client ID** (Web application type)
321
- - Add authorized redirect URI: `https://<your-hostname>.ts.net/dc/oauth/callback/gmail`
322
- 2. Set env vars:
323
- ```bash
324
- GOOGLE_CLIENT_ID=123456789-abc.apps.googleusercontent.com
325
- GOOGLE_CLIENT_SECRET=GOCSPX-...
326
- ```
327
- 3. **(Optional) Set up Pub/Sub** for real-time push notifications:
328
- - Create a Pub/Sub topic in Google Cloud Console
329
- - Set: `GOOGLE_PUBSUB_TOPIC=projects/your-project/topics/your-topic`
330
- 4. Complete OAuth: visit `https://<your-hostname>.ts.net/dc/setup/gmail`
331
-
332
- | Environment Variable | Description |
333
- |---------------------|-------------|
334
- | `GOOGLE_CLIENT_ID` | OAuth client ID |
335
- | `GOOGLE_CLIENT_SECRET` | OAuth client secret |
336
- | `GOOGLE_ACCESS_TOKEN` | Access token (skip OAuth) |
337
- | `GOOGLE_REFRESH_TOKEN` | Refresh token |
338
- | `GOOGLE_PUBSUB_TOPIC` | Pub/Sub topic for push notifications |
339
-
340
- ---
341
-
342
316
  ## Usage
343
317
 
344
318
  ### Trigger Format
@@ -350,7 +324,6 @@ Write **"Dear Claude"** (case-insensitive, with a space) anywhere in:
350
324
  - GitLab issue/MR descriptions or comments
351
325
  - Notion page comments
352
326
  - Obsidian `.md` files
353
- - Gmail emails
354
327
 
355
328
  ### Example
356
329
 
@@ -427,13 +400,6 @@ NOTION_ACCESS_TOKEN=
427
400
  OBSIDIAN_VAULT_PATH=
428
401
  OBSIDIAN_WATCH_DEBOUNCE_MS=2000
429
402
 
430
- # Gmail/Google
431
- GOOGLE_CLIENT_ID=
432
- GOOGLE_CLIENT_SECRET=
433
- GOOGLE_ACCESS_TOKEN=
434
- GOOGLE_REFRESH_TOKEN=
435
- GOOGLE_PUBSUB_TOPIC=
436
-
437
403
  # Optional
438
404
  GIPHY_API_KEY= # For fun GIF reactions in responses
439
405
  ```
@@ -505,11 +471,11 @@ The server also exposes REST endpoints on `localhost:3334`:
505
471
 
506
472
  ```
507
473
  Webhooks / File Watcher
508
- ┌────────┐ ┌────────┐ ┌──────┐ ┌────────┐ ┌──────────┐ ┌──────┐
509
- │ GitHub │ │ Linear │ │ Jira │ │ GitLab │ │ Obsidian │ │Gmail
510
- └───┬────┘ └───┬────┘ └──┬───┘ └───┬────┘ └────┬─────┘ └──┬───┘
511
- │ │ │ │ │
512
- └──────────┴────┬────┴─────────┴────────────┴───────────┘
474
+ ┌────────┐ ┌────────┐ ┌──────┐ ┌────────┐ ┌──────────┐ ┌────────┐
475
+ │ GitHub │ │ Linear │ │ Jira │ │ GitLab │ │ Obsidian │ │ Notion
476
+ └───┬────┘ └───┬────┘ └──┬───┘ └───┬────┘ └────┬─────┘ └───┬────┘
477
+ │ │ │ │ │
478
+ └──────────┴────┬────┴─────────┴────────────┴────────────┘
513
479
 
514
480
 
515
481
  ┌─────────────────────┐
package/dist/index.js CHANGED
@@ -1960,18 +1960,6 @@ ${event.data.description || ""}`,
1960
1960
  }
1961
1961
  return null;
1962
1962
  }
1963
- static parseGmailEvent(message, threadId, isFirstInThread) {
1964
- return {
1965
- threadId,
1966
- platform: "gmail",
1967
- content: `${message.subject || ""}
1968
- ${message.body || ""}`,
1969
- isDescription: isFirstInThread,
1970
- messageId: message.id,
1971
- authorId: message.from,
1972
- timestamp: message.timestamp
1973
- };
1974
- }
1975
1963
  static parseGitHubEvent(event) {
1976
1964
  if (event.action === "opened" && event.issue) {
1977
1965
  return {
@@ -15246,230 +15234,6 @@ ${issue.description || ""}`,
15246
15234
  }
15247
15235
  var init_linear_adapter = () => {};
15248
15236
 
15249
- // src/adapters/gmail-adapter.ts
15250
- class GmailAdapter {
15251
- platform = "gmail";
15252
- config;
15253
- apiUrl = "https://gmail.googleapis.com/gmail/v1";
15254
- authUrl = "https://accounts.google.com/o/oauth2/v2/auth";
15255
- tokenUrl = "https://oauth2.googleapis.com/token";
15256
- constructor(config) {
15257
- this.config = config;
15258
- }
15259
- isConfigured() {
15260
- return !!(this.config.accessToken || this.config.clientId && this.config.clientSecret);
15261
- }
15262
- async verifySignature(ctx, body) {
15263
- const authHeader = ctx.req.header("authorization");
15264
- if (!authHeader?.startsWith("Bearer ")) {
15265
- console.warn("[GmailAdapter] Missing or invalid authorization header");
15266
- }
15267
- return true;
15268
- }
15269
- async parseWebhook(ctx, body) {
15270
- const pubsubMessage = body;
15271
- if (!pubsubMessage.message?.data) {
15272
- console.warn("[GmailAdapter] Invalid Pub/Sub message format");
15273
- return null;
15274
- }
15275
- const notificationData = Buffer.from(pubsubMessage.message.data, "base64").toString();
15276
- const notification = JSON.parse(notificationData);
15277
- console.log(`[GmailAdapter] Received notification for ${notification.emailAddress}, historyId: ${notification.historyId}`);
15278
- return {
15279
- platform: "gmail",
15280
- threadId: notification.historyId,
15281
- content: "",
15282
- isDescription: true,
15283
- raw: notification
15284
- };
15285
- }
15286
- async processNotification(historyId, startHistoryId) {
15287
- if (!this.config.accessToken) {
15288
- throw new Error("Gmail access token not configured");
15289
- }
15290
- const events = [];
15291
- const historyUrl = `${this.apiUrl}/users/me/history?startHistoryId=${startHistoryId || historyId}&historyTypes=messageAdded`;
15292
- const historyResponse = await fetch(historyUrl, {
15293
- headers: {
15294
- Authorization: `Bearer ${this.config.accessToken}`
15295
- }
15296
- });
15297
- if (!historyResponse.ok) {
15298
- const error = await historyResponse.text();
15299
- throw new Error(`Failed to fetch history: ${error}`);
15300
- }
15301
- const historyData = await historyResponse.json();
15302
- if (!historyData.history) {
15303
- return events;
15304
- }
15305
- for (const historyItem of historyData.history) {
15306
- if (!historyItem.messagesAdded)
15307
- continue;
15308
- for (const added of historyItem.messagesAdded) {
15309
- const message = await this.getMessage(added.message.id);
15310
- if (!message)
15311
- continue;
15312
- const threadMessages = await this.getThreadMessages(message.threadId);
15313
- const isFirstInThread = threadMessages.length === 1;
15314
- const content = this.extractMessageContent(message);
15315
- const subject = this.getHeader(message, "Subject") || "";
15316
- const from = this.getHeader(message, "From") || "";
15317
- events.push({
15318
- platform: "gmail",
15319
- threadId: message.threadId,
15320
- content: `${subject}
15321
- ${content}`,
15322
- isDescription: isFirstInThread,
15323
- messageId: message.id,
15324
- authorId: from,
15325
- raw: message
15326
- });
15327
- }
15328
- }
15329
- return events;
15330
- }
15331
- async getMessage(messageId) {
15332
- const response = await fetch(`${this.apiUrl}/users/me/messages/${messageId}?format=full`, {
15333
- headers: {
15334
- Authorization: `Bearer ${this.config.accessToken}`
15335
- }
15336
- });
15337
- if (!response.ok) {
15338
- console.error(`[GmailAdapter] Failed to fetch message ${messageId}`);
15339
- return null;
15340
- }
15341
- return response.json();
15342
- }
15343
- async getThreadMessages(threadId) {
15344
- const response = await fetch(`${this.apiUrl}/users/me/threads/${threadId}?format=metadata`, {
15345
- headers: {
15346
- Authorization: `Bearer ${this.config.accessToken}`
15347
- }
15348
- });
15349
- if (!response.ok) {
15350
- return [];
15351
- }
15352
- const thread = await response.json();
15353
- return thread.messages || [];
15354
- }
15355
- getHeader(message, name) {
15356
- return message.payload.headers.find((h3) => h3.name.toLowerCase() === name.toLowerCase())?.value;
15357
- }
15358
- extractMessageContent(message) {
15359
- if (message.payload.body?.data) {
15360
- return Buffer.from(message.payload.body.data, "base64").toString();
15361
- }
15362
- if (message.payload.parts) {
15363
- for (const part of message.payload.parts) {
15364
- if (part.mimeType === "text/plain" && part.body?.data) {
15365
- return Buffer.from(part.body.data, "base64").toString();
15366
- }
15367
- }
15368
- }
15369
- return message.snippet || "";
15370
- }
15371
- async postResponse(threadId, message) {
15372
- if (!this.config.accessToken) {
15373
- throw new Error("Gmail access token not configured");
15374
- }
15375
- const threadMessages = await this.getThreadMessages(threadId);
15376
- if (threadMessages.length === 0) {
15377
- throw new Error(`Thread ${threadId} not found`);
15378
- }
15379
- const originalMessage = threadMessages[threadMessages.length - 1];
15380
- const originalMessageId = this.getHeader(originalMessage, "Message-ID");
15381
- const subject = this.getHeader(originalMessage, "Subject") || "";
15382
- const to = this.getHeader(originalMessage, "From") || "";
15383
- const replySubject = subject.startsWith("Re:") ? subject : `Re: ${subject}`;
15384
- const email = [
15385
- `To: ${to}`,
15386
- `Subject: ${replySubject}`,
15387
- `In-Reply-To: ${originalMessageId}`,
15388
- `References: ${originalMessageId}`,
15389
- "Content-Type: text/plain; charset=utf-8",
15390
- "",
15391
- message
15392
- ].join(`\r
15393
- `);
15394
- const encodedEmail = Buffer.from(email).toString("base64url");
15395
- const response = await fetch(`${this.apiUrl}/users/me/messages/send`, {
15396
- method: "POST",
15397
- headers: {
15398
- Authorization: `Bearer ${this.config.accessToken}`,
15399
- "Content-Type": "application/json"
15400
- },
15401
- body: JSON.stringify({
15402
- raw: encodedEmail,
15403
- threadId
15404
- })
15405
- });
15406
- if (!response.ok) {
15407
- const error = await response.text();
15408
- throw new Error(`Failed to send reply: ${error}`);
15409
- }
15410
- console.log(`[GmailAdapter] Sent reply to thread ${threadId}`);
15411
- }
15412
- getAuthUrl(redirectUri, state) {
15413
- const params = new URLSearchParams({
15414
- client_id: this.config.clientId,
15415
- redirect_uri: redirectUri,
15416
- response_type: "code",
15417
- scope: "https://www.googleapis.com/auth/gmail.readonly https://www.googleapis.com/auth/gmail.send https://www.googleapis.com/auth/gmail.modify",
15418
- state,
15419
- access_type: "offline",
15420
- prompt: "consent"
15421
- });
15422
- return `${this.authUrl}?${params.toString()}`;
15423
- }
15424
- async handleCallback(code, redirectUri) {
15425
- const response = await fetch(this.tokenUrl, {
15426
- method: "POST",
15427
- headers: {
15428
- "Content-Type": "application/x-www-form-urlencoded"
15429
- },
15430
- body: new URLSearchParams({
15431
- grant_type: "authorization_code",
15432
- client_id: this.config.clientId,
15433
- client_secret: this.config.clientSecret,
15434
- redirect_uri: redirectUri,
15435
- code
15436
- })
15437
- });
15438
- if (!response.ok) {
15439
- const error = await response.text();
15440
- throw new Error(`Failed to exchange code: ${error}`);
15441
- }
15442
- const data = await response.json();
15443
- return {
15444
- accessToken: data.access_token,
15445
- refreshToken: data.refresh_token
15446
- };
15447
- }
15448
- async setupPushNotifications(topicName) {
15449
- if (!this.config.accessToken) {
15450
- throw new Error("Gmail access token not configured");
15451
- }
15452
- const response = await fetch(`${this.apiUrl}/users/me/watch`, {
15453
- method: "POST",
15454
- headers: {
15455
- Authorization: `Bearer ${this.config.accessToken}`,
15456
- "Content-Type": "application/json"
15457
- },
15458
- body: JSON.stringify({
15459
- topicName,
15460
- labelIds: ["INBOX"]
15461
- })
15462
- });
15463
- if (!response.ok) {
15464
- const error = await response.text();
15465
- throw new Error(`Failed to setup watch: ${error}`);
15466
- }
15467
- const data = await response.json();
15468
- console.log(`[GmailAdapter] Watch setup, historyId: ${data.historyId}, expires: ${data.expiration}`);
15469
- return data;
15470
- }
15471
- }
15472
-
15473
15237
  // src/adapters/github-adapter.ts
15474
15238
  import { createHmac as createHmac2, createSign, timingSafeEqual as timingSafeEqual2 } from "crypto";
15475
15239
  import { readFileSync as readFileSync3, appendFileSync as appendFileSync2 } from "fs";
@@ -16930,9 +16694,6 @@ function createServer(config, db, instanceManager, executor, obsidianWatcher) {
16930
16694
  if (config.linear) {
16931
16695
  adapters.set("linear", new LinearAdapter(config.linear));
16932
16696
  }
16933
- if (config.gmail) {
16934
- adapters.set("gmail", new GmailAdapter(config.gmail));
16935
- }
16936
16697
  if (config.github) {
16937
16698
  adapters.set("github", new GitHubAdapter(config.github));
16938
16699
  }
@@ -16958,7 +16719,6 @@ function createServer(config, db, instanceManager, executor, obsidianWatcher) {
16958
16719
  webhooks: publicUrl ? {
16959
16720
  github: `${publicUrl}/webhook/github`,
16960
16721
  linear: `${publicUrl}/webhook/linear`,
16961
- gmail: `${publicUrl}/webhook/gmail`,
16962
16722
  gitlab: `${publicUrl}/webhook/gitlab`,
16963
16723
  jira: `${publicUrl}/webhook/jira`,
16964
16724
  notion: `${publicUrl}/webhook/notion`
@@ -16966,12 +16726,10 @@ function createServer(config, db, instanceManager, executor, obsidianWatcher) {
16966
16726
  oauth: publicUrl ? {
16967
16727
  github: `${publicUrl}/setup/github`,
16968
16728
  linear: `${publicUrl}/setup/linear`,
16969
- gmail: `${publicUrl}/setup/gmail`,
16970
16729
  notion: `${publicUrl}/setup/notion`
16971
16730
  } : null,
16972
16731
  platforms: {
16973
16732
  linear: adapters.has("linear"),
16974
- gmail: adapters.has("gmail"),
16975
16733
  github: adapters.has("github"),
16976
16734
  gitlab: adapters.has("gitlab"),
16977
16735
  jira: adapters.has("jira"),
@@ -16980,8 +16738,7 @@ function createServer(config, db, instanceManager, executor, obsidianWatcher) {
16980
16738
  },
16981
16739
  authenticatedUsers: {
16982
16740
  github: db.getPlatformUsername("github") || null,
16983
- linear: db.getPlatformUsername("linear") || null,
16984
- google: db.getPlatformUsername("google") || null
16741
+ linear: db.getPlatformUsername("linear") || null
16985
16742
  }
16986
16743
  });
16987
16744
  });
@@ -17200,10 +16957,9 @@ ${safeError}`, installationId);
17200
16957
  try {
17201
16958
  const redirectUri = `${config.publicUrl}/oauth/callback/${platform}`;
17202
16959
  const tokens = await adapter.handleCallback(code, redirectUri);
17203
- const oauthProvider = platform === "gmail" ? "google" : platform;
17204
16960
  db.saveOAuthToken({
17205
16961
  id: crypto.randomUUID(),
17206
- provider: oauthProvider,
16962
+ provider: platform,
17207
16963
  user_id: "default",
17208
16964
  access_token: tokens.accessToken,
17209
16965
  refresh_token: tokens.refreshToken,
@@ -24082,7 +23838,7 @@ var SCHEMA = `
24082
23838
  CREATE TABLE IF NOT EXISTS instances (
24083
23839
  id TEXT PRIMARY KEY,
24084
23840
  thread_id TEXT NOT NULL,
24085
- platform TEXT NOT NULL CHECK (platform IN ('linear', 'gmail', 'github', 'gitlab', 'jira', 'notion', 'obsidian')),
23841
+ platform TEXT NOT NULL CHECK (platform IN ('linear', 'github', 'gitlab', 'jira', 'notion', 'obsidian')),
24086
23842
  status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'running', 'completed', 'failed', 'idle', 'expired')),
24087
23843
  working_dir TEXT NOT NULL,
24088
23844
  original_prompt TEXT NOT NULL,
@@ -24112,7 +23868,7 @@ CREATE INDEX IF NOT EXISTS idx_messages_instance ON messages (instance_id);
24112
23868
  -- OAuth tokens table
24113
23869
  CREATE TABLE IF NOT EXISTS oauth_tokens (
24114
23870
  id TEXT PRIMARY KEY,
24115
- provider TEXT NOT NULL CHECK (provider IN ('linear', 'google', 'github', 'gitlab', 'jira', 'notion')),
23871
+ provider TEXT NOT NULL CHECK (provider IN ('linear', 'github', 'gitlab', 'jira', 'notion')),
24116
23872
  user_id TEXT NOT NULL,
24117
23873
  access_token TEXT NOT NULL,
24118
23874
  refresh_token TEXT,
@@ -24128,7 +23884,7 @@ CREATE UNIQUE INDEX IF NOT EXISTS idx_oauth_provider_user ON oauth_tokens (provi
24128
23884
  -- Webhook configurations table
24129
23885
  CREATE TABLE IF NOT EXISTS webhook_configs (
24130
23886
  id TEXT PRIMARY KEY,
24131
- platform TEXT NOT NULL UNIQUE CHECK (platform IN ('linear', 'gmail', 'github', 'gitlab', 'jira', 'notion', 'obsidian')),
23887
+ platform TEXT NOT NULL UNIQUE CHECK (platform IN ('linear', 'github', 'gitlab', 'jira', 'notion', 'obsidian')),
24132
23888
  webhook_id TEXT,
24133
23889
  webhook_secret TEXT,
24134
23890
  subscription_id TEXT,
@@ -24203,7 +23959,7 @@ class DatabaseManager {
24203
23959
  this.db.exec("ALTER TABLE instances RENAME TO instances_old");
24204
23960
  this.db.exec(`CREATE TABLE instances (
24205
23961
  id TEXT PRIMARY KEY, thread_id TEXT NOT NULL,
24206
- platform TEXT NOT NULL CHECK (platform IN ('linear', 'gmail', 'github', 'gitlab', 'jira', 'notion', 'obsidian')),
23962
+ platform TEXT NOT NULL CHECK (platform IN ('linear', 'github', 'gitlab', 'jira', 'notion', 'obsidian')),
24207
23963
  status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'running', 'completed', 'failed', 'idle', 'expired')),
24208
23964
  working_dir TEXT NOT NULL, original_prompt TEXT NOT NULL, completion_summary TEXT,
24209
23965
  created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL, expires_at INTEGER NOT NULL
@@ -24225,7 +23981,7 @@ class DatabaseManager {
24225
23981
  this.db.exec("CREATE INDEX IF NOT EXISTS idx_messages_instance ON messages (instance_id)");
24226
23982
  this.db.exec("ALTER TABLE oauth_tokens RENAME TO oauth_tokens_old");
24227
23983
  this.db.exec(`CREATE TABLE oauth_tokens (
24228
- id TEXT PRIMARY KEY, provider TEXT NOT NULL CHECK (provider IN ('linear', 'google', 'github', 'gitlab', 'jira', 'notion')),
23984
+ id TEXT PRIMARY KEY, provider TEXT NOT NULL CHECK (provider IN ('linear', 'github', 'gitlab', 'jira', 'notion')),
24229
23985
  user_id TEXT NOT NULL, access_token TEXT NOT NULL, refresh_token TEXT, expires_at INTEGER,
24230
23986
  scope TEXT NOT NULL, platform_username TEXT, created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL
24231
23987
  )`);
@@ -24234,7 +23990,7 @@ class DatabaseManager {
24234
23990
  this.db.exec("CREATE UNIQUE INDEX IF NOT EXISTS idx_oauth_provider_user ON oauth_tokens (provider, user_id)");
24235
23991
  this.db.exec("ALTER TABLE webhook_configs RENAME TO webhook_configs_old");
24236
23992
  this.db.exec(`CREATE TABLE webhook_configs (
24237
- id TEXT PRIMARY KEY, platform TEXT NOT NULL UNIQUE CHECK (platform IN ('linear', 'gmail', 'github', 'gitlab', 'jira', 'notion', 'obsidian')),
23993
+ id TEXT PRIMARY KEY, platform TEXT NOT NULL UNIQUE CHECK (platform IN ('linear', 'github', 'gitlab', 'jira', 'notion', 'obsidian')),
24238
23994
  webhook_id TEXT, webhook_secret TEXT, subscription_id TEXT, created_at INTEGER NOT NULL
24239
23995
  )`);
24240
23996
  this.db.exec("INSERT INTO webhook_configs SELECT * FROM webhook_configs_old");
@@ -24256,7 +24012,7 @@ class DatabaseManager {
24256
24012
  this.db.exec("ALTER TABLE instances RENAME TO instances_old");
24257
24013
  this.db.exec(`CREATE TABLE instances (
24258
24014
  id TEXT PRIMARY KEY, thread_id TEXT NOT NULL,
24259
- platform TEXT NOT NULL CHECK (platform IN ('linear', 'gmail', 'github', 'gitlab', 'jira', 'notion', 'obsidian')),
24015
+ platform TEXT NOT NULL CHECK (platform IN ('linear', 'github', 'gitlab', 'jira', 'notion', 'obsidian')),
24260
24016
  status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'running', 'completed', 'failed', 'idle', 'expired')),
24261
24017
  working_dir TEXT NOT NULL, original_prompt TEXT NOT NULL, completion_summary TEXT,
24262
24018
  created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL, expires_at INTEGER NOT NULL
@@ -24278,7 +24034,7 @@ class DatabaseManager {
24278
24034
  this.db.exec("CREATE INDEX IF NOT EXISTS idx_messages_instance ON messages (instance_id)");
24279
24035
  this.db.exec("ALTER TABLE oauth_tokens RENAME TO oauth_tokens_old");
24280
24036
  this.db.exec(`CREATE TABLE oauth_tokens (
24281
- id TEXT PRIMARY KEY, provider TEXT NOT NULL CHECK (provider IN ('linear', 'google', 'github', 'gitlab', 'jira', 'notion')),
24037
+ id TEXT PRIMARY KEY, provider TEXT NOT NULL CHECK (provider IN ('linear', 'github', 'gitlab', 'jira', 'notion')),
24282
24038
  user_id TEXT NOT NULL, access_token TEXT NOT NULL, refresh_token TEXT, expires_at INTEGER,
24283
24039
  scope TEXT NOT NULL, platform_username TEXT, created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL
24284
24040
  )`);
@@ -24287,7 +24043,7 @@ class DatabaseManager {
24287
24043
  this.db.exec("CREATE UNIQUE INDEX IF NOT EXISTS idx_oauth_provider_user ON oauth_tokens (provider, user_id)");
24288
24044
  this.db.exec("ALTER TABLE webhook_configs RENAME TO webhook_configs_old");
24289
24045
  this.db.exec(`CREATE TABLE webhook_configs (
24290
- id TEXT PRIMARY KEY, platform TEXT NOT NULL UNIQUE CHECK (platform IN ('linear', 'gmail', 'github', 'gitlab', 'jira', 'notion', 'obsidian')),
24046
+ id TEXT PRIMARY KEY, platform TEXT NOT NULL UNIQUE CHECK (platform IN ('linear', 'github', 'gitlab', 'jira', 'notion', 'obsidian')),
24291
24047
  webhook_id TEXT, webhook_secret TEXT, subscription_id TEXT, created_at INTEGER NOT NULL
24292
24048
  )`);
24293
24049
  this.db.exec("INSERT INTO webhook_configs SELECT * FROM webhook_configs_old");
@@ -31894,7 +31650,7 @@ function createMCPServer(instanceManager, executor, config2, options) {
31894
31650
  platform: {
31895
31651
  type: "string",
31896
31652
  description: "Target platform (default: github)",
31897
- enum: ["linear", "gmail", "github", "gitlab", "jira", "notion", "obsidian"]
31653
+ enum: ["linear", "github", "gitlab", "jira", "notion", "obsidian"]
31898
31654
  },
31899
31655
  repo_url: {
31900
31656
  type: "string",
@@ -31948,7 +31704,6 @@ function createMCPServer(instanceManager, executor, config2, options) {
31948
31704
  case "list_platforms": {
31949
31705
  const platforms = {
31950
31706
  linear: !!(config2?.linear?.clientId || config2?.linear?.accessToken),
31951
- gmail: !!config2?.gmail?.clientId,
31952
31707
  github: !!(config2?.github?.clientId || config2?.github?.accessToken),
31953
31708
  gitlab: !!config2?.gitlab?.accessToken,
31954
31709
  jira: !!(config2?.jira?.domain && config2?.jira?.apiToken),
@@ -32267,14 +32022,12 @@ async function startMCPServer(instanceManager, executor, options) {
32267
32022
  console.error(`[MCP] Webhook URLs (public):`);
32268
32023
  console.error(` GitHub: ${publicUrl}/webhook/github`);
32269
32024
  console.error(` Linear: ${publicUrl}/webhook/linear`);
32270
- console.error(` Gmail: ${publicUrl}/webhook/gmail`);
32271
32025
  console.error(`[MCP] OAuth Setup:`);
32272
32026
  console.error(` GitHub: ${publicUrl}/setup/github`);
32273
32027
  } else {
32274
32028
  console.error(`[MCP] Webhook URLs (local only):`);
32275
32029
  console.error(` GitHub: http://localhost:${httpPort}/webhook/github`);
32276
32030
  console.error(` Linear: http://localhost:${httpPort}/webhook/linear`);
32277
- console.error(` Gmail: http://localhost:${httpPort}/webhook/gmail`);
32278
32031
  }
32279
32032
  }
32280
32033
  if (options?.config?.obsidian?.vaultPath && options.db) {
@@ -32373,13 +32126,6 @@ function getConfig() {
32373
32126
  webhookSecret: process.env.LINEAR_WEBHOOK_SECRET,
32374
32127
  accessToken: process.env.LINEAR_ACCESS_TOKEN
32375
32128
  },
32376
- gmail: {
32377
- clientId: process.env.GOOGLE_CLIENT_ID,
32378
- clientSecret: process.env.GOOGLE_CLIENT_SECRET,
32379
- accessToken: process.env.GOOGLE_ACCESS_TOKEN,
32380
- refreshToken: process.env.GOOGLE_REFRESH_TOKEN,
32381
- pubsubTopic: process.env.GOOGLE_PUBSUB_TOPIC
32382
- },
32383
32129
  github: {
32384
32130
  clientId: process.env.GITHUB_CLIENT_ID,
32385
32131
  clientSecret: process.env.GITHUB_CLIENT_SECRET,
@@ -32527,7 +32273,6 @@ ${safeError}`);
32527
32273
  console.log(`
32528
32274
  \uD83D\uDCCD Webhook URLs:`);
32529
32275
  console.log(` Linear: ${config2.publicUrl}/webhook/linear`);
32530
- console.log(` Gmail: ${config2.publicUrl}/webhook/gmail`);
32531
32276
  console.log(` GitHub: ${config2.publicUrl}/webhook/github`);
32532
32277
  console.log(` GitLab: ${config2.publicUrl}/webhook/gitlab`);
32533
32278
  console.log(` Jira: ${config2.publicUrl}/webhook/jira`);
@@ -32535,7 +32280,6 @@ ${safeError}`);
32535
32280
  console.log(`
32536
32281
  \uD83D\uDD10 OAuth Setup:`);
32537
32282
  console.log(` Linear: ${config2.publicUrl}/setup/linear`);
32538
- console.log(` Gmail: ${config2.publicUrl}/setup/gmail`);
32539
32283
  console.log(` GitHub: ${config2.publicUrl}/setup/github`);
32540
32284
  console.log(` Notion: ${config2.publicUrl}/setup/notion`);
32541
32285
  }
@@ -32567,7 +32311,6 @@ Shutting down...`);
32567
32311
  `);
32568
32312
  console.log("Platforms:");
32569
32313
  console.log(` Linear: ${config2.linear?.accessToken ? "\u2705 Connected" : config2.linear?.clientId ? "\u26A0\uFE0F OAuth configured" : "\u274C Not configured"}`);
32570
- console.log(` Gmail: ${config2.gmail?.accessToken ? "\u2705 Connected" : config2.gmail?.clientId ? "\u26A0\uFE0F OAuth configured" : "\u274C Not configured"}`);
32571
32314
  console.log(` GitHub: ${config2.github?.accessToken ? "\u2705 Connected" : config2.github?.clientId ? "\u26A0\uFE0F OAuth configured" : "\u274C Not configured"}`);
32572
32315
  console.log(` GitLab: ${config2.gitlab?.accessToken ? "\u2705 Connected" : "\u274C Not configured"}`);
32573
32316
  console.log(` Jira: ${config2.jira?.apiToken ? "\u2705 Connected" : config2.jira?.domain ? "\u26A0\uFE0F Domain configured" : "\u274C Not configured"}`);
@@ -32648,8 +32391,8 @@ Database: \u274C Error - ${err}`);
32648
32391
  }
32649
32392
  db.close();
32650
32393
  });
32651
- program2.command("setup <platform>").description("Configure a platform (linear, gmail, github, jira, notion, obsidian)").action(async (platform) => {
32652
- const validPlatforms = ["linear", "gmail", "github", "jira", "notion", "obsidian"];
32394
+ program2.command("setup <platform>").description("Configure a platform (linear, github, jira, notion, obsidian)").action(async (platform) => {
32395
+ const validPlatforms = ["linear", "github", "jira", "notion", "obsidian"];
32653
32396
  if (!validPlatforms.includes(platform)) {
32654
32397
  console.error(`Invalid platform: ${platform}`);
32655
32398
  console.log(`Valid platforms: ${validPlatforms.join(", ")}`);
@@ -32722,11 +32465,6 @@ Once configured, run 'dear-claude start' and Obsidian watcher will be active.`);
32722
32465
  console.log(" LINEAR_CLIENT_SECRET");
32723
32466
  console.log(`
32724
32467
  (Get these from Linear Settings \u2192 API \u2192 OAuth Applications)`);
32725
- } else if (platform === "gmail") {
32726
- console.log(" GOOGLE_CLIENT_ID");
32727
- console.log(" GOOGLE_CLIENT_SECRET");
32728
- console.log(`
32729
- (Get these from Google Cloud Console \u2192 APIs & Services \u2192 Credentials)`);
32730
32468
  } else if (platform === "github") {
32731
32469
  console.log(" GITHUB_CLIENT_ID");
32732
32470
  console.log(" GITHUB_CLIENT_SECRET");
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "dear-claude",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "mcpName": "io.github.sns45/dear-claude",
5
- "description": "MCP server that triggers local Claude Code instances from external platforms (GitHub, Linear, Jira, GitLab, Notion, Obsidian, Gmail) when 'Dear Claude' is mentioned",
5
+ "description": "MCP server that triggers local Claude Code instances from external platforms (GitHub, Linear, Jira, GitLab, Notion, Obsidian) when 'Dear Claude' is mentioned",
6
6
  "type": "module",
7
7
  "main": "./dist/index.js",
8
8
  "bin": {
@@ -22,7 +22,6 @@
22
22
  },
23
23
  "dependencies": {
24
24
  "@anthropic-ai/claude-agent-sdk": "^0.2.25",
25
- "@googleapis/gmail": "^2.0.0",
26
25
  "@linear/sdk": "^29.0.0",
27
26
  "@modelcontextprotocol/sdk": "^1.0.0",
28
27
  "@octokit/rest": "^20.0.0",
@@ -37,7 +36,6 @@
37
36
  "claude-code",
38
37
  "mcp",
39
38
  "linear",
40
- "gmail",
41
39
  "github",
42
40
  "gitlab",
43
41
  "jira",