@shin1ohno/sage 0.8.4 → 0.8.7
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/dist/cli/mcp-handler.d.ts.map +1 -1
- package/dist/cli/mcp-handler.js +202 -1575
- package/dist/cli/mcp-handler.js.map +1 -1
- package/dist/config/update-validation.d.ts +52 -0
- package/dist/config/update-validation.d.ts.map +1 -0
- package/dist/config/update-validation.js +133 -0
- package/dist/config/update-validation.js.map +1 -0
- package/dist/index.js +163 -1815
- package/dist/index.js.map +1 -1
- package/dist/integrations/calendar-event-creator.d.ts +2 -3
- package/dist/integrations/calendar-event-creator.d.ts.map +1 -1
- package/dist/integrations/calendar-event-creator.js +3 -4
- package/dist/integrations/calendar-event-creator.js.map +1 -1
- package/dist/integrations/calendar-event-deleter.d.ts +2 -3
- package/dist/integrations/calendar-event-deleter.d.ts.map +1 -1
- package/dist/integrations/calendar-event-deleter.js +3 -4
- package/dist/integrations/calendar-event-deleter.js.map +1 -1
- package/dist/integrations/calendar-event-response.d.ts +4 -17
- package/dist/integrations/calendar-event-response.d.ts.map +1 -1
- package/dist/integrations/calendar-event-response.js +3 -4
- package/dist/integrations/calendar-event-response.js.map +1 -1
- package/dist/integrations/notion-mcp.d.ts +28 -3
- package/dist/integrations/notion-mcp.d.ts.map +1 -1
- package/dist/integrations/notion-mcp.js +21 -5
- package/dist/integrations/notion-mcp.js.map +1 -1
- package/dist/integrations/reminder-manager.d.ts.map +1 -1
- package/dist/integrations/reminder-manager.js +2 -0
- package/dist/integrations/reminder-manager.js.map +1 -1
- package/dist/services/container.d.ts +56 -0
- package/dist/services/container.d.ts.map +1 -0
- package/dist/services/container.js +76 -0
- package/dist/services/container.js.map +1 -0
- package/dist/tools/calendar/handlers.d.ts +186 -0
- package/dist/tools/calendar/handlers.d.ts.map +1 -0
- package/dist/tools/calendar/handlers.js +525 -0
- package/dist/tools/calendar/handlers.js.map +1 -0
- package/dist/tools/calendar/index.d.ts +11 -0
- package/dist/tools/calendar/index.d.ts.map +1 -0
- package/dist/tools/calendar/index.js +10 -0
- package/dist/tools/calendar/index.js.map +1 -0
- package/dist/tools/index.d.ts +23 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +24 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/integrations/handlers.d.ts +57 -0
- package/dist/tools/integrations/handlers.d.ts.map +1 -0
- package/dist/tools/integrations/handlers.js +159 -0
- package/dist/tools/integrations/handlers.js.map +1 -0
- package/dist/tools/integrations/index.d.ts +11 -0
- package/dist/tools/integrations/index.d.ts.map +1 -0
- package/dist/tools/integrations/index.js +10 -0
- package/dist/tools/integrations/index.js.map +1 -0
- package/dist/tools/registry.d.ts +8 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +10 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/reminders/handlers.d.ts +61 -0
- package/dist/tools/reminders/handlers.d.ts.map +1 -0
- package/dist/tools/reminders/handlers.js +148 -0
- package/dist/tools/reminders/handlers.js.map +1 -0
- package/dist/tools/reminders/index.d.ts +11 -0
- package/dist/tools/reminders/index.d.ts.map +1 -0
- package/dist/tools/reminders/index.js +10 -0
- package/dist/tools/reminders/index.js.map +1 -0
- package/dist/tools/setup/handlers.d.ts +81 -0
- package/dist/tools/setup/handlers.d.ts.map +1 -0
- package/dist/tools/setup/handlers.js +172 -0
- package/dist/tools/setup/handlers.js.map +1 -0
- package/dist/tools/setup/index.d.ts +11 -0
- package/dist/tools/setup/index.d.ts.map +1 -0
- package/dist/tools/setup/index.js +10 -0
- package/dist/tools/setup/index.js.map +1 -0
- package/dist/tools/tasks/handlers.d.ts +95 -0
- package/dist/tools/tasks/handlers.d.ts.map +1 -0
- package/dist/tools/tasks/handlers.js +197 -0
- package/dist/tools/tasks/handlers.js.map +1 -0
- package/dist/tools/tasks/index.d.ts +11 -0
- package/dist/tools/tasks/index.d.ts.map +1 -0
- package/dist/tools/tasks/index.js +10 -0
- package/dist/tools/tasks/index.js.map +1 -0
- package/dist/tools/types.d.ts +54 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/types.js +9 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/types/calendar.d.ts +41 -0
- package/dist/types/calendar.d.ts.map +1 -0
- package/dist/types/calendar.js +18 -0
- package/dist/types/calendar.js.map +1 -0
- package/dist/utils/estimation.d.ts +34 -0
- package/dist/utils/estimation.d.ts.map +1 -1
- package/dist/utils/estimation.js +38 -1
- package/dist/utils/estimation.js.map +1 -1
- package/dist/utils/mcp-response.d.ts +89 -0
- package/dist/utils/mcp-response.d.ts.map +1 -0
- package/dist/utils/mcp-response.js +103 -0
- package/dist/utils/mcp-response.js.map +1 -0
- package/dist/utils/task-splitter.d.ts +65 -4
- package/dist/utils/task-splitter.d.ts.map +1 -1
- package/dist/utils/task-splitter.js +69 -5
- package/dist/utils/task-splitter.js.map +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/dist/cli/http-server.d.ts +0 -74
- package/dist/cli/http-server.d.ts.map +0 -1
- package/dist/cli/http-server.js +0 -407
- package/dist/cli/http-server.js.map +0 -1
- package/dist/remote/remote-mcp-server.d.ts +0 -244
- package/dist/remote/remote-mcp-server.d.ts.map +0 -1
- package/dist/remote/remote-mcp-server.js +0 -507
- package/dist/remote/remote-mcp-server.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -9,8 +9,6 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
9
9
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
10
10
|
import { z } from "zod";
|
|
11
11
|
import { ConfigLoader } from "./config/loader.js";
|
|
12
|
-
import { SetupWizard } from "./setup/wizard.js";
|
|
13
|
-
import { TaskAnalyzer } from "./tools/analyze-tasks.js";
|
|
14
12
|
import { ReminderManager } from "./integrations/reminder-manager.js";
|
|
15
13
|
import { CalendarService } from "./integrations/calendar-service.js";
|
|
16
14
|
import { CalendarSourceManager } from "./integrations/calendar-source-manager.js";
|
|
@@ -21,6 +19,13 @@ import { TaskSynchronizer } from "./integrations/task-synchronizer.js";
|
|
|
21
19
|
import { CalendarEventResponseService } from "./integrations/calendar-event-response.js";
|
|
22
20
|
import { WorkingCadenceService } from "./services/working-cadence.js";
|
|
23
21
|
import { VERSION, SERVER_NAME } from "./version.js";
|
|
22
|
+
import { createErrorFromCatch } from "./utils/mcp-response.js";
|
|
23
|
+
// Extracted tool handlers
|
|
24
|
+
import { handleCheckSetupStatus, handleStartSetupWizard, handleAnswerWizardQuestion, handleSaveConfig, } from "./tools/setup/index.js";
|
|
25
|
+
import { handleAnalyzeTasks, handleUpdateTaskStatus, handleSyncTasks, handleDetectDuplicates, } from "./tools/tasks/index.js";
|
|
26
|
+
import { handleFindAvailableSlots, handleListCalendarEvents, handleRespondToCalendarEvent, handleRespondToCalendarEventsBatch, handleCreateCalendarEvent, handleDeleteCalendarEvent, handleDeleteCalendarEventsBatch, handleListCalendarSources, handleGetWorkingCadence, } from "./tools/calendar/index.js";
|
|
27
|
+
import { handleSetReminder, handleListTodos, } from "./tools/reminders/index.js";
|
|
28
|
+
import { handleSyncToNotion, handleUpdateConfig, } from "./tools/integrations/index.js";
|
|
24
29
|
// Global state
|
|
25
30
|
let config = null;
|
|
26
31
|
let wizardSession = null;
|
|
@@ -33,111 +38,6 @@ let todoListManager = null;
|
|
|
33
38
|
let taskSynchronizer = null;
|
|
34
39
|
let calendarEventResponseService = null;
|
|
35
40
|
let workingCadenceService = null;
|
|
36
|
-
/**
|
|
37
|
-
* Validate config updates for a specific section
|
|
38
|
-
*/
|
|
39
|
-
function validateConfigUpdate(section, updates) {
|
|
40
|
-
const invalidFields = [];
|
|
41
|
-
switch (section) {
|
|
42
|
-
case "user":
|
|
43
|
-
if (updates.name !== undefined && typeof updates.name !== "string") {
|
|
44
|
-
invalidFields.push("name");
|
|
45
|
-
}
|
|
46
|
-
if (updates.timezone !== undefined &&
|
|
47
|
-
typeof updates.timezone !== "string") {
|
|
48
|
-
invalidFields.push("timezone");
|
|
49
|
-
}
|
|
50
|
-
break;
|
|
51
|
-
case "calendar":
|
|
52
|
-
if (updates.workingHours !== undefined) {
|
|
53
|
-
const wh = updates.workingHours;
|
|
54
|
-
if (!wh.start || !wh.end) {
|
|
55
|
-
invalidFields.push("workingHours");
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
if (updates.deepWorkDays !== undefined &&
|
|
59
|
-
!Array.isArray(updates.deepWorkDays)) {
|
|
60
|
-
invalidFields.push("deepWorkDays");
|
|
61
|
-
}
|
|
62
|
-
if (updates.meetingHeavyDays !== undefined &&
|
|
63
|
-
!Array.isArray(updates.meetingHeavyDays)) {
|
|
64
|
-
invalidFields.push("meetingHeavyDays");
|
|
65
|
-
}
|
|
66
|
-
break;
|
|
67
|
-
case "integrations":
|
|
68
|
-
if (updates.notion !== undefined) {
|
|
69
|
-
const notion = updates.notion;
|
|
70
|
-
if (notion.enabled === true && !notion.databaseId) {
|
|
71
|
-
invalidFields.push("notion.databaseId");
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
break;
|
|
75
|
-
case "team":
|
|
76
|
-
if (updates.members !== undefined && !Array.isArray(updates.members)) {
|
|
77
|
-
invalidFields.push("members");
|
|
78
|
-
}
|
|
79
|
-
if (updates.managers !== undefined && !Array.isArray(updates.managers)) {
|
|
80
|
-
invalidFields.push("managers");
|
|
81
|
-
}
|
|
82
|
-
break;
|
|
83
|
-
}
|
|
84
|
-
if (invalidFields.length > 0) {
|
|
85
|
-
return {
|
|
86
|
-
valid: false,
|
|
87
|
-
error: `無効なフィールド: ${invalidFields.join(", ")}`,
|
|
88
|
-
invalidFields,
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
return { valid: true };
|
|
92
|
-
}
|
|
93
|
-
/**
|
|
94
|
-
* Apply config updates to a specific section
|
|
95
|
-
*/
|
|
96
|
-
function applyConfigUpdates(currentConfig, section, updates) {
|
|
97
|
-
const newConfig = { ...currentConfig };
|
|
98
|
-
switch (section) {
|
|
99
|
-
case "user":
|
|
100
|
-
newConfig.user = { ...newConfig.user, ...updates };
|
|
101
|
-
break;
|
|
102
|
-
case "calendar":
|
|
103
|
-
newConfig.calendar = {
|
|
104
|
-
...newConfig.calendar,
|
|
105
|
-
...updates,
|
|
106
|
-
};
|
|
107
|
-
break;
|
|
108
|
-
case "priorityRules":
|
|
109
|
-
newConfig.priorityRules = {
|
|
110
|
-
...newConfig.priorityRules,
|
|
111
|
-
...updates,
|
|
112
|
-
};
|
|
113
|
-
break;
|
|
114
|
-
case "integrations":
|
|
115
|
-
// Deep merge for integrations
|
|
116
|
-
if (updates.appleReminders) {
|
|
117
|
-
newConfig.integrations.appleReminders = {
|
|
118
|
-
...newConfig.integrations.appleReminders,
|
|
119
|
-
...updates.appleReminders,
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
if (updates.notion) {
|
|
123
|
-
newConfig.integrations.notion = {
|
|
124
|
-
...newConfig.integrations.notion,
|
|
125
|
-
...updates.notion,
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
break;
|
|
129
|
-
case "team":
|
|
130
|
-
newConfig.team = { ...newConfig.team, ...updates };
|
|
131
|
-
break;
|
|
132
|
-
case "preferences":
|
|
133
|
-
newConfig.preferences = {
|
|
134
|
-
...newConfig.preferences,
|
|
135
|
-
...updates,
|
|
136
|
-
};
|
|
137
|
-
break;
|
|
138
|
-
}
|
|
139
|
-
return newConfig;
|
|
140
|
-
}
|
|
141
41
|
/**
|
|
142
42
|
* Initialize services with config
|
|
143
43
|
*/
|
|
@@ -178,6 +78,61 @@ function initializeServices(userConfig) {
|
|
|
178
78
|
calendarEventResponseService = new CalendarEventResponseService();
|
|
179
79
|
workingCadenceService = new WorkingCadenceService();
|
|
180
80
|
}
|
|
81
|
+
// ============================================
|
|
82
|
+
// Context Factory Functions
|
|
83
|
+
// ============================================
|
|
84
|
+
function createSetupContext() {
|
|
85
|
+
return {
|
|
86
|
+
getConfig: () => config,
|
|
87
|
+
setConfig: (c) => {
|
|
88
|
+
config = c;
|
|
89
|
+
},
|
|
90
|
+
getWizardSession: () => wizardSession,
|
|
91
|
+
setWizardSession: (session) => {
|
|
92
|
+
wizardSession = session;
|
|
93
|
+
},
|
|
94
|
+
initializeServices,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function createTaskToolsContext() {
|
|
98
|
+
return {
|
|
99
|
+
getConfig: () => config,
|
|
100
|
+
getTodoListManager: () => todoListManager,
|
|
101
|
+
getTaskSynchronizer: () => taskSynchronizer,
|
|
102
|
+
initializeServices,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function createCalendarToolsContext() {
|
|
106
|
+
return {
|
|
107
|
+
getConfig: () => config,
|
|
108
|
+
getCalendarSourceManager: () => calendarSourceManager,
|
|
109
|
+
getCalendarEventResponseService: () => calendarEventResponseService,
|
|
110
|
+
getGoogleCalendarService: () => googleCalendarService,
|
|
111
|
+
getWorkingCadenceService: () => workingCadenceService,
|
|
112
|
+
setWorkingCadenceService: (service) => {
|
|
113
|
+
workingCadenceService = service;
|
|
114
|
+
},
|
|
115
|
+
initializeServices,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
function createReminderTodoContext() {
|
|
119
|
+
return {
|
|
120
|
+
getConfig: () => config,
|
|
121
|
+
getReminderManager: () => reminderManager,
|
|
122
|
+
getTodoListManager: () => todoListManager,
|
|
123
|
+
initializeServices,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
function createIntegrationToolsContext() {
|
|
127
|
+
return {
|
|
128
|
+
getConfig: () => config,
|
|
129
|
+
setConfig: (c) => {
|
|
130
|
+
config = c;
|
|
131
|
+
},
|
|
132
|
+
getNotionService: () => notionService,
|
|
133
|
+
initializeServices,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
181
136
|
/**
|
|
182
137
|
* Initialize the MCP server with all tools
|
|
183
138
|
*/
|
|
@@ -197,257 +152,27 @@ async function createServer() {
|
|
|
197
152
|
config = null;
|
|
198
153
|
}
|
|
199
154
|
// ============================================
|
|
200
|
-
// Setup & Configuration Tools
|
|
155
|
+
// Setup & Configuration Tools - uses extracted handlers
|
|
201
156
|
// ============================================
|
|
202
|
-
|
|
203
|
-
* check_setup_status - Check if initial setup is complete
|
|
204
|
-
* Requirement: 1.1, 1.2
|
|
205
|
-
*/
|
|
206
|
-
server.tool("check_setup_status", "Check if sage has been configured. Returns setup status and guidance.", {}, async () => {
|
|
207
|
-
const exists = await ConfigLoader.exists();
|
|
208
|
-
const isValid = config !== null;
|
|
209
|
-
if (!exists) {
|
|
210
|
-
return {
|
|
211
|
-
content: [
|
|
212
|
-
{
|
|
213
|
-
type: "text",
|
|
214
|
-
text: JSON.stringify({
|
|
215
|
-
setupComplete: false,
|
|
216
|
-
configExists: false,
|
|
217
|
-
message: "sageの初期設定が必要です。start_setup_wizardを実行してセットアップを開始してください。",
|
|
218
|
-
nextAction: "start_setup_wizard",
|
|
219
|
-
}, null, 2),
|
|
220
|
-
},
|
|
221
|
-
],
|
|
222
|
-
};
|
|
223
|
-
}
|
|
224
|
-
if (!isValid) {
|
|
225
|
-
return {
|
|
226
|
-
content: [
|
|
227
|
-
{
|
|
228
|
-
type: "text",
|
|
229
|
-
text: JSON.stringify({
|
|
230
|
-
setupComplete: false,
|
|
231
|
-
configExists: true,
|
|
232
|
-
message: "設定ファイルが見つかりましたが、読み込みに失敗しました。設定を再作成してください。",
|
|
233
|
-
nextAction: "start_setup_wizard",
|
|
234
|
-
}, null, 2),
|
|
235
|
-
},
|
|
236
|
-
],
|
|
237
|
-
};
|
|
238
|
-
}
|
|
239
|
-
return {
|
|
240
|
-
content: [
|
|
241
|
-
{
|
|
242
|
-
type: "text",
|
|
243
|
-
text: JSON.stringify({
|
|
244
|
-
setupComplete: true,
|
|
245
|
-
configExists: true,
|
|
246
|
-
userName: config?.user.name,
|
|
247
|
-
message: "sageは設定済みです。タスク分析やリマインド設定を開始できます。",
|
|
248
|
-
availableTools: [
|
|
249
|
-
"analyze_tasks",
|
|
250
|
-
"set_reminder",
|
|
251
|
-
"find_available_slots",
|
|
252
|
-
"sync_to_notion",
|
|
253
|
-
"update_config",
|
|
254
|
-
],
|
|
255
|
-
}, null, 2),
|
|
256
|
-
},
|
|
257
|
-
],
|
|
258
|
-
};
|
|
259
|
-
});
|
|
260
|
-
/**
|
|
261
|
-
* start_setup_wizard - Begin the interactive setup process
|
|
262
|
-
* Requirement: 1.3
|
|
263
|
-
*/
|
|
157
|
+
server.tool("check_setup_status", "Check if sage has been configured. Returns setup status and guidance.", {}, async () => handleCheckSetupStatus(createSetupContext()));
|
|
264
158
|
server.tool("start_setup_wizard", "Start the interactive setup wizard for sage. Returns the first question.", {
|
|
265
159
|
mode: z
|
|
266
160
|
.enum(["full", "quick"])
|
|
267
161
|
.optional()
|
|
268
162
|
.describe("Setup mode: full (all questions) or quick (essential only)"),
|
|
269
|
-
}, async ({ mode
|
|
270
|
-
wizardSession = SetupWizard.createSession(mode);
|
|
271
|
-
const question = SetupWizard.getCurrentQuestion(wizardSession);
|
|
272
|
-
return {
|
|
273
|
-
content: [
|
|
274
|
-
{
|
|
275
|
-
type: "text",
|
|
276
|
-
text: JSON.stringify({
|
|
277
|
-
sessionId: wizardSession.sessionId,
|
|
278
|
-
currentStep: wizardSession.currentStep,
|
|
279
|
-
totalSteps: wizardSession.totalSteps,
|
|
280
|
-
progress: Math.round((wizardSession.currentStep / wizardSession.totalSteps) * 100),
|
|
281
|
-
question: {
|
|
282
|
-
id: question.id,
|
|
283
|
-
text: question.text,
|
|
284
|
-
type: question.type,
|
|
285
|
-
options: question.options,
|
|
286
|
-
defaultValue: question.defaultValue,
|
|
287
|
-
helpText: question.helpText,
|
|
288
|
-
},
|
|
289
|
-
message: "セットアップを開始します。以下の質問に回答してください。",
|
|
290
|
-
}, null, 2),
|
|
291
|
-
},
|
|
292
|
-
],
|
|
293
|
-
};
|
|
294
|
-
});
|
|
295
|
-
/**
|
|
296
|
-
* answer_wizard_question - Answer a setup wizard question
|
|
297
|
-
* Requirement: 1.3, 1.4
|
|
298
|
-
*/
|
|
163
|
+
}, async ({ mode }) => handleStartSetupWizard(createSetupContext(), { mode: mode ?? "full" }));
|
|
299
164
|
server.tool("answer_wizard_question", "Answer a question in the setup wizard and get the next question.", {
|
|
300
165
|
questionId: z.string().describe("The ID of the question being answered"),
|
|
301
166
|
answer: z
|
|
302
167
|
.union([z.string(), z.array(z.string())])
|
|
303
168
|
.describe("The answer to the question"),
|
|
304
|
-
}, async ({ questionId, answer }) => {
|
|
305
|
-
if (!wizardSession) {
|
|
306
|
-
return {
|
|
307
|
-
content: [
|
|
308
|
-
{
|
|
309
|
-
type: "text",
|
|
310
|
-
text: JSON.stringify({
|
|
311
|
-
error: true,
|
|
312
|
-
message: "セットアップセッションが見つかりません。start_setup_wizardを実行してください。",
|
|
313
|
-
}, null, 2),
|
|
314
|
-
},
|
|
315
|
-
],
|
|
316
|
-
};
|
|
317
|
-
}
|
|
318
|
-
const result = SetupWizard.answerQuestion(wizardSession, questionId, answer);
|
|
319
|
-
if (!result.success) {
|
|
320
|
-
return {
|
|
321
|
-
content: [
|
|
322
|
-
{
|
|
323
|
-
type: "text",
|
|
324
|
-
text: JSON.stringify({
|
|
325
|
-
error: true,
|
|
326
|
-
message: result.error,
|
|
327
|
-
currentQuestion: result.currentQuestion,
|
|
328
|
-
}, null, 2),
|
|
329
|
-
},
|
|
330
|
-
],
|
|
331
|
-
};
|
|
332
|
-
}
|
|
333
|
-
if (result.isComplete) {
|
|
334
|
-
return {
|
|
335
|
-
content: [
|
|
336
|
-
{
|
|
337
|
-
type: "text",
|
|
338
|
-
text: JSON.stringify({
|
|
339
|
-
isComplete: true,
|
|
340
|
-
sessionId: wizardSession.sessionId,
|
|
341
|
-
answers: wizardSession.answers,
|
|
342
|
-
message: "すべての質問に回答しました。save_configを実行して設定を保存してください。",
|
|
343
|
-
nextAction: "save_config",
|
|
344
|
-
}, null, 2),
|
|
345
|
-
},
|
|
346
|
-
],
|
|
347
|
-
};
|
|
348
|
-
}
|
|
349
|
-
const nextQuestion = SetupWizard.getCurrentQuestion(wizardSession);
|
|
350
|
-
return {
|
|
351
|
-
content: [
|
|
352
|
-
{
|
|
353
|
-
type: "text",
|
|
354
|
-
text: JSON.stringify({
|
|
355
|
-
success: true,
|
|
356
|
-
currentStep: wizardSession.currentStep,
|
|
357
|
-
totalSteps: wizardSession.totalSteps,
|
|
358
|
-
progress: Math.round((wizardSession.currentStep / wizardSession.totalSteps) * 100),
|
|
359
|
-
question: {
|
|
360
|
-
id: nextQuestion.id,
|
|
361
|
-
text: nextQuestion.text,
|
|
362
|
-
type: nextQuestion.type,
|
|
363
|
-
options: nextQuestion.options,
|
|
364
|
-
defaultValue: nextQuestion.defaultValue,
|
|
365
|
-
helpText: nextQuestion.helpText,
|
|
366
|
-
},
|
|
367
|
-
}, null, 2),
|
|
368
|
-
},
|
|
369
|
-
],
|
|
370
|
-
};
|
|
371
|
-
});
|
|
372
|
-
/**
|
|
373
|
-
* save_config - Save the configuration from the setup wizard
|
|
374
|
-
* Requirement: 1.4, 1.5, 1.6
|
|
375
|
-
*/
|
|
169
|
+
}, async ({ questionId, answer }) => handleAnswerWizardQuestion(createSetupContext(), { questionId, answer }));
|
|
376
170
|
server.tool("save_config", "Save the configuration after completing the setup wizard.", {
|
|
377
171
|
confirm: z.boolean().describe("Confirm saving the configuration"),
|
|
378
|
-
}, async ({ confirm }) => {
|
|
379
|
-
if (!confirm) {
|
|
380
|
-
return {
|
|
381
|
-
content: [
|
|
382
|
-
{
|
|
383
|
-
type: "text",
|
|
384
|
-
text: JSON.stringify({
|
|
385
|
-
saved: false,
|
|
386
|
-
message: "設定の保存がキャンセルされました。",
|
|
387
|
-
}, null, 2),
|
|
388
|
-
},
|
|
389
|
-
],
|
|
390
|
-
};
|
|
391
|
-
}
|
|
392
|
-
if (!wizardSession) {
|
|
393
|
-
return {
|
|
394
|
-
content: [
|
|
395
|
-
{
|
|
396
|
-
type: "text",
|
|
397
|
-
text: JSON.stringify({
|
|
398
|
-
error: true,
|
|
399
|
-
message: "セットアップセッションが見つかりません。start_setup_wizardを実行してください。",
|
|
400
|
-
}, null, 2),
|
|
401
|
-
},
|
|
402
|
-
],
|
|
403
|
-
};
|
|
404
|
-
}
|
|
405
|
-
try {
|
|
406
|
-
const newConfig = SetupWizard.buildConfig(wizardSession);
|
|
407
|
-
await ConfigLoader.save(newConfig);
|
|
408
|
-
config = newConfig;
|
|
409
|
-
wizardSession = null;
|
|
410
|
-
return {
|
|
411
|
-
content: [
|
|
412
|
-
{
|
|
413
|
-
type: "text",
|
|
414
|
-
text: JSON.stringify({
|
|
415
|
-
saved: true,
|
|
416
|
-
configPath: ConfigLoader.getConfigPath(),
|
|
417
|
-
userName: newConfig.user.name,
|
|
418
|
-
message: `設定を保存しました。${newConfig.user.name}さん、sageをご利用いただきありがとうございます!`,
|
|
419
|
-
availableTools: [
|
|
420
|
-
"analyze_tasks",
|
|
421
|
-
"set_reminder",
|
|
422
|
-
"find_available_slots",
|
|
423
|
-
"sync_to_notion",
|
|
424
|
-
],
|
|
425
|
-
}, null, 2),
|
|
426
|
-
},
|
|
427
|
-
],
|
|
428
|
-
};
|
|
429
|
-
}
|
|
430
|
-
catch (error) {
|
|
431
|
-
return {
|
|
432
|
-
content: [
|
|
433
|
-
{
|
|
434
|
-
type: "text",
|
|
435
|
-
text: JSON.stringify({
|
|
436
|
-
error: true,
|
|
437
|
-
message: `設定の保存に失敗しました: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
438
|
-
}, null, 2),
|
|
439
|
-
},
|
|
440
|
-
],
|
|
441
|
-
};
|
|
442
|
-
}
|
|
443
|
-
});
|
|
172
|
+
}, async ({ confirm }) => handleSaveConfig(createSetupContext(), { confirm }));
|
|
444
173
|
// ============================================
|
|
445
|
-
// Task Analysis Tools
|
|
174
|
+
// Task Analysis Tools - uses extracted handlers
|
|
446
175
|
// ============================================
|
|
447
|
-
/**
|
|
448
|
-
* analyze_tasks - Analyze tasks and provide prioritization
|
|
449
|
-
* Requirement: 2.1-2.6, 3.1-3.2, 4.1-4.5
|
|
450
|
-
*/
|
|
451
176
|
server.tool("analyze_tasks", "Analyze tasks to determine priority, estimate time, and identify stakeholders.", {
|
|
452
177
|
tasks: z
|
|
453
178
|
.array(z.object({
|
|
@@ -459,63 +184,8 @@ async function createServer() {
|
|
|
459
184
|
.describe("Task deadline (ISO 8601 format)"),
|
|
460
185
|
}))
|
|
461
186
|
.describe("List of tasks to analyze"),
|
|
462
|
-
}, async ({ tasks }) => {
|
|
463
|
-
|
|
464
|
-
return {
|
|
465
|
-
content: [
|
|
466
|
-
{
|
|
467
|
-
type: "text",
|
|
468
|
-
text: JSON.stringify({
|
|
469
|
-
error: true,
|
|
470
|
-
message: "sageが設定されていません。check_setup_statusを実行してください。",
|
|
471
|
-
}, null, 2),
|
|
472
|
-
},
|
|
473
|
-
],
|
|
474
|
-
};
|
|
475
|
-
}
|
|
476
|
-
try {
|
|
477
|
-
const result = await TaskAnalyzer.analyzeTasks(tasks, config);
|
|
478
|
-
return {
|
|
479
|
-
content: [
|
|
480
|
-
{
|
|
481
|
-
type: "text",
|
|
482
|
-
text: JSON.stringify({
|
|
483
|
-
success: true,
|
|
484
|
-
summary: result.summary,
|
|
485
|
-
tasks: result.analyzedTasks.map((t) => ({
|
|
486
|
-
title: t.original.title,
|
|
487
|
-
description: t.original.description,
|
|
488
|
-
deadline: t.original.deadline,
|
|
489
|
-
priority: t.priority,
|
|
490
|
-
estimatedMinutes: t.estimatedMinutes,
|
|
491
|
-
stakeholders: t.stakeholders,
|
|
492
|
-
tags: t.tags,
|
|
493
|
-
reasoning: t.reasoning,
|
|
494
|
-
suggestedReminders: t.suggestedReminders,
|
|
495
|
-
})),
|
|
496
|
-
}, null, 2),
|
|
497
|
-
},
|
|
498
|
-
],
|
|
499
|
-
};
|
|
500
|
-
}
|
|
501
|
-
catch (error) {
|
|
502
|
-
return {
|
|
503
|
-
content: [
|
|
504
|
-
{
|
|
505
|
-
type: "text",
|
|
506
|
-
text: JSON.stringify({
|
|
507
|
-
error: true,
|
|
508
|
-
message: `タスク分析に失敗しました: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
509
|
-
}, null, 2),
|
|
510
|
-
},
|
|
511
|
-
],
|
|
512
|
-
};
|
|
513
|
-
}
|
|
514
|
-
});
|
|
515
|
-
/**
|
|
516
|
-
* set_reminder - Set a reminder for a task
|
|
517
|
-
* Requirement: 5.1-5.6
|
|
518
|
-
*/
|
|
187
|
+
}, async ({ tasks }) => handleAnalyzeTasks(createTaskToolsContext(), { tasks }));
|
|
188
|
+
// set_reminder - uses extracted handler
|
|
519
189
|
server.tool("set_reminder", "Set a reminder for a task in Apple Reminders or Notion.", {
|
|
520
190
|
taskTitle: z.string().describe("Title of the task"),
|
|
521
191
|
dueDate: z
|
|
@@ -544,107 +214,15 @@ async function createServer() {
|
|
|
544
214
|
.string()
|
|
545
215
|
.optional()
|
|
546
216
|
.describe("Additional notes for the reminder"),
|
|
547
|
-
}, async ({ taskTitle, dueDate, reminderType, list, priority, notes }) => {
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
}, null, 2),
|
|
557
|
-
},
|
|
558
|
-
],
|
|
559
|
-
};
|
|
560
|
-
}
|
|
561
|
-
if (!reminderManager) {
|
|
562
|
-
initializeServices(config);
|
|
563
|
-
}
|
|
564
|
-
try {
|
|
565
|
-
const result = await reminderManager.setReminder({
|
|
566
|
-
taskTitle,
|
|
567
|
-
targetDate: dueDate,
|
|
568
|
-
reminderType,
|
|
569
|
-
list: list ?? config.integrations.appleReminders.defaultList,
|
|
570
|
-
priority: priority,
|
|
571
|
-
notes,
|
|
572
|
-
});
|
|
573
|
-
if (result.success) {
|
|
574
|
-
// Check if this is a delegation request for Notion
|
|
575
|
-
if (result.delegateToNotion && result.notionRequest) {
|
|
576
|
-
return {
|
|
577
|
-
content: [
|
|
578
|
-
{
|
|
579
|
-
type: "text",
|
|
580
|
-
text: JSON.stringify({
|
|
581
|
-
success: true,
|
|
582
|
-
destination: "notion_mcp",
|
|
583
|
-
method: "delegate",
|
|
584
|
-
delegateToNotion: true,
|
|
585
|
-
notionRequest: result.notionRequest,
|
|
586
|
-
message: `Notionへの追加はClaude Codeが直接notion-create-pagesツールを使用してください。`,
|
|
587
|
-
instruction: `notion-create-pagesツールを以下のパラメータで呼び出してください:
|
|
588
|
-
- parent: { "type": "data_source_id", "data_source_id": "${result.notionRequest.databaseId.replace(/-/g, "")}" }
|
|
589
|
-
- pages: [{ "properties": ${JSON.stringify(result.notionRequest.properties)} }]`,
|
|
590
|
-
}, null, 2),
|
|
591
|
-
},
|
|
592
|
-
],
|
|
593
|
-
};
|
|
594
|
-
}
|
|
595
|
-
return {
|
|
596
|
-
content: [
|
|
597
|
-
{
|
|
598
|
-
type: "text",
|
|
599
|
-
text: JSON.stringify({
|
|
600
|
-
success: true,
|
|
601
|
-
destination: result.destination,
|
|
602
|
-
method: result.method,
|
|
603
|
-
reminderId: result.reminderId,
|
|
604
|
-
reminderUrl: result.reminderUrl ?? result.pageUrl,
|
|
605
|
-
message: result.destination === "apple_reminders"
|
|
606
|
-
? `Apple Remindersにリマインダーを作成しました: ${taskTitle}`
|
|
607
|
-
: `Notionにタスクを作成しました: ${taskTitle}`,
|
|
608
|
-
}, null, 2),
|
|
609
|
-
},
|
|
610
|
-
],
|
|
611
|
-
};
|
|
612
|
-
}
|
|
613
|
-
return {
|
|
614
|
-
content: [
|
|
615
|
-
{
|
|
616
|
-
type: "text",
|
|
617
|
-
text: JSON.stringify({
|
|
618
|
-
success: false,
|
|
619
|
-
destination: result.destination,
|
|
620
|
-
error: result.error,
|
|
621
|
-
fallbackText: result.fallbackText,
|
|
622
|
-
message: result.fallbackText
|
|
623
|
-
? "自動作成に失敗しました。以下のテキストを手動でコピーしてください。"
|
|
624
|
-
: `リマインダー作成に失敗しました: ${result.error}`,
|
|
625
|
-
}, null, 2),
|
|
626
|
-
},
|
|
627
|
-
],
|
|
628
|
-
};
|
|
629
|
-
}
|
|
630
|
-
catch (error) {
|
|
631
|
-
return {
|
|
632
|
-
content: [
|
|
633
|
-
{
|
|
634
|
-
type: "text",
|
|
635
|
-
text: JSON.stringify({
|
|
636
|
-
error: true,
|
|
637
|
-
message: `リマインダー設定に失敗しました: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
638
|
-
}, null, 2),
|
|
639
|
-
},
|
|
640
|
-
],
|
|
641
|
-
};
|
|
642
|
-
}
|
|
643
|
-
});
|
|
644
|
-
/**
|
|
645
|
-
* find_available_slots - Find available time slots in calendar
|
|
646
|
-
* Requirement: 3.3-3.6, 6.1-6.6, 7 (Task 28: Multi-source support)
|
|
647
|
-
*/
|
|
217
|
+
}, async ({ taskTitle, dueDate, reminderType, list, priority, notes }) => handleSetReminder(createReminderTodoContext(), {
|
|
218
|
+
taskTitle,
|
|
219
|
+
dueDate,
|
|
220
|
+
reminderType,
|
|
221
|
+
list,
|
|
222
|
+
priority,
|
|
223
|
+
notes,
|
|
224
|
+
}));
|
|
225
|
+
// find_available_slots - uses extracted handler
|
|
648
226
|
server.tool("find_available_slots", "Find available time slots in the calendar for scheduling tasks from all enabled calendar sources.", {
|
|
649
227
|
durationMinutes: z.number().describe("Required duration in minutes"),
|
|
650
228
|
startDate: z
|
|
@@ -667,110 +245,15 @@ async function createServer() {
|
|
|
667
245
|
.number()
|
|
668
246
|
.optional()
|
|
669
247
|
.describe("Maximum slot duration in minutes (default: 480)"),
|
|
670
|
-
}, async ({ durationMinutes, startDate, endDate, preferDeepWork, minDurationMinutes, maxDurationMinutes }) => {
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
}, null, 2),
|
|
680
|
-
},
|
|
681
|
-
],
|
|
682
|
-
};
|
|
683
|
-
}
|
|
684
|
-
if (!calendarSourceManager) {
|
|
685
|
-
initializeServices(config);
|
|
686
|
-
}
|
|
687
|
-
try {
|
|
688
|
-
// Get enabled sources
|
|
689
|
-
const enabledSources = calendarSourceManager.getEnabledSources();
|
|
690
|
-
if (enabledSources.length === 0) {
|
|
691
|
-
return {
|
|
692
|
-
content: [
|
|
693
|
-
{
|
|
694
|
-
type: "text",
|
|
695
|
-
text: JSON.stringify({
|
|
696
|
-
success: false,
|
|
697
|
-
message: "有効なカレンダーソースがありません。設定でEventKitまたはGoogle Calendarを有効にしてください。",
|
|
698
|
-
}, null, 2),
|
|
699
|
-
},
|
|
700
|
-
],
|
|
701
|
-
};
|
|
702
|
-
}
|
|
703
|
-
// Prepare date range
|
|
704
|
-
const searchStart = startDate ?? new Date().toISOString().split("T")[0];
|
|
705
|
-
const searchEnd = endDate ??
|
|
706
|
-
new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)
|
|
707
|
-
.toISOString()
|
|
708
|
-
.split("T")[0];
|
|
709
|
-
// Get working hours from config
|
|
710
|
-
const workingHours = {
|
|
711
|
-
start: config.calendar.workingHours.start,
|
|
712
|
-
end: config.calendar.workingHours.end,
|
|
713
|
-
};
|
|
714
|
-
// Use durationMinutes as default min duration for backwards compatibility
|
|
715
|
-
// New parameters minDurationMinutes and maxDurationMinutes take precedence
|
|
716
|
-
const minDuration = minDurationMinutes ?? durationMinutes ?? 25;
|
|
717
|
-
const maxDuration = maxDurationMinutes ?? 480;
|
|
718
|
-
// Find available slots using CalendarSourceManager
|
|
719
|
-
// This automatically fetches from all enabled sources, merges, and deduplicates
|
|
720
|
-
const slots = await calendarSourceManager.findAvailableSlots({
|
|
721
|
-
startDate: searchStart,
|
|
722
|
-
endDate: searchEnd,
|
|
723
|
-
minDurationMinutes: minDuration,
|
|
724
|
-
maxDurationMinutes: maxDuration,
|
|
725
|
-
workingHours,
|
|
726
|
-
});
|
|
727
|
-
// Filter for deep work preference if requested
|
|
728
|
-
const filteredSlots = preferDeepWork
|
|
729
|
-
? slots.filter((s) => s.dayType === "deep-work")
|
|
730
|
-
: slots;
|
|
731
|
-
return {
|
|
732
|
-
content: [
|
|
733
|
-
{
|
|
734
|
-
type: "text",
|
|
735
|
-
text: JSON.stringify({
|
|
736
|
-
success: true,
|
|
737
|
-
sources: enabledSources,
|
|
738
|
-
searchRange: { start: searchStart, end: searchEnd },
|
|
739
|
-
totalSlots: filteredSlots.length,
|
|
740
|
-
slots: filteredSlots.slice(0, 10).map((slot) => ({
|
|
741
|
-
start: slot.start,
|
|
742
|
-
end: slot.end,
|
|
743
|
-
durationMinutes: slot.durationMinutes,
|
|
744
|
-
suitability: slot.suitability,
|
|
745
|
-
dayType: slot.dayType,
|
|
746
|
-
reason: slot.reason,
|
|
747
|
-
})),
|
|
748
|
-
message: filteredSlots.length > 0
|
|
749
|
-
? `${filteredSlots.length}件の空き時間が見つかりました (ソース: ${enabledSources.join(', ')})。`
|
|
750
|
-
: "指定した条件に合う空き時間が見つかりませんでした。",
|
|
751
|
-
}, null, 2),
|
|
752
|
-
},
|
|
753
|
-
],
|
|
754
|
-
};
|
|
755
|
-
}
|
|
756
|
-
catch (error) {
|
|
757
|
-
return {
|
|
758
|
-
content: [
|
|
759
|
-
{
|
|
760
|
-
type: "text",
|
|
761
|
-
text: JSON.stringify({
|
|
762
|
-
error: true,
|
|
763
|
-
message: `カレンダー検索に失敗しました: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
764
|
-
}, null, 2),
|
|
765
|
-
},
|
|
766
|
-
],
|
|
767
|
-
};
|
|
768
|
-
}
|
|
769
|
-
});
|
|
770
|
-
/**
|
|
771
|
-
* list_calendar_events - List calendar events for a specified period
|
|
772
|
-
* Requirement: 16.1-16.12, Task 27 (Multi-source support)
|
|
773
|
-
*/
|
|
248
|
+
}, async ({ durationMinutes, startDate, endDate, preferDeepWork, minDurationMinutes, maxDurationMinutes }) => handleFindAvailableSlots(createCalendarToolsContext(), {
|
|
249
|
+
durationMinutes,
|
|
250
|
+
startDate,
|
|
251
|
+
endDate,
|
|
252
|
+
preferDeepWork,
|
|
253
|
+
minDurationMinutes,
|
|
254
|
+
maxDurationMinutes,
|
|
255
|
+
}));
|
|
256
|
+
// list_calendar_events - uses extracted handler
|
|
774
257
|
server.tool("list_calendar_events", "List calendar events for a specified period from enabled sources (EventKit, Google Calendar, or both). Returns events with details including calendar name and location.", {
|
|
775
258
|
startDate: z
|
|
776
259
|
.string()
|
|
@@ -782,87 +265,12 @@ async function createServer() {
|
|
|
782
265
|
.string()
|
|
783
266
|
.optional()
|
|
784
267
|
.describe("Optional: filter events by calendar ID or name"),
|
|
785
|
-
}, async ({ startDate, endDate, calendarId }) => {
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
text: JSON.stringify({
|
|
792
|
-
error: true,
|
|
793
|
-
message: "sageが設定されていません。check_setup_statusを実行してください。",
|
|
794
|
-
}, null, 2),
|
|
795
|
-
},
|
|
796
|
-
],
|
|
797
|
-
};
|
|
798
|
-
}
|
|
799
|
-
if (!calendarSourceManager) {
|
|
800
|
-
initializeServices(config);
|
|
801
|
-
}
|
|
802
|
-
try {
|
|
803
|
-
// Get enabled sources
|
|
804
|
-
const enabledSources = calendarSourceManager.getEnabledSources();
|
|
805
|
-
if (enabledSources.length === 0) {
|
|
806
|
-
return {
|
|
807
|
-
content: [
|
|
808
|
-
{
|
|
809
|
-
type: "text",
|
|
810
|
-
text: JSON.stringify({
|
|
811
|
-
success: false,
|
|
812
|
-
message: "有効なカレンダーソースがありません。設定でEventKitまたはGoogle Calendarを有効にしてください。",
|
|
813
|
-
}, null, 2),
|
|
814
|
-
},
|
|
815
|
-
],
|
|
816
|
-
};
|
|
817
|
-
}
|
|
818
|
-
// Get events from all enabled sources (already merged/deduplicated)
|
|
819
|
-
const events = await calendarSourceManager.getEvents(startDate, endDate, calendarId);
|
|
820
|
-
return {
|
|
821
|
-
content: [
|
|
822
|
-
{
|
|
823
|
-
type: "text",
|
|
824
|
-
text: JSON.stringify({
|
|
825
|
-
success: true,
|
|
826
|
-
sources: enabledSources,
|
|
827
|
-
events: events.map((event) => ({
|
|
828
|
-
id: event.id,
|
|
829
|
-
title: event.title,
|
|
830
|
-
start: event.start,
|
|
831
|
-
end: event.end,
|
|
832
|
-
isAllDay: event.isAllDay,
|
|
833
|
-
// Note: calendar and location are optional fields added in Task 25
|
|
834
|
-
calendar: event.calendar,
|
|
835
|
-
location: event.location,
|
|
836
|
-
source: event.source,
|
|
837
|
-
})),
|
|
838
|
-
period: { start: startDate, end: endDate },
|
|
839
|
-
totalEvents: events.length,
|
|
840
|
-
message: events.length > 0
|
|
841
|
-
? `${events.length}件のイベントが見つかりました (ソース: ${enabledSources.join(', ')})。`
|
|
842
|
-
: "指定した期間にイベントが見つかりませんでした。",
|
|
843
|
-
}, null, 2),
|
|
844
|
-
},
|
|
845
|
-
],
|
|
846
|
-
};
|
|
847
|
-
}
|
|
848
|
-
catch (error) {
|
|
849
|
-
return {
|
|
850
|
-
content: [
|
|
851
|
-
{
|
|
852
|
-
type: "text",
|
|
853
|
-
text: JSON.stringify({
|
|
854
|
-
error: true,
|
|
855
|
-
message: `カレンダーイベントの取得に失敗しました: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
856
|
-
}, null, 2),
|
|
857
|
-
},
|
|
858
|
-
],
|
|
859
|
-
};
|
|
860
|
-
}
|
|
861
|
-
});
|
|
862
|
-
/**
|
|
863
|
-
* respond_to_calendar_event - Respond to a single calendar event
|
|
864
|
-
* Requirement: 17.1, 17.2, 17.5-17.11, 6 (Google Calendar support)
|
|
865
|
-
*/
|
|
268
|
+
}, async ({ startDate, endDate, calendarId }) => handleListCalendarEvents(createCalendarToolsContext(), {
|
|
269
|
+
startDate,
|
|
270
|
+
endDate,
|
|
271
|
+
calendarId,
|
|
272
|
+
}));
|
|
273
|
+
// respond_to_calendar_event - uses extracted handler
|
|
866
274
|
server.tool("respond_to_calendar_event", "Respond to a calendar event with accept, decline, or tentative. Supports both EventKit (macOS) and Google Calendar events. Use this to RSVP to meeting invitations from any enabled calendar source.", {
|
|
867
275
|
eventId: z.string().describe("The ID of the calendar event to respond to"),
|
|
868
276
|
response: z
|
|
@@ -880,154 +288,14 @@ async function createServer() {
|
|
|
880
288
|
.string()
|
|
881
289
|
.optional()
|
|
882
290
|
.describe("Optional: Google Calendar ID (defaults to 'primary'). Only used for Google Calendar events."),
|
|
883
|
-
}, async ({ eventId, response, comment, source, calendarId }) => {
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
message: "sageが設定されていません。check_setup_statusを実行してください。",
|
|
892
|
-
}, null, 2),
|
|
893
|
-
},
|
|
894
|
-
],
|
|
895
|
-
};
|
|
896
|
-
}
|
|
897
|
-
if (!calendarSourceManager || !calendarEventResponseService) {
|
|
898
|
-
initializeServices(config);
|
|
899
|
-
}
|
|
900
|
-
try {
|
|
901
|
-
// If source is explicitly Google Calendar, or if source is not specified, try Google Calendar first
|
|
902
|
-
if (source === 'google' || !source) {
|
|
903
|
-
try {
|
|
904
|
-
const result = await calendarSourceManager.respondToEvent(eventId, response, source === 'google' ? 'google' : undefined, calendarId);
|
|
905
|
-
return {
|
|
906
|
-
content: [
|
|
907
|
-
{
|
|
908
|
-
type: "text",
|
|
909
|
-
text: JSON.stringify({
|
|
910
|
-
success: true,
|
|
911
|
-
eventId,
|
|
912
|
-
source: result.source || 'google',
|
|
913
|
-
message: result.message,
|
|
914
|
-
}, null, 2),
|
|
915
|
-
},
|
|
916
|
-
],
|
|
917
|
-
};
|
|
918
|
-
}
|
|
919
|
-
catch (error) {
|
|
920
|
-
// If source was explicitly 'google', don't try EventKit
|
|
921
|
-
if (source === 'google') {
|
|
922
|
-
return {
|
|
923
|
-
content: [
|
|
924
|
-
{
|
|
925
|
-
type: "text",
|
|
926
|
-
text: JSON.stringify({
|
|
927
|
-
error: true,
|
|
928
|
-
message: `Google Calendarイベント返信に失敗しました: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
929
|
-
}, null, 2),
|
|
930
|
-
},
|
|
931
|
-
],
|
|
932
|
-
};
|
|
933
|
-
}
|
|
934
|
-
// If source was not specified, continue to try EventKit
|
|
935
|
-
}
|
|
936
|
-
}
|
|
937
|
-
// Try EventKit (either explicitly requested or as fallback)
|
|
938
|
-
if (source === 'eventkit' || !source) {
|
|
939
|
-
// Check platform availability
|
|
940
|
-
const isAvailable = await calendarEventResponseService.isEventKitAvailable();
|
|
941
|
-
if (!isAvailable) {
|
|
942
|
-
return {
|
|
943
|
-
content: [
|
|
944
|
-
{
|
|
945
|
-
type: "text",
|
|
946
|
-
text: JSON.stringify({
|
|
947
|
-
success: false,
|
|
948
|
-
message: "EventKitカレンダーイベント返信機能はmacOSでのみ利用可能です。Google Calendarイベントの場合は、source='google'を指定してください。",
|
|
949
|
-
}, null, 2),
|
|
950
|
-
},
|
|
951
|
-
],
|
|
952
|
-
};
|
|
953
|
-
}
|
|
954
|
-
// Respond to the event via EventKit
|
|
955
|
-
const result = await calendarEventResponseService.respondToEvent({
|
|
956
|
-
eventId,
|
|
957
|
-
response,
|
|
958
|
-
comment,
|
|
959
|
-
});
|
|
960
|
-
if (result.success) {
|
|
961
|
-
return {
|
|
962
|
-
content: [
|
|
963
|
-
{
|
|
964
|
-
type: "text",
|
|
965
|
-
text: JSON.stringify({
|
|
966
|
-
success: true,
|
|
967
|
-
eventId: result.eventId,
|
|
968
|
-
eventTitle: result.eventTitle,
|
|
969
|
-
newStatus: result.newStatus,
|
|
970
|
-
method: result.method,
|
|
971
|
-
instanceOnly: result.instanceOnly,
|
|
972
|
-
source: 'eventkit',
|
|
973
|
-
message: result.message,
|
|
974
|
-
}, null, 2),
|
|
975
|
-
},
|
|
976
|
-
],
|
|
977
|
-
};
|
|
978
|
-
}
|
|
979
|
-
// Handle skipped or failed response
|
|
980
|
-
return {
|
|
981
|
-
content: [
|
|
982
|
-
{
|
|
983
|
-
type: "text",
|
|
984
|
-
text: JSON.stringify({
|
|
985
|
-
success: false,
|
|
986
|
-
eventId: result.eventId,
|
|
987
|
-
eventTitle: result.eventTitle,
|
|
988
|
-
skipped: result.skipped,
|
|
989
|
-
reason: result.reason,
|
|
990
|
-
error: result.error,
|
|
991
|
-
source: 'eventkit',
|
|
992
|
-
message: result.skipped
|
|
993
|
-
? `イベントをスキップしました: ${result.reason}`
|
|
994
|
-
: `イベント返信に失敗しました: ${result.error}`,
|
|
995
|
-
}, null, 2),
|
|
996
|
-
},
|
|
997
|
-
],
|
|
998
|
-
};
|
|
999
|
-
}
|
|
1000
|
-
// Should not reach here, but just in case
|
|
1001
|
-
return {
|
|
1002
|
-
content: [
|
|
1003
|
-
{
|
|
1004
|
-
type: "text",
|
|
1005
|
-
text: JSON.stringify({
|
|
1006
|
-
error: true,
|
|
1007
|
-
message: "有効なカレンダーソースが見つかりません。",
|
|
1008
|
-
}, null, 2),
|
|
1009
|
-
},
|
|
1010
|
-
],
|
|
1011
|
-
};
|
|
1012
|
-
}
|
|
1013
|
-
catch (error) {
|
|
1014
|
-
return {
|
|
1015
|
-
content: [
|
|
1016
|
-
{
|
|
1017
|
-
type: "text",
|
|
1018
|
-
text: JSON.stringify({
|
|
1019
|
-
error: true,
|
|
1020
|
-
message: `カレンダーイベント返信に失敗しました: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
1021
|
-
}, null, 2),
|
|
1022
|
-
},
|
|
1023
|
-
],
|
|
1024
|
-
};
|
|
1025
|
-
}
|
|
1026
|
-
});
|
|
1027
|
-
/**
|
|
1028
|
-
* respond_to_calendar_events_batch - Respond to multiple calendar events
|
|
1029
|
-
* Requirement: 17.3, 17.4, 17.12
|
|
1030
|
-
*/
|
|
291
|
+
}, async ({ eventId, response, comment, source, calendarId }) => handleRespondToCalendarEvent(createCalendarToolsContext(), {
|
|
292
|
+
eventId,
|
|
293
|
+
response,
|
|
294
|
+
comment,
|
|
295
|
+
source,
|
|
296
|
+
calendarId,
|
|
297
|
+
}));
|
|
298
|
+
// respond_to_calendar_events_batch - uses extracted handler
|
|
1031
299
|
server.tool("respond_to_calendar_events_batch", "Respond to multiple calendar events at once. Useful for declining all events during vacation or leave periods.", {
|
|
1032
300
|
eventIds: z
|
|
1033
301
|
.array(z.string())
|
|
@@ -1039,81 +307,12 @@ async function createServer() {
|
|
|
1039
307
|
.string()
|
|
1040
308
|
.optional()
|
|
1041
309
|
.describe("Optional comment to include with all responses (e.g., '年末年始休暇のため')"),
|
|
1042
|
-
}, async ({ eventIds, response, comment }) => {
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
text: JSON.stringify({
|
|
1049
|
-
error: true,
|
|
1050
|
-
message: "sageが設定されていません。check_setup_statusを実行してください。",
|
|
1051
|
-
}, null, 2),
|
|
1052
|
-
},
|
|
1053
|
-
],
|
|
1054
|
-
};
|
|
1055
|
-
}
|
|
1056
|
-
if (!calendarEventResponseService) {
|
|
1057
|
-
initializeServices(config);
|
|
1058
|
-
}
|
|
1059
|
-
try {
|
|
1060
|
-
// Check platform availability
|
|
1061
|
-
const isAvailable = await calendarEventResponseService.isEventKitAvailable();
|
|
1062
|
-
if (!isAvailable) {
|
|
1063
|
-
return {
|
|
1064
|
-
content: [
|
|
1065
|
-
{
|
|
1066
|
-
type: "text",
|
|
1067
|
-
text: JSON.stringify({
|
|
1068
|
-
success: false,
|
|
1069
|
-
message: "カレンダーイベント返信機能はmacOSでのみ利用可能です。",
|
|
1070
|
-
}, null, 2),
|
|
1071
|
-
},
|
|
1072
|
-
],
|
|
1073
|
-
};
|
|
1074
|
-
}
|
|
1075
|
-
// Respond to all events in batch
|
|
1076
|
-
const result = await calendarEventResponseService.respondToEventsBatch({
|
|
1077
|
-
eventIds,
|
|
1078
|
-
response,
|
|
1079
|
-
comment,
|
|
1080
|
-
});
|
|
1081
|
-
return {
|
|
1082
|
-
content: [
|
|
1083
|
-
{
|
|
1084
|
-
type: "text",
|
|
1085
|
-
text: JSON.stringify({
|
|
1086
|
-
success: result.success,
|
|
1087
|
-
summary: result.summary,
|
|
1088
|
-
details: {
|
|
1089
|
-
succeeded: result.details.succeeded,
|
|
1090
|
-
skipped: result.details.skipped,
|
|
1091
|
-
failed: result.details.failed,
|
|
1092
|
-
},
|
|
1093
|
-
message: result.message,
|
|
1094
|
-
}, null, 2),
|
|
1095
|
-
},
|
|
1096
|
-
],
|
|
1097
|
-
};
|
|
1098
|
-
}
|
|
1099
|
-
catch (error) {
|
|
1100
|
-
return {
|
|
1101
|
-
content: [
|
|
1102
|
-
{
|
|
1103
|
-
type: "text",
|
|
1104
|
-
text: JSON.stringify({
|
|
1105
|
-
error: true,
|
|
1106
|
-
message: `カレンダーイベント一括返信に失敗しました: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
1107
|
-
}, null, 2),
|
|
1108
|
-
},
|
|
1109
|
-
],
|
|
1110
|
-
};
|
|
1111
|
-
}
|
|
1112
|
-
});
|
|
1113
|
-
/**
|
|
1114
|
-
* create_calendar_event - Create a new calendar event
|
|
1115
|
-
* Requirement: 18.1-18.11, Task 29 (Multi-source support)
|
|
1116
|
-
*/
|
|
310
|
+
}, async ({ eventIds, response, comment }) => handleRespondToCalendarEventsBatch(createCalendarToolsContext(), {
|
|
311
|
+
eventIds,
|
|
312
|
+
response,
|
|
313
|
+
comment,
|
|
314
|
+
}));
|
|
315
|
+
// create_calendar_event - uses extracted handler
|
|
1117
316
|
server.tool("create_calendar_event", "Create a new calendar event in the appropriate calendar source with optional location, notes, and alarms.", {
|
|
1118
317
|
title: z.string().describe("Event title"),
|
|
1119
318
|
startDate: z
|
|
@@ -1136,258 +335,33 @@ async function createServer() {
|
|
|
1136
335
|
.enum(['eventkit', 'google'])
|
|
1137
336
|
.optional()
|
|
1138
337
|
.describe("Preferred calendar source to create the event in. If not specified, uses the first enabled source."),
|
|
1139
|
-
}, async ({ title, startDate, endDate, location, notes, calendarName
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
],
|
|
1151
|
-
};
|
|
1152
|
-
}
|
|
1153
|
-
if (!calendarSourceManager) {
|
|
1154
|
-
initializeServices(config);
|
|
1155
|
-
}
|
|
1156
|
-
try {
|
|
1157
|
-
// Get enabled sources
|
|
1158
|
-
const enabledSources = calendarSourceManager.getEnabledSources();
|
|
1159
|
-
if (enabledSources.length === 0) {
|
|
1160
|
-
return {
|
|
1161
|
-
content: [
|
|
1162
|
-
{
|
|
1163
|
-
type: "text",
|
|
1164
|
-
text: JSON.stringify({
|
|
1165
|
-
success: false,
|
|
1166
|
-
message: "有効なカレンダーソースがありません。設定でEventKitまたはGoogle Calendarを有効にしてください。",
|
|
1167
|
-
}, null, 2),
|
|
1168
|
-
},
|
|
1169
|
-
],
|
|
1170
|
-
};
|
|
1171
|
-
}
|
|
1172
|
-
// Build create event request
|
|
1173
|
-
const request = {
|
|
1174
|
-
title,
|
|
1175
|
-
start: startDate,
|
|
1176
|
-
end: endDate,
|
|
1177
|
-
location,
|
|
1178
|
-
description: notes,
|
|
1179
|
-
// Note: calendarId is passed separately to GoogleCalendarService.createEvent
|
|
1180
|
-
// alarms parameter is not supported in CreateEventRequest interface (uses reminders instead)
|
|
1181
|
-
};
|
|
1182
|
-
// Create the event using CalendarSourceManager
|
|
1183
|
-
// This automatically routes to the preferred source or falls back to other enabled sources
|
|
1184
|
-
const event = await calendarSourceManager.createEvent(request, preferredSource);
|
|
1185
|
-
return {
|
|
1186
|
-
content: [
|
|
1187
|
-
{
|
|
1188
|
-
type: "text",
|
|
1189
|
-
text: JSON.stringify({
|
|
1190
|
-
success: true,
|
|
1191
|
-
eventId: event.id,
|
|
1192
|
-
title: event.title,
|
|
1193
|
-
startDate: event.start,
|
|
1194
|
-
endDate: event.end,
|
|
1195
|
-
source: event.source || 'unknown',
|
|
1196
|
-
calendarName: event.calendar,
|
|
1197
|
-
isAllDay: event.isAllDay,
|
|
1198
|
-
message: `カレンダーイベントを作成しました: ${event.title} (ソース: ${event.source || 'unknown'})`,
|
|
1199
|
-
}, null, 2),
|
|
1200
|
-
},
|
|
1201
|
-
],
|
|
1202
|
-
};
|
|
1203
|
-
}
|
|
1204
|
-
catch (error) {
|
|
1205
|
-
return {
|
|
1206
|
-
content: [
|
|
1207
|
-
{
|
|
1208
|
-
type: "text",
|
|
1209
|
-
text: JSON.stringify({
|
|
1210
|
-
error: true,
|
|
1211
|
-
message: `カレンダーイベント作成に失敗しました: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
1212
|
-
}, null, 2),
|
|
1213
|
-
},
|
|
1214
|
-
],
|
|
1215
|
-
};
|
|
1216
|
-
}
|
|
1217
|
-
});
|
|
1218
|
-
/**
|
|
1219
|
-
* delete_calendar_event - Delete a calendar event
|
|
1220
|
-
* Requirement: 19.1-19.9, Task 30 (Multi-source support)
|
|
1221
|
-
*/
|
|
338
|
+
}, async ({ title, startDate, endDate, location, notes, calendarName, alarms, preferredSource }) => handleCreateCalendarEvent(createCalendarToolsContext(), {
|
|
339
|
+
title,
|
|
340
|
+
startDate,
|
|
341
|
+
endDate,
|
|
342
|
+
location,
|
|
343
|
+
notes,
|
|
344
|
+
calendarName,
|
|
345
|
+
alarms,
|
|
346
|
+
preferredSource,
|
|
347
|
+
}));
|
|
348
|
+
// delete_calendar_event - uses extracted handler
|
|
1222
349
|
server.tool("delete_calendar_event", "Delete a calendar event from enabled calendar sources by its ID. If source not specified, attempts deletion from all enabled sources.", {
|
|
1223
350
|
eventId: z.string().describe("Event ID (UUID or full ID from list_calendar_events)"),
|
|
1224
351
|
source: z
|
|
1225
352
|
.enum(['eventkit', 'google'])
|
|
1226
353
|
.optional()
|
|
1227
354
|
.describe("Calendar source to delete from. If not specified, attempts deletion from all enabled sources."),
|
|
1228
|
-
}, async ({ eventId, source }) => {
|
|
1229
|
-
|
|
1230
|
-
return {
|
|
1231
|
-
content: [
|
|
1232
|
-
{
|
|
1233
|
-
type: "text",
|
|
1234
|
-
text: JSON.stringify({
|
|
1235
|
-
error: true,
|
|
1236
|
-
message: "sageが設定されていません。check_setup_statusを実行してください。",
|
|
1237
|
-
}, null, 2),
|
|
1238
|
-
},
|
|
1239
|
-
],
|
|
1240
|
-
};
|
|
1241
|
-
}
|
|
1242
|
-
if (!calendarSourceManager) {
|
|
1243
|
-
initializeServices(config);
|
|
1244
|
-
}
|
|
1245
|
-
try {
|
|
1246
|
-
// Get enabled sources
|
|
1247
|
-
const enabledSources = calendarSourceManager.getEnabledSources();
|
|
1248
|
-
if (enabledSources.length === 0) {
|
|
1249
|
-
return {
|
|
1250
|
-
content: [
|
|
1251
|
-
{
|
|
1252
|
-
type: "text",
|
|
1253
|
-
text: JSON.stringify({
|
|
1254
|
-
success: false,
|
|
1255
|
-
message: "有効なカレンダーソースがありません。設定でEventKitまたはGoogle Calendarを有効にしてください。",
|
|
1256
|
-
}, null, 2),
|
|
1257
|
-
},
|
|
1258
|
-
],
|
|
1259
|
-
};
|
|
1260
|
-
}
|
|
1261
|
-
// Delete the event using CalendarSourceManager
|
|
1262
|
-
// This automatically handles deletion from specified source or all enabled sources
|
|
1263
|
-
await calendarSourceManager.deleteEvent(eventId, source);
|
|
1264
|
-
return {
|
|
1265
|
-
content: [
|
|
1266
|
-
{
|
|
1267
|
-
type: "text",
|
|
1268
|
-
text: JSON.stringify({
|
|
1269
|
-
success: true,
|
|
1270
|
-
eventId,
|
|
1271
|
-
source: source || 'all enabled sources',
|
|
1272
|
-
message: source
|
|
1273
|
-
? `カレンダーイベントを削除しました (ソース: ${source})`
|
|
1274
|
-
: `カレンダーイベントを削除しました (全ての有効なソースから)`,
|
|
1275
|
-
}, null, 2),
|
|
1276
|
-
},
|
|
1277
|
-
],
|
|
1278
|
-
};
|
|
1279
|
-
}
|
|
1280
|
-
catch (error) {
|
|
1281
|
-
return {
|
|
1282
|
-
content: [
|
|
1283
|
-
{
|
|
1284
|
-
type: "text",
|
|
1285
|
-
text: JSON.stringify({
|
|
1286
|
-
error: true,
|
|
1287
|
-
message: `カレンダーイベント削除に失敗しました: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
1288
|
-
}, null, 2),
|
|
1289
|
-
},
|
|
1290
|
-
],
|
|
1291
|
-
};
|
|
1292
|
-
}
|
|
1293
|
-
});
|
|
1294
|
-
/**
|
|
1295
|
-
* delete_calendar_events_batch - Delete multiple calendar events
|
|
1296
|
-
* Requirement: 19.10-19.11, Task 30 (Multi-source support)
|
|
1297
|
-
*/
|
|
355
|
+
}, async ({ eventId, source }) => handleDeleteCalendarEvent(createCalendarToolsContext(), { eventId, source }));
|
|
356
|
+
// delete_calendar_events_batch - uses extracted handler
|
|
1298
357
|
server.tool("delete_calendar_events_batch", "Delete multiple calendar events from enabled calendar sources by their IDs. If source not specified, attempts deletion from all enabled sources.", {
|
|
1299
358
|
eventIds: z.array(z.string()).describe("Array of event IDs to delete"),
|
|
1300
359
|
source: z
|
|
1301
360
|
.enum(['eventkit', 'google'])
|
|
1302
361
|
.optional()
|
|
1303
362
|
.describe("Calendar source to delete from. If not specified, attempts deletion from all enabled sources."),
|
|
1304
|
-
}, async ({ eventIds, source }) => {
|
|
1305
|
-
|
|
1306
|
-
return {
|
|
1307
|
-
content: [
|
|
1308
|
-
{
|
|
1309
|
-
type: "text",
|
|
1310
|
-
text: JSON.stringify({
|
|
1311
|
-
error: true,
|
|
1312
|
-
message: "sageが設定されていません。check_setup_statusを実行してください。",
|
|
1313
|
-
}, null, 2),
|
|
1314
|
-
},
|
|
1315
|
-
],
|
|
1316
|
-
};
|
|
1317
|
-
}
|
|
1318
|
-
if (!calendarSourceManager) {
|
|
1319
|
-
initializeServices(config);
|
|
1320
|
-
}
|
|
1321
|
-
try {
|
|
1322
|
-
// Get enabled sources
|
|
1323
|
-
const enabledSources = calendarSourceManager.getEnabledSources();
|
|
1324
|
-
if (enabledSources.length === 0) {
|
|
1325
|
-
return {
|
|
1326
|
-
content: [
|
|
1327
|
-
{
|
|
1328
|
-
type: "text",
|
|
1329
|
-
text: JSON.stringify({
|
|
1330
|
-
success: false,
|
|
1331
|
-
message: "有効なカレンダーソースがありません。設定でEventKitまたはGoogle Calendarを有効にしてください。",
|
|
1332
|
-
}, null, 2),
|
|
1333
|
-
},
|
|
1334
|
-
],
|
|
1335
|
-
};
|
|
1336
|
-
}
|
|
1337
|
-
// Delete events one by one using CalendarSourceManager
|
|
1338
|
-
const results = [];
|
|
1339
|
-
for (const eventId of eventIds) {
|
|
1340
|
-
try {
|
|
1341
|
-
await calendarSourceManager.deleteEvent(eventId, source);
|
|
1342
|
-
results.push({ eventId, success: true });
|
|
1343
|
-
}
|
|
1344
|
-
catch (error) {
|
|
1345
|
-
results.push({
|
|
1346
|
-
eventId,
|
|
1347
|
-
success: false,
|
|
1348
|
-
error: error instanceof Error ? error.message : 'Unknown error',
|
|
1349
|
-
});
|
|
1350
|
-
}
|
|
1351
|
-
}
|
|
1352
|
-
const successCount = results.filter((r) => r.success).length;
|
|
1353
|
-
const failedCount = results.filter((r) => !r.success).length;
|
|
1354
|
-
return {
|
|
1355
|
-
content: [
|
|
1356
|
-
{
|
|
1357
|
-
type: "text",
|
|
1358
|
-
text: JSON.stringify({
|
|
1359
|
-
success: failedCount === 0,
|
|
1360
|
-
totalCount: eventIds.length,
|
|
1361
|
-
successCount,
|
|
1362
|
-
failedCount,
|
|
1363
|
-
source: source || 'all enabled sources',
|
|
1364
|
-
results,
|
|
1365
|
-
message: source
|
|
1366
|
-
? `${successCount}/${eventIds.length}件のイベントを削除しました (ソース: ${source})`
|
|
1367
|
-
: `${successCount}/${eventIds.length}件のイベントを削除しました (全ての有効なソースから)`,
|
|
1368
|
-
}, null, 2),
|
|
1369
|
-
},
|
|
1370
|
-
],
|
|
1371
|
-
};
|
|
1372
|
-
}
|
|
1373
|
-
catch (error) {
|
|
1374
|
-
return {
|
|
1375
|
-
content: [
|
|
1376
|
-
{
|
|
1377
|
-
type: "text",
|
|
1378
|
-
text: JSON.stringify({
|
|
1379
|
-
error: true,
|
|
1380
|
-
message: `カレンダーイベント一括削除に失敗しました: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
1381
|
-
}, null, 2),
|
|
1382
|
-
},
|
|
1383
|
-
],
|
|
1384
|
-
};
|
|
1385
|
-
}
|
|
1386
|
-
});
|
|
1387
|
-
/**
|
|
1388
|
-
* sync_to_notion - Sync a task to Notion
|
|
1389
|
-
* Requirement: 8.1-8.5
|
|
1390
|
-
*/
|
|
363
|
+
}, async ({ eventIds, source }) => handleDeleteCalendarEventsBatch(createCalendarToolsContext(), { eventIds, source }));
|
|
364
|
+
// sync_to_notion - uses extracted handler
|
|
1391
365
|
server.tool("sync_to_notion", "Sync a task to Notion database for long-term tracking.", {
|
|
1392
366
|
taskTitle: z.string().describe("Title of the task"),
|
|
1393
367
|
description: z.string().optional().describe("Task description"),
|
|
@@ -1404,143 +378,15 @@ async function createServer() {
|
|
|
1404
378
|
.number()
|
|
1405
379
|
.optional()
|
|
1406
380
|
.describe("Estimated duration in minutes"),
|
|
1407
|
-
}, async ({ taskTitle, description, priority, dueDate, stakeholders, estimatedMinutes
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
}, null, 2),
|
|
1417
|
-
},
|
|
1418
|
-
],
|
|
1419
|
-
};
|
|
1420
|
-
}
|
|
1421
|
-
if (!config.integrations.notion.enabled) {
|
|
1422
|
-
return {
|
|
1423
|
-
content: [
|
|
1424
|
-
{
|
|
1425
|
-
type: "text",
|
|
1426
|
-
text: JSON.stringify({
|
|
1427
|
-
error: true,
|
|
1428
|
-
message: "Notion統合が有効になっていません。update_configでNotion設定を更新してください。",
|
|
1429
|
-
}, null, 2),
|
|
1430
|
-
},
|
|
1431
|
-
],
|
|
1432
|
-
};
|
|
1433
|
-
}
|
|
1434
|
-
if (!notionService) {
|
|
1435
|
-
initializeServices(config);
|
|
1436
|
-
}
|
|
1437
|
-
try {
|
|
1438
|
-
// Check if Notion MCP is available
|
|
1439
|
-
const isAvailable = await notionService.isAvailable();
|
|
1440
|
-
// Build properties for Notion page
|
|
1441
|
-
const properties = notionService.buildNotionProperties({
|
|
1442
|
-
title: taskTitle,
|
|
1443
|
-
priority,
|
|
1444
|
-
deadline: dueDate,
|
|
1445
|
-
stakeholders,
|
|
1446
|
-
estimatedMinutes,
|
|
1447
|
-
description,
|
|
1448
|
-
});
|
|
1449
|
-
if (!isAvailable) {
|
|
1450
|
-
// Generate fallback template for manual copy
|
|
1451
|
-
const fallbackText = notionService.generateFallbackTemplate({
|
|
1452
|
-
title: taskTitle,
|
|
1453
|
-
priority,
|
|
1454
|
-
deadline: dueDate,
|
|
1455
|
-
stakeholders,
|
|
1456
|
-
estimatedMinutes,
|
|
1457
|
-
description,
|
|
1458
|
-
});
|
|
1459
|
-
return {
|
|
1460
|
-
content: [
|
|
1461
|
-
{
|
|
1462
|
-
type: "text",
|
|
1463
|
-
text: JSON.stringify({
|
|
1464
|
-
success: false,
|
|
1465
|
-
method: "fallback",
|
|
1466
|
-
message: "Notion MCP統合が利用できません。以下のテンプレートを手動でNotionにコピーしてください。",
|
|
1467
|
-
fallbackText,
|
|
1468
|
-
task: {
|
|
1469
|
-
taskTitle,
|
|
1470
|
-
priority: priority ?? "P3",
|
|
1471
|
-
dueDate,
|
|
1472
|
-
stakeholders: stakeholders ?? [],
|
|
1473
|
-
estimatedMinutes,
|
|
1474
|
-
},
|
|
1475
|
-
}, null, 2),
|
|
1476
|
-
},
|
|
1477
|
-
],
|
|
1478
|
-
};
|
|
1479
|
-
}
|
|
1480
|
-
// Create page in Notion via MCP
|
|
1481
|
-
const result = await notionService.createPage({
|
|
1482
|
-
databaseId: config.integrations.notion.databaseId,
|
|
1483
|
-
title: taskTitle,
|
|
1484
|
-
properties,
|
|
1485
|
-
});
|
|
1486
|
-
if (result.success) {
|
|
1487
|
-
return {
|
|
1488
|
-
content: [
|
|
1489
|
-
{
|
|
1490
|
-
type: "text",
|
|
1491
|
-
text: JSON.stringify({
|
|
1492
|
-
success: true,
|
|
1493
|
-
method: "mcp",
|
|
1494
|
-
pageId: result.pageId,
|
|
1495
|
-
pageUrl: result.pageUrl,
|
|
1496
|
-
message: `Notionにタスクを同期しました: ${taskTitle}`,
|
|
1497
|
-
}, null, 2),
|
|
1498
|
-
},
|
|
1499
|
-
],
|
|
1500
|
-
};
|
|
1501
|
-
}
|
|
1502
|
-
// MCP call failed, provide fallback
|
|
1503
|
-
const fallbackText = notionService.generateFallbackTemplate({
|
|
1504
|
-
title: taskTitle,
|
|
1505
|
-
priority,
|
|
1506
|
-
deadline: dueDate,
|
|
1507
|
-
stakeholders,
|
|
1508
|
-
estimatedMinutes,
|
|
1509
|
-
description,
|
|
1510
|
-
});
|
|
1511
|
-
return {
|
|
1512
|
-
content: [
|
|
1513
|
-
{
|
|
1514
|
-
type: "text",
|
|
1515
|
-
text: JSON.stringify({
|
|
1516
|
-
success: false,
|
|
1517
|
-
method: "fallback",
|
|
1518
|
-
error: result.error,
|
|
1519
|
-
message: "Notion MCP呼び出しに失敗しました。以下のテンプレートを手動でコピーしてください。",
|
|
1520
|
-
fallbackText,
|
|
1521
|
-
}, null, 2),
|
|
1522
|
-
},
|
|
1523
|
-
],
|
|
1524
|
-
};
|
|
1525
|
-
}
|
|
1526
|
-
catch (error) {
|
|
1527
|
-
return {
|
|
1528
|
-
content: [
|
|
1529
|
-
{
|
|
1530
|
-
type: "text",
|
|
1531
|
-
text: JSON.stringify({
|
|
1532
|
-
error: true,
|
|
1533
|
-
message: `Notion同期に失敗しました: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
1534
|
-
}, null, 2),
|
|
1535
|
-
},
|
|
1536
|
-
],
|
|
1537
|
-
};
|
|
1538
|
-
}
|
|
1539
|
-
});
|
|
1540
|
-
/**
|
|
1541
|
-
* update_config - Update configuration
|
|
1542
|
-
* Requirement: 10.1-10.6
|
|
1543
|
-
*/
|
|
381
|
+
}, async ({ taskTitle, description, priority, dueDate, stakeholders, estimatedMinutes }) => handleSyncToNotion(createIntegrationToolsContext(), {
|
|
382
|
+
taskTitle,
|
|
383
|
+
description,
|
|
384
|
+
priority,
|
|
385
|
+
dueDate,
|
|
386
|
+
stakeholders,
|
|
387
|
+
estimatedMinutes,
|
|
388
|
+
}));
|
|
389
|
+
// update_config - uses extracted handler
|
|
1544
390
|
server.tool("update_config", "Update sage configuration settings.", {
|
|
1545
391
|
section: z
|
|
1546
392
|
.enum([
|
|
@@ -1553,81 +399,13 @@ async function createServer() {
|
|
|
1553
399
|
])
|
|
1554
400
|
.describe("Configuration section to update"),
|
|
1555
401
|
updates: z.record(z.unknown()).describe("Key-value pairs to update"),
|
|
1556
|
-
}, async ({ section, updates }) => {
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
{
|
|
1561
|
-
type: "text",
|
|
1562
|
-
text: JSON.stringify({
|
|
1563
|
-
error: true,
|
|
1564
|
-
message: "sageが設定されていません。check_setup_statusを実行してください。",
|
|
1565
|
-
}, null, 2),
|
|
1566
|
-
},
|
|
1567
|
-
],
|
|
1568
|
-
};
|
|
1569
|
-
}
|
|
1570
|
-
try {
|
|
1571
|
-
// Validate section-specific updates
|
|
1572
|
-
const validationResult = validateConfigUpdate(section, updates);
|
|
1573
|
-
if (!validationResult.valid) {
|
|
1574
|
-
return {
|
|
1575
|
-
content: [
|
|
1576
|
-
{
|
|
1577
|
-
type: "text",
|
|
1578
|
-
text: JSON.stringify({
|
|
1579
|
-
error: true,
|
|
1580
|
-
message: `設定の検証に失敗しました: ${validationResult.error}`,
|
|
1581
|
-
invalidFields: validationResult.invalidFields,
|
|
1582
|
-
}, null, 2),
|
|
1583
|
-
},
|
|
1584
|
-
],
|
|
1585
|
-
};
|
|
1586
|
-
}
|
|
1587
|
-
// Apply updates to config
|
|
1588
|
-
const updatedConfig = applyConfigUpdates(config, section, updates);
|
|
1589
|
-
// Save the updated config
|
|
1590
|
-
await ConfigLoader.save(updatedConfig);
|
|
1591
|
-
config = updatedConfig;
|
|
1592
|
-
// Re-initialize services if integrations changed
|
|
1593
|
-
if (section === "integrations") {
|
|
1594
|
-
initializeServices(config);
|
|
1595
|
-
}
|
|
1596
|
-
return {
|
|
1597
|
-
content: [
|
|
1598
|
-
{
|
|
1599
|
-
type: "text",
|
|
1600
|
-
text: JSON.stringify({
|
|
1601
|
-
success: true,
|
|
1602
|
-
section,
|
|
1603
|
-
updatedFields: Object.keys(updates),
|
|
1604
|
-
message: `設定を更新しました: ${section}`,
|
|
1605
|
-
}, null, 2),
|
|
1606
|
-
},
|
|
1607
|
-
],
|
|
1608
|
-
};
|
|
1609
|
-
}
|
|
1610
|
-
catch (error) {
|
|
1611
|
-
return {
|
|
1612
|
-
content: [
|
|
1613
|
-
{
|
|
1614
|
-
type: "text",
|
|
1615
|
-
text: JSON.stringify({
|
|
1616
|
-
error: true,
|
|
1617
|
-
message: `設定の更新に失敗しました: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
1618
|
-
}, null, 2),
|
|
1619
|
-
},
|
|
1620
|
-
],
|
|
1621
|
-
};
|
|
1622
|
-
}
|
|
1623
|
-
});
|
|
402
|
+
}, async ({ section, updates }) => handleUpdateConfig(createIntegrationToolsContext(), {
|
|
403
|
+
section,
|
|
404
|
+
updates: updates,
|
|
405
|
+
}));
|
|
1624
406
|
// ============================================
|
|
1625
|
-
// TODO List Management Tools
|
|
407
|
+
// TODO List Management Tools - uses extracted handlers
|
|
1626
408
|
// ============================================
|
|
1627
|
-
/**
|
|
1628
|
-
* list_todos - List all TODO items with optional filtering
|
|
1629
|
-
* Requirement: 12.1, 12.2, 12.3, 12.4, 12.7, 12.8
|
|
1630
|
-
*/
|
|
1631
409
|
server.tool("list_todos", "List TODO items from Apple Reminders and Notion with optional filtering.", {
|
|
1632
410
|
priority: z
|
|
1633
411
|
.array(z.enum(["P0", "P1", "P2", "P3"]))
|
|
@@ -1642,90 +420,15 @@ async function createServer() {
|
|
|
1642
420
|
.optional()
|
|
1643
421
|
.describe("Filter by source"),
|
|
1644
422
|
todayOnly: z.boolean().optional().describe("Show only tasks due today"),
|
|
1645
|
-
tags: z.array(z.string()).optional().describe("Filter by tags"),
|
|
1646
|
-
}, async ({ priority, status, source, todayOnly, tags }) => {
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
message: "sageが設定されていません。check_setup_statusを実行してください。",
|
|
1655
|
-
}, null, 2),
|
|
1656
|
-
},
|
|
1657
|
-
],
|
|
1658
|
-
};
|
|
1659
|
-
}
|
|
1660
|
-
if (!todoListManager) {
|
|
1661
|
-
initializeServices(config);
|
|
1662
|
-
}
|
|
1663
|
-
try {
|
|
1664
|
-
let todos;
|
|
1665
|
-
if (todayOnly) {
|
|
1666
|
-
todos = await todoListManager.getTodaysTasks();
|
|
1667
|
-
}
|
|
1668
|
-
else {
|
|
1669
|
-
todos = await todoListManager.listTodos({
|
|
1670
|
-
priority: priority,
|
|
1671
|
-
status,
|
|
1672
|
-
source,
|
|
1673
|
-
tags,
|
|
1674
|
-
});
|
|
1675
|
-
}
|
|
1676
|
-
// Format todos for display
|
|
1677
|
-
const formattedTodos = todos.map((todo) => ({
|
|
1678
|
-
id: todo.id,
|
|
1679
|
-
title: todo.title,
|
|
1680
|
-
priority: todo.priority,
|
|
1681
|
-
status: todo.status,
|
|
1682
|
-
dueDate: todo.dueDate,
|
|
1683
|
-
source: todo.source,
|
|
1684
|
-
tags: todo.tags,
|
|
1685
|
-
estimatedMinutes: todo.estimatedMinutes,
|
|
1686
|
-
stakeholders: todo.stakeholders,
|
|
1687
|
-
}));
|
|
1688
|
-
return {
|
|
1689
|
-
content: [
|
|
1690
|
-
{
|
|
1691
|
-
type: "text",
|
|
1692
|
-
text: JSON.stringify({
|
|
1693
|
-
success: true,
|
|
1694
|
-
totalCount: todos.length,
|
|
1695
|
-
todos: formattedTodos,
|
|
1696
|
-
message: todos.length > 0
|
|
1697
|
-
? `${todos.length}件のタスクが見つかりました。`
|
|
1698
|
-
: "タスクが見つかりませんでした。",
|
|
1699
|
-
filters: {
|
|
1700
|
-
priority,
|
|
1701
|
-
status,
|
|
1702
|
-
source,
|
|
1703
|
-
todayOnly,
|
|
1704
|
-
tags,
|
|
1705
|
-
},
|
|
1706
|
-
}, null, 2),
|
|
1707
|
-
},
|
|
1708
|
-
],
|
|
1709
|
-
};
|
|
1710
|
-
}
|
|
1711
|
-
catch (error) {
|
|
1712
|
-
return {
|
|
1713
|
-
content: [
|
|
1714
|
-
{
|
|
1715
|
-
type: "text",
|
|
1716
|
-
text: JSON.stringify({
|
|
1717
|
-
error: true,
|
|
1718
|
-
message: `TODOリストの取得に失敗しました: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
1719
|
-
}, null, 2),
|
|
1720
|
-
},
|
|
1721
|
-
],
|
|
1722
|
-
};
|
|
1723
|
-
}
|
|
1724
|
-
});
|
|
1725
|
-
/**
|
|
1726
|
-
* update_task_status - Update the status of a task
|
|
1727
|
-
* Requirement: 12.5, 12.6
|
|
1728
|
-
*/
|
|
423
|
+
tags: z.array(z.string()).optional().describe("Filter by tags"),
|
|
424
|
+
}, async ({ priority, status, source, todayOnly, tags }) => handleListTodos(createReminderTodoContext(), {
|
|
425
|
+
priority,
|
|
426
|
+
status,
|
|
427
|
+
source,
|
|
428
|
+
todayOnly,
|
|
429
|
+
tags,
|
|
430
|
+
}));
|
|
431
|
+
// update_task_status - uses extracted handler
|
|
1729
432
|
server.tool("update_task_status", "Update the status of a task in Apple Reminders or Notion.", {
|
|
1730
433
|
taskId: z.string().describe("ID of the task to update"),
|
|
1731
434
|
status: z
|
|
@@ -1738,290 +441,23 @@ async function createServer() {
|
|
|
1738
441
|
.boolean()
|
|
1739
442
|
.optional()
|
|
1740
443
|
.describe("Whether to sync the status across all sources"),
|
|
1741
|
-
}, async ({ taskId, status, source, syncAcrossSources }) => {
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
}, null, 2),
|
|
1751
|
-
},
|
|
1752
|
-
],
|
|
1753
|
-
};
|
|
1754
|
-
}
|
|
1755
|
-
if (!todoListManager) {
|
|
1756
|
-
initializeServices(config);
|
|
1757
|
-
}
|
|
1758
|
-
try {
|
|
1759
|
-
// Update the task status
|
|
1760
|
-
const result = await todoListManager.updateTaskStatus(taskId, status, source);
|
|
1761
|
-
if (!result.success) {
|
|
1762
|
-
return {
|
|
1763
|
-
content: [
|
|
1764
|
-
{
|
|
1765
|
-
type: "text",
|
|
1766
|
-
text: JSON.stringify({
|
|
1767
|
-
success: false,
|
|
1768
|
-
taskId,
|
|
1769
|
-
error: result.error,
|
|
1770
|
-
message: `タスクステータスの更新に失敗しました: ${result.error}`,
|
|
1771
|
-
}, null, 2),
|
|
1772
|
-
},
|
|
1773
|
-
],
|
|
1774
|
-
};
|
|
1775
|
-
}
|
|
1776
|
-
// Optionally sync across sources
|
|
1777
|
-
let syncResult;
|
|
1778
|
-
if (syncAcrossSources) {
|
|
1779
|
-
syncResult = await todoListManager.syncTaskAcrossSources(taskId);
|
|
1780
|
-
}
|
|
1781
|
-
return {
|
|
1782
|
-
content: [
|
|
1783
|
-
{
|
|
1784
|
-
type: "text",
|
|
1785
|
-
text: JSON.stringify({
|
|
1786
|
-
success: true,
|
|
1787
|
-
taskId,
|
|
1788
|
-
newStatus: status,
|
|
1789
|
-
updatedFields: result.updatedFields,
|
|
1790
|
-
syncedSources: result.syncedSources,
|
|
1791
|
-
syncResult: syncAcrossSources ? syncResult : undefined,
|
|
1792
|
-
message: `タスクのステータスを「${status}」に更新しました。`,
|
|
1793
|
-
}, null, 2),
|
|
1794
|
-
},
|
|
1795
|
-
],
|
|
1796
|
-
};
|
|
1797
|
-
}
|
|
1798
|
-
catch (error) {
|
|
1799
|
-
return {
|
|
1800
|
-
content: [
|
|
1801
|
-
{
|
|
1802
|
-
type: "text",
|
|
1803
|
-
text: JSON.stringify({
|
|
1804
|
-
error: true,
|
|
1805
|
-
message: `タスクステータスの更新に失敗しました: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
1806
|
-
}, null, 2),
|
|
1807
|
-
},
|
|
1808
|
-
],
|
|
1809
|
-
};
|
|
1810
|
-
}
|
|
1811
|
-
});
|
|
1812
|
-
/**
|
|
1813
|
-
* sync_tasks - Sync tasks across all sources
|
|
1814
|
-
* Requirement: 12.6
|
|
1815
|
-
*/
|
|
1816
|
-
server.tool("sync_tasks", "Synchronize tasks between Apple Reminders and Notion, detecting and resolving conflicts.", {}, async () => {
|
|
1817
|
-
if (!config) {
|
|
1818
|
-
return {
|
|
1819
|
-
content: [
|
|
1820
|
-
{
|
|
1821
|
-
type: "text",
|
|
1822
|
-
text: JSON.stringify({
|
|
1823
|
-
error: true,
|
|
1824
|
-
message: "sageが設定されていません。check_setup_statusを実行してください。",
|
|
1825
|
-
}, null, 2),
|
|
1826
|
-
},
|
|
1827
|
-
],
|
|
1828
|
-
};
|
|
1829
|
-
}
|
|
1830
|
-
if (!taskSynchronizer) {
|
|
1831
|
-
initializeServices(config);
|
|
1832
|
-
}
|
|
1833
|
-
try {
|
|
1834
|
-
const result = await taskSynchronizer.syncAllTasks();
|
|
1835
|
-
return {
|
|
1836
|
-
content: [
|
|
1837
|
-
{
|
|
1838
|
-
type: "text",
|
|
1839
|
-
text: JSON.stringify({
|
|
1840
|
-
success: true,
|
|
1841
|
-
totalTasks: result.totalTasks,
|
|
1842
|
-
syncedTasks: result.syncedTasks,
|
|
1843
|
-
conflicts: result.conflicts,
|
|
1844
|
-
errors: result.errors,
|
|
1845
|
-
durationMs: result.duration,
|
|
1846
|
-
message: result.conflicts.length > 0
|
|
1847
|
-
? `${result.syncedTasks}件のタスクを同期しました。${result.conflicts.length}件の競合が検出されました。`
|
|
1848
|
-
: `${result.syncedTasks}件のタスクを同期しました。`,
|
|
1849
|
-
}, null, 2),
|
|
1850
|
-
},
|
|
1851
|
-
],
|
|
1852
|
-
};
|
|
1853
|
-
}
|
|
1854
|
-
catch (error) {
|
|
1855
|
-
return {
|
|
1856
|
-
content: [
|
|
1857
|
-
{
|
|
1858
|
-
type: "text",
|
|
1859
|
-
text: JSON.stringify({
|
|
1860
|
-
error: true,
|
|
1861
|
-
message: `タスク同期に失敗しました: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
1862
|
-
}, null, 2),
|
|
1863
|
-
},
|
|
1864
|
-
],
|
|
1865
|
-
};
|
|
1866
|
-
}
|
|
1867
|
-
});
|
|
1868
|
-
/**
|
|
1869
|
-
* detect_duplicates - Detect duplicate tasks across sources
|
|
1870
|
-
* Requirement: 12.5
|
|
1871
|
-
*/
|
|
444
|
+
}, async ({ taskId, status, source, syncAcrossSources }) => handleUpdateTaskStatus(createTaskToolsContext(), {
|
|
445
|
+
taskId,
|
|
446
|
+
status,
|
|
447
|
+
source,
|
|
448
|
+
syncAcrossSources,
|
|
449
|
+
}));
|
|
450
|
+
// sync_tasks - uses extracted handler
|
|
451
|
+
server.tool("sync_tasks", "Synchronize tasks between Apple Reminders and Notion, detecting and resolving conflicts.", {}, async () => handleSyncTasks(createTaskToolsContext()));
|
|
452
|
+
// detect_duplicates - uses extracted handler
|
|
1872
453
|
server.tool("detect_duplicates", "Detect duplicate tasks between Apple Reminders and Notion.", {
|
|
1873
454
|
autoMerge: z
|
|
1874
455
|
.boolean()
|
|
1875
456
|
.optional()
|
|
1876
457
|
.describe("Whether to automatically merge high-confidence duplicates"),
|
|
1877
|
-
}, async ({ autoMerge }) => {
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
content: [
|
|
1881
|
-
{
|
|
1882
|
-
type: "text",
|
|
1883
|
-
text: JSON.stringify({
|
|
1884
|
-
error: true,
|
|
1885
|
-
message: "sageが設定されていません。check_setup_statusを実行してください。",
|
|
1886
|
-
}, null, 2),
|
|
1887
|
-
},
|
|
1888
|
-
],
|
|
1889
|
-
};
|
|
1890
|
-
}
|
|
1891
|
-
if (!taskSynchronizer) {
|
|
1892
|
-
initializeServices(config);
|
|
1893
|
-
}
|
|
1894
|
-
try {
|
|
1895
|
-
const duplicates = await taskSynchronizer.detectDuplicates();
|
|
1896
|
-
// Format duplicates for display
|
|
1897
|
-
const formattedDuplicates = duplicates.map((d) => ({
|
|
1898
|
-
tasks: d.tasks.map((t) => ({
|
|
1899
|
-
id: t.id,
|
|
1900
|
-
title: t.title,
|
|
1901
|
-
source: t.source,
|
|
1902
|
-
status: t.status,
|
|
1903
|
-
priority: t.priority,
|
|
1904
|
-
})),
|
|
1905
|
-
similarity: Math.round(d.similarity * 100),
|
|
1906
|
-
confidence: d.confidence,
|
|
1907
|
-
suggestedMerge: {
|
|
1908
|
-
title: d.suggestedMerge.title,
|
|
1909
|
-
priority: d.suggestedMerge.priority,
|
|
1910
|
-
status: d.suggestedMerge.status,
|
|
1911
|
-
tags: d.suggestedMerge.tags,
|
|
1912
|
-
},
|
|
1913
|
-
}));
|
|
1914
|
-
// Auto-merge high-confidence duplicates if requested
|
|
1915
|
-
let mergeResults;
|
|
1916
|
-
if (autoMerge) {
|
|
1917
|
-
const highConfidenceDuplicates = duplicates.filter((d) => d.confidence === "high");
|
|
1918
|
-
if (highConfidenceDuplicates.length > 0) {
|
|
1919
|
-
mergeResults = await taskSynchronizer.mergeDuplicates(highConfidenceDuplicates);
|
|
1920
|
-
}
|
|
1921
|
-
}
|
|
1922
|
-
return {
|
|
1923
|
-
content: [
|
|
1924
|
-
{
|
|
1925
|
-
type: "text",
|
|
1926
|
-
text: JSON.stringify({
|
|
1927
|
-
success: true,
|
|
1928
|
-
duplicatesFound: duplicates.length,
|
|
1929
|
-
duplicates: formattedDuplicates,
|
|
1930
|
-
mergeResults: autoMerge ? mergeResults : undefined,
|
|
1931
|
-
message: duplicates.length > 0
|
|
1932
|
-
? `${duplicates.length}件の重複タスクが検出されました。`
|
|
1933
|
-
: "重複タスクは見つかりませんでした。",
|
|
1934
|
-
}, null, 2),
|
|
1935
|
-
},
|
|
1936
|
-
],
|
|
1937
|
-
};
|
|
1938
|
-
}
|
|
1939
|
-
catch (error) {
|
|
1940
|
-
return {
|
|
1941
|
-
content: [
|
|
1942
|
-
{
|
|
1943
|
-
type: "text",
|
|
1944
|
-
text: JSON.stringify({
|
|
1945
|
-
error: true,
|
|
1946
|
-
message: `重複検出に失敗しました: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
1947
|
-
}, null, 2),
|
|
1948
|
-
},
|
|
1949
|
-
],
|
|
1950
|
-
};
|
|
1951
|
-
}
|
|
1952
|
-
});
|
|
1953
|
-
/**
|
|
1954
|
-
* list_calendar_sources - List available and enabled calendar sources
|
|
1955
|
-
* Requirement: Google Calendar API integration (Task 32)
|
|
1956
|
-
*/
|
|
1957
|
-
server.tool("list_calendar_sources", "List available and enabled calendar sources (EventKit, Google Calendar) with their health status. Shows which sources can be used and their current state.", {}, async () => {
|
|
1958
|
-
if (!config) {
|
|
1959
|
-
return {
|
|
1960
|
-
content: [
|
|
1961
|
-
{
|
|
1962
|
-
type: "text",
|
|
1963
|
-
text: JSON.stringify({
|
|
1964
|
-
error: true,
|
|
1965
|
-
message: "sageが設定されていません。check_setup_statusを実行してください。",
|
|
1966
|
-
}, null, 2),
|
|
1967
|
-
},
|
|
1968
|
-
],
|
|
1969
|
-
};
|
|
1970
|
-
}
|
|
1971
|
-
if (!calendarSourceManager) {
|
|
1972
|
-
initializeServices(config);
|
|
1973
|
-
}
|
|
1974
|
-
try {
|
|
1975
|
-
// Get available sources
|
|
1976
|
-
const availableSources = await calendarSourceManager.detectAvailableSources();
|
|
1977
|
-
// Get enabled sources
|
|
1978
|
-
const enabledSources = calendarSourceManager.getEnabledSources();
|
|
1979
|
-
// Get health status
|
|
1980
|
-
const healthStatus = await calendarSourceManager.healthCheck();
|
|
1981
|
-
return {
|
|
1982
|
-
content: [
|
|
1983
|
-
{
|
|
1984
|
-
type: "text",
|
|
1985
|
-
text: JSON.stringify({
|
|
1986
|
-
success: true,
|
|
1987
|
-
sources: {
|
|
1988
|
-
eventkit: {
|
|
1989
|
-
available: availableSources.eventkit,
|
|
1990
|
-
enabled: enabledSources.includes('eventkit'),
|
|
1991
|
-
healthy: healthStatus.eventkit,
|
|
1992
|
-
description: "macOS EventKit calendar integration (macOS only)",
|
|
1993
|
-
},
|
|
1994
|
-
google: {
|
|
1995
|
-
available: availableSources.google,
|
|
1996
|
-
enabled: enabledSources.includes('google'),
|
|
1997
|
-
healthy: healthStatus.google,
|
|
1998
|
-
description: "Google Calendar API integration (all platforms)",
|
|
1999
|
-
},
|
|
2000
|
-
},
|
|
2001
|
-
summary: {
|
|
2002
|
-
totalAvailable: Object.values(availableSources).filter(Boolean).length,
|
|
2003
|
-
totalEnabled: enabledSources.length,
|
|
2004
|
-
allHealthy: Object.values(healthStatus).every(Boolean),
|
|
2005
|
-
},
|
|
2006
|
-
}, null, 2),
|
|
2007
|
-
},
|
|
2008
|
-
],
|
|
2009
|
-
};
|
|
2010
|
-
}
|
|
2011
|
-
catch (error) {
|
|
2012
|
-
return {
|
|
2013
|
-
content: [
|
|
2014
|
-
{
|
|
2015
|
-
type: "text",
|
|
2016
|
-
text: JSON.stringify({
|
|
2017
|
-
error: true,
|
|
2018
|
-
message: `カレンダーソース情報の取得に失敗しました: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
2019
|
-
}, null, 2),
|
|
2020
|
-
},
|
|
2021
|
-
],
|
|
2022
|
-
};
|
|
2023
|
-
}
|
|
2024
|
-
});
|
|
458
|
+
}, async ({ autoMerge }) => handleDetectDuplicates(createTaskToolsContext(), { autoMerge }));
|
|
459
|
+
// list_calendar_sources - uses extracted handler
|
|
460
|
+
server.tool("list_calendar_sources", "List available and enabled calendar sources (EventKit, Google Calendar) with their health status. Shows which sources can be used and their current state.", {}, async () => handleListCalendarSources(createCalendarToolsContext()));
|
|
2025
461
|
/**
|
|
2026
462
|
* set_calendar_source - Enable or disable a calendar source
|
|
2027
463
|
* Requirement: 9, 11, Task 33
|
|
@@ -2185,17 +621,7 @@ async function createServer() {
|
|
|
2185
621
|
}
|
|
2186
622
|
}
|
|
2187
623
|
catch (error) {
|
|
2188
|
-
return
|
|
2189
|
-
content: [
|
|
2190
|
-
{
|
|
2191
|
-
type: "text",
|
|
2192
|
-
text: JSON.stringify({
|
|
2193
|
-
error: true,
|
|
2194
|
-
message: `カレンダーソース設定に失敗しました: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
2195
|
-
}, null, 2),
|
|
2196
|
-
},
|
|
2197
|
-
],
|
|
2198
|
-
};
|
|
624
|
+
return createErrorFromCatch('カレンダーソース設定に失敗しました', error);
|
|
2199
625
|
}
|
|
2200
626
|
});
|
|
2201
627
|
/**
|
|
@@ -2275,17 +701,7 @@ async function createServer() {
|
|
|
2275
701
|
};
|
|
2276
702
|
}
|
|
2277
703
|
catch (error) {
|
|
2278
|
-
return
|
|
2279
|
-
content: [
|
|
2280
|
-
{
|
|
2281
|
-
type: "text",
|
|
2282
|
-
text: JSON.stringify({
|
|
2283
|
-
error: true,
|
|
2284
|
-
message: `カレンダー同期に失敗しました: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
2285
|
-
}, null, 2),
|
|
2286
|
-
},
|
|
2287
|
-
],
|
|
2288
|
-
};
|
|
704
|
+
return createErrorFromCatch('カレンダー同期に失敗しました', error);
|
|
2289
705
|
}
|
|
2290
706
|
});
|
|
2291
707
|
/**
|
|
@@ -2338,23 +754,10 @@ async function createServer() {
|
|
|
2338
754
|
};
|
|
2339
755
|
}
|
|
2340
756
|
catch (error) {
|
|
2341
|
-
return
|
|
2342
|
-
content: [
|
|
2343
|
-
{
|
|
2344
|
-
type: "text",
|
|
2345
|
-
text: JSON.stringify({
|
|
2346
|
-
error: true,
|
|
2347
|
-
message: `同期状態の取得に失敗しました: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
2348
|
-
}, null, 2),
|
|
2349
|
-
},
|
|
2350
|
-
],
|
|
2351
|
-
};
|
|
757
|
+
return createErrorFromCatch('同期状態の取得に失敗しました', error);
|
|
2352
758
|
}
|
|
2353
759
|
});
|
|
2354
|
-
|
|
2355
|
-
* get_working_cadence - Get user's working rhythm information
|
|
2356
|
-
* Requirement: 32.1-32.10
|
|
2357
|
-
*/
|
|
760
|
+
// get_working_cadence - uses extracted handler
|
|
2358
761
|
server.tool("get_working_cadence", "Get user's working rhythm including deep work days, meeting-heavy days, and scheduling recommendations.", {
|
|
2359
762
|
dayOfWeek: z
|
|
2360
763
|
.enum([
|
|
@@ -2372,62 +775,7 @@ async function createServer() {
|
|
|
2372
775
|
.string()
|
|
2373
776
|
.optional()
|
|
2374
777
|
.describe("Get info for a specific date in ISO 8601 format (e.g., 2025-01-15)"),
|
|
2375
|
-
}, async ({ dayOfWeek, date }) => {
|
|
2376
|
-
// Initialize service if not already done
|
|
2377
|
-
if (!workingCadenceService) {
|
|
2378
|
-
workingCadenceService = new WorkingCadenceService();
|
|
2379
|
-
}
|
|
2380
|
-
try {
|
|
2381
|
-
const result = await workingCadenceService.getWorkingCadence({
|
|
2382
|
-
dayOfWeek,
|
|
2383
|
-
date,
|
|
2384
|
-
});
|
|
2385
|
-
if (!result.success) {
|
|
2386
|
-
return {
|
|
2387
|
-
content: [
|
|
2388
|
-
{
|
|
2389
|
-
type: "text",
|
|
2390
|
-
text: JSON.stringify({
|
|
2391
|
-
error: true,
|
|
2392
|
-
message: result.error || "勤務リズム情報の取得に失敗しました。",
|
|
2393
|
-
}, null, 2),
|
|
2394
|
-
},
|
|
2395
|
-
],
|
|
2396
|
-
};
|
|
2397
|
-
}
|
|
2398
|
-
return {
|
|
2399
|
-
content: [
|
|
2400
|
-
{
|
|
2401
|
-
type: "text",
|
|
2402
|
-
text: JSON.stringify({
|
|
2403
|
-
success: true,
|
|
2404
|
-
user: result.user,
|
|
2405
|
-
workingHours: result.workingHours,
|
|
2406
|
-
weeklyPattern: result.weeklyPattern,
|
|
2407
|
-
deepWorkBlocks: result.deepWorkBlocks,
|
|
2408
|
-
weeklyReview: result.weeklyReview,
|
|
2409
|
-
specificDay: result.specificDay,
|
|
2410
|
-
recommendations: result.recommendations,
|
|
2411
|
-
summary: result.summary,
|
|
2412
|
-
}, null, 2),
|
|
2413
|
-
},
|
|
2414
|
-
],
|
|
2415
|
-
};
|
|
2416
|
-
}
|
|
2417
|
-
catch (error) {
|
|
2418
|
-
return {
|
|
2419
|
-
content: [
|
|
2420
|
-
{
|
|
2421
|
-
type: "text",
|
|
2422
|
-
text: JSON.stringify({
|
|
2423
|
-
error: true,
|
|
2424
|
-
message: `勤務リズム情報の取得に失敗しました: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
2425
|
-
}, null, 2),
|
|
2426
|
-
},
|
|
2427
|
-
],
|
|
2428
|
-
};
|
|
2429
|
-
}
|
|
2430
|
-
});
|
|
778
|
+
}, async ({ dayOfWeek, date }) => handleGetWorkingCadence(createCalendarToolsContext(), { dayOfWeek, date }));
|
|
2431
779
|
return server;
|
|
2432
780
|
}
|
|
2433
781
|
/**
|