@shin1ohno/sage 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +168 -0
- package/dist/config/loader.d.ts +37 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/loader.js +95 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/storage/file-storage.d.ts +35 -0
- package/dist/config/storage/file-storage.d.ts.map +1 -0
- package/dist/config/storage/file-storage.js +76 -0
- package/dist/config/storage/file-storage.js.map +1 -0
- package/dist/config/storage/index.d.ts +8 -0
- package/dist/config/storage/index.d.ts.map +1 -0
- package/dist/config/storage/index.js +8 -0
- package/dist/config/storage/index.js.map +1 -0
- package/dist/config/storage/session-storage.d.ts +35 -0
- package/dist/config/storage/session-storage.d.ts.map +1 -0
- package/dist/config/storage/session-storage.js +44 -0
- package/dist/config/storage/session-storage.js.map +1 -0
- package/dist/config/storage/storage-factory.d.ts +32 -0
- package/dist/config/storage/storage-factory.d.ts.map +1 -0
- package/dist/config/storage/storage-factory.js +78 -0
- package/dist/config/storage/storage-factory.js.map +1 -0
- package/dist/core/index.d.ts +6 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +6 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/sage-core.d.ts +80 -0
- package/dist/core/sage-core.d.ts.map +1 -0
- package/dist/core/sage-core.js +190 -0
- package/dist/core/sage-core.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +901 -0
- package/dist/index.js.map +1 -0
- package/dist/integrations/apple-reminders.d.ts +96 -0
- package/dist/integrations/apple-reminders.d.ts.map +1 -0
- package/dist/integrations/apple-reminders.js +250 -0
- package/dist/integrations/apple-reminders.js.map +1 -0
- package/dist/integrations/calendar-service.d.ts +126 -0
- package/dist/integrations/calendar-service.d.ts.map +1 -0
- package/dist/integrations/calendar-service.js +295 -0
- package/dist/integrations/calendar-service.js.map +1 -0
- package/dist/integrations/notion-mcp.d.ts +121 -0
- package/dist/integrations/notion-mcp.d.ts.map +1 -0
- package/dist/integrations/notion-mcp.js +281 -0
- package/dist/integrations/notion-mcp.js.map +1 -0
- package/dist/integrations/reminder-manager.d.ts +90 -0
- package/dist/integrations/reminder-manager.d.ts.map +1 -0
- package/dist/integrations/reminder-manager.js +182 -0
- package/dist/integrations/reminder-manager.js.map +1 -0
- package/dist/platform/adapter-factory.d.ts +22 -0
- package/dist/platform/adapter-factory.d.ts.map +1 -0
- package/dist/platform/adapter-factory.js +41 -0
- package/dist/platform/adapter-factory.js.map +1 -0
- package/dist/platform/adapters/mcp-adapter.d.ts +32 -0
- package/dist/platform/adapters/mcp-adapter.d.ts.map +1 -0
- package/dist/platform/adapters/mcp-adapter.js +52 -0
- package/dist/platform/adapters/mcp-adapter.js.map +1 -0
- package/dist/platform/adapters/skills-adapter-ios.d.ts +38 -0
- package/dist/platform/adapters/skills-adapter-ios.d.ts.map +1 -0
- package/dist/platform/adapters/skills-adapter-ios.js +59 -0
- package/dist/platform/adapters/skills-adapter-ios.js.map +1 -0
- package/dist/platform/adapters/skills-adapter-web.d.ts +36 -0
- package/dist/platform/adapters/skills-adapter-web.d.ts.map +1 -0
- package/dist/platform/adapters/skills-adapter-web.js +56 -0
- package/dist/platform/adapters/skills-adapter-web.js.map +1 -0
- package/dist/platform/detector.d.ts +60 -0
- package/dist/platform/detector.d.ts.map +1 -0
- package/dist/platform/detector.js +217 -0
- package/dist/platform/detector.js.map +1 -0
- package/dist/platform/index.d.ts +11 -0
- package/dist/platform/index.d.ts.map +1 -0
- package/dist/platform/index.js +11 -0
- package/dist/platform/index.js.map +1 -0
- package/dist/platform/types.d.ts +205 -0
- package/dist/platform/types.d.ts.map +1 -0
- package/dist/platform/types.js +33 -0
- package/dist/platform/types.js.map +1 -0
- package/dist/setup/questions.d.ts +15 -0
- package/dist/setup/questions.d.ts.map +1 -0
- package/dist/setup/questions.js +131 -0
- package/dist/setup/questions.js.map +1 -0
- package/dist/setup/wizard.d.ts +51 -0
- package/dist/setup/wizard.d.ts.map +1 -0
- package/dist/setup/wizard.js +210 -0
- package/dist/setup/wizard.js.map +1 -0
- package/dist/tools/analyze-tasks.d.ts +61 -0
- package/dist/tools/analyze-tasks.d.ts.map +1 -0
- package/dist/tools/analyze-tasks.js +258 -0
- package/dist/tools/analyze-tasks.js.map +1 -0
- package/dist/types/config.d.ts +126 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +118 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/errors.d.ts +39 -0
- package/dist/types/errors.d.ts.map +1 -0
- package/dist/types/errors.js +83 -0
- package/dist/types/errors.js.map +1 -0
- package/dist/types/index.d.ts +7 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +7 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/task.d.ts +69 -0
- package/dist/types/task.d.ts.map +1 -0
- package/dist/types/task.js +5 -0
- package/dist/types/task.js.map +1 -0
- package/dist/utils/estimation.d.ts +49 -0
- package/dist/utils/estimation.d.ts.map +1 -0
- package/dist/utils/estimation.js +244 -0
- package/dist/utils/estimation.js.map +1 -0
- package/dist/utils/priority.d.ts +68 -0
- package/dist/utils/priority.d.ts.map +1 -0
- package/dist/utils/priority.js +243 -0
- package/dist/utils/priority.js.map +1 -0
- package/dist/utils/retry.d.ts +62 -0
- package/dist/utils/retry.d.ts.map +1 -0
- package/dist/utils/retry.js +161 -0
- package/dist/utils/retry.js.map +1 -0
- package/dist/utils/stakeholders.d.ts +61 -0
- package/dist/utils/stakeholders.d.ts.map +1 -0
- package/dist/utils/stakeholders.js +301 -0
- package/dist/utils/stakeholders.js.map +1 -0
- package/dist/utils/task-splitter.d.ts +45 -0
- package/dist/utils/task-splitter.d.ts.map +1 -0
- package/dist/utils/task-splitter.js +321 -0
- package/dist/utils/task-splitter.js.map +1 -0
- package/package.json +66 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,901 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* sage - AI Task Management Assistant MCP Server
|
|
4
|
+
*
|
|
5
|
+
* An MCP server for Claude Desktop and Claude Code that provides
|
|
6
|
+
* task management, prioritization, and reminder integration.
|
|
7
|
+
*/
|
|
8
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
9
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
10
|
+
import { z } from 'zod';
|
|
11
|
+
import { ConfigLoader } from './config/loader.js';
|
|
12
|
+
import { SetupWizard } from './setup/wizard.js';
|
|
13
|
+
import { TaskAnalyzer } from './tools/analyze-tasks.js';
|
|
14
|
+
import { ReminderManager } from './integrations/reminder-manager.js';
|
|
15
|
+
import { CalendarService } from './integrations/calendar-service.js';
|
|
16
|
+
import { NotionMCPService } from './integrations/notion-mcp.js';
|
|
17
|
+
// Server metadata
|
|
18
|
+
const SERVER_NAME = 'sage';
|
|
19
|
+
const SERVER_VERSION = '0.1.0';
|
|
20
|
+
// Global state
|
|
21
|
+
let config = null;
|
|
22
|
+
let wizardSession = null;
|
|
23
|
+
let reminderManager = null;
|
|
24
|
+
let calendarService = null;
|
|
25
|
+
let notionService = null;
|
|
26
|
+
/**
|
|
27
|
+
* Validate config updates for a specific section
|
|
28
|
+
*/
|
|
29
|
+
function validateConfigUpdate(section, updates) {
|
|
30
|
+
const invalidFields = [];
|
|
31
|
+
switch (section) {
|
|
32
|
+
case 'user':
|
|
33
|
+
if (updates.name !== undefined && typeof updates.name !== 'string') {
|
|
34
|
+
invalidFields.push('name');
|
|
35
|
+
}
|
|
36
|
+
if (updates.timezone !== undefined && typeof updates.timezone !== 'string') {
|
|
37
|
+
invalidFields.push('timezone');
|
|
38
|
+
}
|
|
39
|
+
break;
|
|
40
|
+
case 'calendar':
|
|
41
|
+
if (updates.workingHours !== undefined) {
|
|
42
|
+
const wh = updates.workingHours;
|
|
43
|
+
if (!wh.start || !wh.end) {
|
|
44
|
+
invalidFields.push('workingHours');
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (updates.deepWorkDays !== undefined && !Array.isArray(updates.deepWorkDays)) {
|
|
48
|
+
invalidFields.push('deepWorkDays');
|
|
49
|
+
}
|
|
50
|
+
if (updates.meetingHeavyDays !== undefined && !Array.isArray(updates.meetingHeavyDays)) {
|
|
51
|
+
invalidFields.push('meetingHeavyDays');
|
|
52
|
+
}
|
|
53
|
+
break;
|
|
54
|
+
case 'integrations':
|
|
55
|
+
if (updates.notion !== undefined) {
|
|
56
|
+
const notion = updates.notion;
|
|
57
|
+
if (notion.enabled === true && !notion.databaseId) {
|
|
58
|
+
invalidFields.push('notion.databaseId');
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
break;
|
|
62
|
+
case 'team':
|
|
63
|
+
if (updates.members !== undefined && !Array.isArray(updates.members)) {
|
|
64
|
+
invalidFields.push('members');
|
|
65
|
+
}
|
|
66
|
+
if (updates.managers !== undefined && !Array.isArray(updates.managers)) {
|
|
67
|
+
invalidFields.push('managers');
|
|
68
|
+
}
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
if (invalidFields.length > 0) {
|
|
72
|
+
return {
|
|
73
|
+
valid: false,
|
|
74
|
+
error: `無効なフィールド: ${invalidFields.join(', ')}`,
|
|
75
|
+
invalidFields,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
return { valid: true };
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Apply config updates to a specific section
|
|
82
|
+
*/
|
|
83
|
+
function applyConfigUpdates(currentConfig, section, updates) {
|
|
84
|
+
const newConfig = { ...currentConfig };
|
|
85
|
+
switch (section) {
|
|
86
|
+
case 'user':
|
|
87
|
+
newConfig.user = { ...newConfig.user, ...updates };
|
|
88
|
+
break;
|
|
89
|
+
case 'calendar':
|
|
90
|
+
newConfig.calendar = { ...newConfig.calendar, ...updates };
|
|
91
|
+
break;
|
|
92
|
+
case 'priorityRules':
|
|
93
|
+
newConfig.priorityRules = {
|
|
94
|
+
...newConfig.priorityRules,
|
|
95
|
+
...updates,
|
|
96
|
+
};
|
|
97
|
+
break;
|
|
98
|
+
case 'integrations':
|
|
99
|
+
// Deep merge for integrations
|
|
100
|
+
if (updates.appleReminders) {
|
|
101
|
+
newConfig.integrations.appleReminders = {
|
|
102
|
+
...newConfig.integrations.appleReminders,
|
|
103
|
+
...updates.appleReminders,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
if (updates.notion) {
|
|
107
|
+
newConfig.integrations.notion = {
|
|
108
|
+
...newConfig.integrations.notion,
|
|
109
|
+
...updates.notion,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
break;
|
|
113
|
+
case 'team':
|
|
114
|
+
newConfig.team = { ...newConfig.team, ...updates };
|
|
115
|
+
break;
|
|
116
|
+
case 'preferences':
|
|
117
|
+
newConfig.preferences = { ...newConfig.preferences, ...updates };
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
return newConfig;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Initialize services with config
|
|
124
|
+
*/
|
|
125
|
+
function initializeServices(userConfig) {
|
|
126
|
+
reminderManager = new ReminderManager({
|
|
127
|
+
appleRemindersThreshold: 7,
|
|
128
|
+
notionThreshold: userConfig.integrations.notion.threshold,
|
|
129
|
+
defaultList: userConfig.integrations.appleReminders.defaultList,
|
|
130
|
+
notionDatabaseId: userConfig.integrations.notion.databaseId,
|
|
131
|
+
});
|
|
132
|
+
calendarService = new CalendarService();
|
|
133
|
+
notionService = new NotionMCPService();
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Initialize the MCP server with all tools
|
|
137
|
+
*/
|
|
138
|
+
async function createServer() {
|
|
139
|
+
const server = new McpServer({
|
|
140
|
+
name: SERVER_NAME,
|
|
141
|
+
version: SERVER_VERSION,
|
|
142
|
+
});
|
|
143
|
+
// Try to load existing config
|
|
144
|
+
try {
|
|
145
|
+
config = await ConfigLoader.load();
|
|
146
|
+
if (config) {
|
|
147
|
+
initializeServices(config);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
config = null;
|
|
152
|
+
}
|
|
153
|
+
// ============================================
|
|
154
|
+
// Setup & Configuration Tools
|
|
155
|
+
// ============================================
|
|
156
|
+
/**
|
|
157
|
+
* check_setup_status - Check if initial setup is complete
|
|
158
|
+
* Requirement: 1.1, 1.2
|
|
159
|
+
*/
|
|
160
|
+
server.tool('check_setup_status', 'Check if sage has been configured. Returns setup status and guidance.', {}, async () => {
|
|
161
|
+
const exists = await ConfigLoader.exists();
|
|
162
|
+
const isValid = config !== null;
|
|
163
|
+
if (!exists) {
|
|
164
|
+
return {
|
|
165
|
+
content: [
|
|
166
|
+
{
|
|
167
|
+
type: 'text',
|
|
168
|
+
text: JSON.stringify({
|
|
169
|
+
setupComplete: false,
|
|
170
|
+
configExists: false,
|
|
171
|
+
message: 'sageの初期設定が必要です。start_setup_wizardを実行してセットアップを開始してください。',
|
|
172
|
+
nextAction: 'start_setup_wizard',
|
|
173
|
+
}, null, 2),
|
|
174
|
+
},
|
|
175
|
+
],
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
if (!isValid) {
|
|
179
|
+
return {
|
|
180
|
+
content: [
|
|
181
|
+
{
|
|
182
|
+
type: 'text',
|
|
183
|
+
text: JSON.stringify({
|
|
184
|
+
setupComplete: false,
|
|
185
|
+
configExists: true,
|
|
186
|
+
message: '設定ファイルが見つかりましたが、読み込みに失敗しました。設定を再作成してください。',
|
|
187
|
+
nextAction: 'start_setup_wizard',
|
|
188
|
+
}, null, 2),
|
|
189
|
+
},
|
|
190
|
+
],
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
return {
|
|
194
|
+
content: [
|
|
195
|
+
{
|
|
196
|
+
type: 'text',
|
|
197
|
+
text: JSON.stringify({
|
|
198
|
+
setupComplete: true,
|
|
199
|
+
configExists: true,
|
|
200
|
+
userName: config?.user.name,
|
|
201
|
+
message: 'sageは設定済みです。タスク分析やリマインド設定を開始できます。',
|
|
202
|
+
availableTools: [
|
|
203
|
+
'analyze_tasks',
|
|
204
|
+
'set_reminder',
|
|
205
|
+
'find_available_slots',
|
|
206
|
+
'sync_to_notion',
|
|
207
|
+
'update_config',
|
|
208
|
+
],
|
|
209
|
+
}, null, 2),
|
|
210
|
+
},
|
|
211
|
+
],
|
|
212
|
+
};
|
|
213
|
+
});
|
|
214
|
+
/**
|
|
215
|
+
* start_setup_wizard - Begin the interactive setup process
|
|
216
|
+
* Requirement: 1.3
|
|
217
|
+
*/
|
|
218
|
+
server.tool('start_setup_wizard', 'Start the interactive setup wizard for sage. Returns the first question.', {
|
|
219
|
+
mode: z
|
|
220
|
+
.enum(['full', 'quick'])
|
|
221
|
+
.optional()
|
|
222
|
+
.describe('Setup mode: full (all questions) or quick (essential only)'),
|
|
223
|
+
}, async ({ mode = 'full' }) => {
|
|
224
|
+
wizardSession = SetupWizard.createSession(mode);
|
|
225
|
+
const question = SetupWizard.getCurrentQuestion(wizardSession);
|
|
226
|
+
return {
|
|
227
|
+
content: [
|
|
228
|
+
{
|
|
229
|
+
type: 'text',
|
|
230
|
+
text: JSON.stringify({
|
|
231
|
+
sessionId: wizardSession.sessionId,
|
|
232
|
+
currentStep: wizardSession.currentStep,
|
|
233
|
+
totalSteps: wizardSession.totalSteps,
|
|
234
|
+
progress: Math.round((wizardSession.currentStep / wizardSession.totalSteps) * 100),
|
|
235
|
+
question: {
|
|
236
|
+
id: question.id,
|
|
237
|
+
text: question.text,
|
|
238
|
+
type: question.type,
|
|
239
|
+
options: question.options,
|
|
240
|
+
defaultValue: question.defaultValue,
|
|
241
|
+
helpText: question.helpText,
|
|
242
|
+
},
|
|
243
|
+
message: 'セットアップを開始します。以下の質問に回答してください。',
|
|
244
|
+
}, null, 2),
|
|
245
|
+
},
|
|
246
|
+
],
|
|
247
|
+
};
|
|
248
|
+
});
|
|
249
|
+
/**
|
|
250
|
+
* answer_wizard_question - Answer a setup wizard question
|
|
251
|
+
* Requirement: 1.3, 1.4
|
|
252
|
+
*/
|
|
253
|
+
server.tool('answer_wizard_question', 'Answer a question in the setup wizard and get the next question.', {
|
|
254
|
+
questionId: z.string().describe('The ID of the question being answered'),
|
|
255
|
+
answer: z.union([z.string(), z.array(z.string())]).describe('The answer to the question'),
|
|
256
|
+
}, async ({ questionId, answer }) => {
|
|
257
|
+
if (!wizardSession) {
|
|
258
|
+
return {
|
|
259
|
+
content: [
|
|
260
|
+
{
|
|
261
|
+
type: 'text',
|
|
262
|
+
text: JSON.stringify({
|
|
263
|
+
error: true,
|
|
264
|
+
message: 'セットアップセッションが見つかりません。start_setup_wizardを実行してください。',
|
|
265
|
+
}, null, 2),
|
|
266
|
+
},
|
|
267
|
+
],
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
const result = SetupWizard.answerQuestion(wizardSession, questionId, answer);
|
|
271
|
+
if (!result.success) {
|
|
272
|
+
return {
|
|
273
|
+
content: [
|
|
274
|
+
{
|
|
275
|
+
type: 'text',
|
|
276
|
+
text: JSON.stringify({
|
|
277
|
+
error: true,
|
|
278
|
+
message: result.error,
|
|
279
|
+
currentQuestion: result.currentQuestion,
|
|
280
|
+
}, null, 2),
|
|
281
|
+
},
|
|
282
|
+
],
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
if (result.isComplete) {
|
|
286
|
+
return {
|
|
287
|
+
content: [
|
|
288
|
+
{
|
|
289
|
+
type: 'text',
|
|
290
|
+
text: JSON.stringify({
|
|
291
|
+
isComplete: true,
|
|
292
|
+
sessionId: wizardSession.sessionId,
|
|
293
|
+
answers: wizardSession.answers,
|
|
294
|
+
message: 'すべての質問に回答しました。save_configを実行して設定を保存してください。',
|
|
295
|
+
nextAction: 'save_config',
|
|
296
|
+
}, null, 2),
|
|
297
|
+
},
|
|
298
|
+
],
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
const nextQuestion = SetupWizard.getCurrentQuestion(wizardSession);
|
|
302
|
+
return {
|
|
303
|
+
content: [
|
|
304
|
+
{
|
|
305
|
+
type: 'text',
|
|
306
|
+
text: JSON.stringify({
|
|
307
|
+
success: true,
|
|
308
|
+
currentStep: wizardSession.currentStep,
|
|
309
|
+
totalSteps: wizardSession.totalSteps,
|
|
310
|
+
progress: Math.round((wizardSession.currentStep / wizardSession.totalSteps) * 100),
|
|
311
|
+
question: {
|
|
312
|
+
id: nextQuestion.id,
|
|
313
|
+
text: nextQuestion.text,
|
|
314
|
+
type: nextQuestion.type,
|
|
315
|
+
options: nextQuestion.options,
|
|
316
|
+
defaultValue: nextQuestion.defaultValue,
|
|
317
|
+
helpText: nextQuestion.helpText,
|
|
318
|
+
},
|
|
319
|
+
}, null, 2),
|
|
320
|
+
},
|
|
321
|
+
],
|
|
322
|
+
};
|
|
323
|
+
});
|
|
324
|
+
/**
|
|
325
|
+
* save_config - Save the configuration from the setup wizard
|
|
326
|
+
* Requirement: 1.4, 1.5, 1.6
|
|
327
|
+
*/
|
|
328
|
+
server.tool('save_config', 'Save the configuration after completing the setup wizard.', {
|
|
329
|
+
confirm: z.boolean().describe('Confirm saving the configuration'),
|
|
330
|
+
}, async ({ confirm }) => {
|
|
331
|
+
if (!confirm) {
|
|
332
|
+
return {
|
|
333
|
+
content: [
|
|
334
|
+
{
|
|
335
|
+
type: 'text',
|
|
336
|
+
text: JSON.stringify({
|
|
337
|
+
saved: false,
|
|
338
|
+
message: '設定の保存がキャンセルされました。',
|
|
339
|
+
}, null, 2),
|
|
340
|
+
},
|
|
341
|
+
],
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
if (!wizardSession) {
|
|
345
|
+
return {
|
|
346
|
+
content: [
|
|
347
|
+
{
|
|
348
|
+
type: 'text',
|
|
349
|
+
text: JSON.stringify({
|
|
350
|
+
error: true,
|
|
351
|
+
message: 'セットアップセッションが見つかりません。start_setup_wizardを実行してください。',
|
|
352
|
+
}, null, 2),
|
|
353
|
+
},
|
|
354
|
+
],
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
try {
|
|
358
|
+
const newConfig = SetupWizard.buildConfig(wizardSession);
|
|
359
|
+
await ConfigLoader.save(newConfig);
|
|
360
|
+
config = newConfig;
|
|
361
|
+
wizardSession = null;
|
|
362
|
+
return {
|
|
363
|
+
content: [
|
|
364
|
+
{
|
|
365
|
+
type: 'text',
|
|
366
|
+
text: JSON.stringify({
|
|
367
|
+
saved: true,
|
|
368
|
+
configPath: ConfigLoader.getConfigPath(),
|
|
369
|
+
userName: newConfig.user.name,
|
|
370
|
+
message: `設定を保存しました。${newConfig.user.name}さん、sageをご利用いただきありがとうございます!`,
|
|
371
|
+
availableTools: [
|
|
372
|
+
'analyze_tasks',
|
|
373
|
+
'set_reminder',
|
|
374
|
+
'find_available_slots',
|
|
375
|
+
'sync_to_notion',
|
|
376
|
+
],
|
|
377
|
+
}, null, 2),
|
|
378
|
+
},
|
|
379
|
+
],
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
catch (error) {
|
|
383
|
+
return {
|
|
384
|
+
content: [
|
|
385
|
+
{
|
|
386
|
+
type: 'text',
|
|
387
|
+
text: JSON.stringify({
|
|
388
|
+
error: true,
|
|
389
|
+
message: `設定の保存に失敗しました: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
390
|
+
}, null, 2),
|
|
391
|
+
},
|
|
392
|
+
],
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
// ============================================
|
|
397
|
+
// Task Analysis Tools (placeholder)
|
|
398
|
+
// ============================================
|
|
399
|
+
/**
|
|
400
|
+
* analyze_tasks - Analyze tasks and provide prioritization
|
|
401
|
+
* Requirement: 2.1-2.6, 3.1-3.2, 4.1-4.5
|
|
402
|
+
*/
|
|
403
|
+
server.tool('analyze_tasks', 'Analyze tasks to determine priority, estimate time, and identify stakeholders.', {
|
|
404
|
+
tasks: z
|
|
405
|
+
.array(z.object({
|
|
406
|
+
title: z.string().describe('Task title'),
|
|
407
|
+
description: z.string().optional().describe('Task description'),
|
|
408
|
+
deadline: z.string().optional().describe('Task deadline (ISO 8601 format)'),
|
|
409
|
+
}))
|
|
410
|
+
.describe('List of tasks to analyze'),
|
|
411
|
+
}, async ({ tasks }) => {
|
|
412
|
+
if (!config) {
|
|
413
|
+
return {
|
|
414
|
+
content: [
|
|
415
|
+
{
|
|
416
|
+
type: 'text',
|
|
417
|
+
text: JSON.stringify({
|
|
418
|
+
error: true,
|
|
419
|
+
message: 'sageが設定されていません。check_setup_statusを実行してください。',
|
|
420
|
+
}, null, 2),
|
|
421
|
+
},
|
|
422
|
+
],
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
try {
|
|
426
|
+
const result = await TaskAnalyzer.analyzeTasks(tasks, config);
|
|
427
|
+
return {
|
|
428
|
+
content: [
|
|
429
|
+
{
|
|
430
|
+
type: 'text',
|
|
431
|
+
text: JSON.stringify({
|
|
432
|
+
success: true,
|
|
433
|
+
summary: result.summary,
|
|
434
|
+
tasks: result.analyzedTasks.map((t) => ({
|
|
435
|
+
title: t.original.title,
|
|
436
|
+
description: t.original.description,
|
|
437
|
+
deadline: t.original.deadline,
|
|
438
|
+
priority: t.priority,
|
|
439
|
+
estimatedMinutes: t.estimatedMinutes,
|
|
440
|
+
stakeholders: t.stakeholders,
|
|
441
|
+
tags: t.tags,
|
|
442
|
+
reasoning: t.reasoning,
|
|
443
|
+
suggestedReminders: t.suggestedReminders,
|
|
444
|
+
})),
|
|
445
|
+
}, null, 2),
|
|
446
|
+
},
|
|
447
|
+
],
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
catch (error) {
|
|
451
|
+
return {
|
|
452
|
+
content: [
|
|
453
|
+
{
|
|
454
|
+
type: 'text',
|
|
455
|
+
text: JSON.stringify({
|
|
456
|
+
error: true,
|
|
457
|
+
message: `タスク分析に失敗しました: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
458
|
+
}, null, 2),
|
|
459
|
+
},
|
|
460
|
+
],
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
});
|
|
464
|
+
/**
|
|
465
|
+
* set_reminder - Set a reminder for a task
|
|
466
|
+
* Requirement: 5.1-5.6
|
|
467
|
+
*/
|
|
468
|
+
server.tool('set_reminder', 'Set a reminder for a task in Apple Reminders or Notion.', {
|
|
469
|
+
taskTitle: z.string().describe('Title of the task'),
|
|
470
|
+
dueDate: z.string().optional().describe('Due date for the reminder (ISO 8601 format)'),
|
|
471
|
+
reminderType: z
|
|
472
|
+
.enum(['1_hour_before', '3_hours_before', '1_day_before', '3_days_before', '1_week_before'])
|
|
473
|
+
.optional()
|
|
474
|
+
.describe('Type of reminder'),
|
|
475
|
+
list: z.string().optional().describe('Reminder list name (for Apple Reminders)'),
|
|
476
|
+
priority: z.enum(['P0', 'P1', 'P2', 'P3']).optional().describe('Task priority'),
|
|
477
|
+
notes: z.string().optional().describe('Additional notes for the reminder'),
|
|
478
|
+
}, async ({ taskTitle, dueDate, reminderType, list, priority, notes }) => {
|
|
479
|
+
if (!config) {
|
|
480
|
+
return {
|
|
481
|
+
content: [
|
|
482
|
+
{
|
|
483
|
+
type: 'text',
|
|
484
|
+
text: JSON.stringify({
|
|
485
|
+
error: true,
|
|
486
|
+
message: 'sageが設定されていません。check_setup_statusを実行してください。',
|
|
487
|
+
}, null, 2),
|
|
488
|
+
},
|
|
489
|
+
],
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
if (!reminderManager) {
|
|
493
|
+
initializeServices(config);
|
|
494
|
+
}
|
|
495
|
+
try {
|
|
496
|
+
const result = await reminderManager.setReminder({
|
|
497
|
+
taskTitle,
|
|
498
|
+
targetDate: dueDate,
|
|
499
|
+
reminderType,
|
|
500
|
+
list: list ?? config.integrations.appleReminders.defaultList,
|
|
501
|
+
priority: priority,
|
|
502
|
+
notes,
|
|
503
|
+
});
|
|
504
|
+
if (result.success) {
|
|
505
|
+
return {
|
|
506
|
+
content: [
|
|
507
|
+
{
|
|
508
|
+
type: 'text',
|
|
509
|
+
text: JSON.stringify({
|
|
510
|
+
success: true,
|
|
511
|
+
destination: result.destination,
|
|
512
|
+
method: result.method,
|
|
513
|
+
reminderId: result.reminderId,
|
|
514
|
+
reminderUrl: result.reminderUrl ?? result.pageUrl,
|
|
515
|
+
message: result.destination === 'apple_reminders'
|
|
516
|
+
? `Apple Remindersにリマインダーを作成しました: ${taskTitle}`
|
|
517
|
+
: `Notionにタスクを作成しました: ${taskTitle}`,
|
|
518
|
+
}, null, 2),
|
|
519
|
+
},
|
|
520
|
+
],
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
return {
|
|
524
|
+
content: [
|
|
525
|
+
{
|
|
526
|
+
type: 'text',
|
|
527
|
+
text: JSON.stringify({
|
|
528
|
+
success: false,
|
|
529
|
+
destination: result.destination,
|
|
530
|
+
error: result.error,
|
|
531
|
+
fallbackText: result.fallbackText,
|
|
532
|
+
message: result.fallbackText
|
|
533
|
+
? '自動作成に失敗しました。以下のテキストを手動でコピーしてください。'
|
|
534
|
+
: `リマインダー作成に失敗しました: ${result.error}`,
|
|
535
|
+
}, null, 2),
|
|
536
|
+
},
|
|
537
|
+
],
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
catch (error) {
|
|
541
|
+
return {
|
|
542
|
+
content: [
|
|
543
|
+
{
|
|
544
|
+
type: 'text',
|
|
545
|
+
text: JSON.stringify({
|
|
546
|
+
error: true,
|
|
547
|
+
message: `リマインダー設定に失敗しました: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
548
|
+
}, null, 2),
|
|
549
|
+
},
|
|
550
|
+
],
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
});
|
|
554
|
+
/**
|
|
555
|
+
* find_available_slots - Find available time slots in calendar
|
|
556
|
+
* Requirement: 3.3-3.6, 6.1-6.6
|
|
557
|
+
*/
|
|
558
|
+
server.tool('find_available_slots', 'Find available time slots in the calendar for scheduling tasks.', {
|
|
559
|
+
durationMinutes: z.number().describe('Required duration in minutes'),
|
|
560
|
+
startDate: z.string().optional().describe('Start date for search (ISO 8601 format)'),
|
|
561
|
+
endDate: z.string().optional().describe('End date for search (ISO 8601 format)'),
|
|
562
|
+
preferDeepWork: z.boolean().optional().describe('Prefer deep work time slots'),
|
|
563
|
+
}, async ({ durationMinutes, startDate, endDate, preferDeepWork }) => {
|
|
564
|
+
if (!config) {
|
|
565
|
+
return {
|
|
566
|
+
content: [
|
|
567
|
+
{
|
|
568
|
+
type: 'text',
|
|
569
|
+
text: JSON.stringify({
|
|
570
|
+
error: true,
|
|
571
|
+
message: 'sageが設定されていません。check_setup_statusを実行してください。',
|
|
572
|
+
}, null, 2),
|
|
573
|
+
},
|
|
574
|
+
],
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
if (!calendarService) {
|
|
578
|
+
initializeServices(config);
|
|
579
|
+
}
|
|
580
|
+
try {
|
|
581
|
+
// Check platform availability
|
|
582
|
+
const platformInfo = await calendarService.detectPlatform();
|
|
583
|
+
const isAvailable = await calendarService.isAvailable();
|
|
584
|
+
if (!isAvailable) {
|
|
585
|
+
// Return manual input prompt for unsupported platforms
|
|
586
|
+
const manualPrompt = calendarService.generateManualInputPrompt(startDate ?? new Date().toISOString().split('T')[0], endDate ?? new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString().split('T')[0]);
|
|
587
|
+
return {
|
|
588
|
+
content: [
|
|
589
|
+
{
|
|
590
|
+
type: 'text',
|
|
591
|
+
text: JSON.stringify({
|
|
592
|
+
success: false,
|
|
593
|
+
platform: platformInfo.platform,
|
|
594
|
+
method: platformInfo.recommendedMethod,
|
|
595
|
+
message: 'カレンダー統合がこのプラットフォームで利用できません。手動で予定を入力してください。',
|
|
596
|
+
manualPrompt,
|
|
597
|
+
}, null, 2),
|
|
598
|
+
},
|
|
599
|
+
],
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
// Fetch events from calendar
|
|
603
|
+
const searchStart = startDate ?? new Date().toISOString().split('T')[0];
|
|
604
|
+
const searchEnd = endDate ?? new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString().split('T')[0];
|
|
605
|
+
const events = await calendarService.fetchEvents(searchStart, searchEnd);
|
|
606
|
+
// Find available slots
|
|
607
|
+
const workingHours = {
|
|
608
|
+
start: config.calendar.workingHours.start,
|
|
609
|
+
end: config.calendar.workingHours.end,
|
|
610
|
+
};
|
|
611
|
+
const slots = calendarService.findAvailableSlotsFromEvents(events, durationMinutes, workingHours, searchStart);
|
|
612
|
+
// Apply suitability scoring
|
|
613
|
+
const suitabilityConfig = {
|
|
614
|
+
deepWorkDays: config.calendar.deepWorkDays,
|
|
615
|
+
meetingHeavyDays: config.calendar.meetingHeavyDays,
|
|
616
|
+
};
|
|
617
|
+
const scoredSlots = slots.map((slot) => calendarService.calculateSuitability(slot, suitabilityConfig));
|
|
618
|
+
// Filter for deep work preference if requested
|
|
619
|
+
const filteredSlots = preferDeepWork
|
|
620
|
+
? scoredSlots.filter((s) => s.dayType === 'deep-work')
|
|
621
|
+
: scoredSlots;
|
|
622
|
+
// Sort by suitability (excellent > good > acceptable)
|
|
623
|
+
const suitabilityOrder = { excellent: 0, good: 1, acceptable: 2 };
|
|
624
|
+
filteredSlots.sort((a, b) => suitabilityOrder[a.suitability] - suitabilityOrder[b.suitability]);
|
|
625
|
+
return {
|
|
626
|
+
content: [
|
|
627
|
+
{
|
|
628
|
+
type: 'text',
|
|
629
|
+
text: JSON.stringify({
|
|
630
|
+
success: true,
|
|
631
|
+
platform: platformInfo.platform,
|
|
632
|
+
method: platformInfo.recommendedMethod,
|
|
633
|
+
searchRange: { start: searchStart, end: searchEnd },
|
|
634
|
+
eventsFound: events.length,
|
|
635
|
+
slots: filteredSlots.slice(0, 10).map((slot) => ({
|
|
636
|
+
start: slot.start,
|
|
637
|
+
end: slot.end,
|
|
638
|
+
durationMinutes: slot.durationMinutes,
|
|
639
|
+
suitability: slot.suitability,
|
|
640
|
+
dayType: slot.dayType,
|
|
641
|
+
reason: slot.reason,
|
|
642
|
+
})),
|
|
643
|
+
message: filteredSlots.length > 0
|
|
644
|
+
? `${filteredSlots.length}件の空き時間が見つかりました。`
|
|
645
|
+
: '指定した条件に合う空き時間が見つかりませんでした。',
|
|
646
|
+
}, null, 2),
|
|
647
|
+
},
|
|
648
|
+
],
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
catch (error) {
|
|
652
|
+
return {
|
|
653
|
+
content: [
|
|
654
|
+
{
|
|
655
|
+
type: 'text',
|
|
656
|
+
text: JSON.stringify({
|
|
657
|
+
error: true,
|
|
658
|
+
message: `カレンダー検索に失敗しました: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
659
|
+
}, null, 2),
|
|
660
|
+
},
|
|
661
|
+
],
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
});
|
|
665
|
+
/**
|
|
666
|
+
* sync_to_notion - Sync a task to Notion
|
|
667
|
+
* Requirement: 8.1-8.5
|
|
668
|
+
*/
|
|
669
|
+
server.tool('sync_to_notion', 'Sync a task to Notion database for long-term tracking.', {
|
|
670
|
+
taskTitle: z.string().describe('Title of the task'),
|
|
671
|
+
description: z.string().optional().describe('Task description'),
|
|
672
|
+
priority: z.enum(['P0', 'P1', 'P2', 'P3']).optional().describe('Task priority'),
|
|
673
|
+
dueDate: z.string().optional().describe('Due date (ISO 8601 format)'),
|
|
674
|
+
stakeholders: z.array(z.string()).optional().describe('List of stakeholders'),
|
|
675
|
+
estimatedMinutes: z.number().optional().describe('Estimated duration in minutes'),
|
|
676
|
+
}, async ({ taskTitle, description, priority, dueDate, stakeholders, estimatedMinutes }) => {
|
|
677
|
+
if (!config) {
|
|
678
|
+
return {
|
|
679
|
+
content: [
|
|
680
|
+
{
|
|
681
|
+
type: 'text',
|
|
682
|
+
text: JSON.stringify({
|
|
683
|
+
error: true,
|
|
684
|
+
message: 'sageが設定されていません。check_setup_statusを実行してください。',
|
|
685
|
+
}, null, 2),
|
|
686
|
+
},
|
|
687
|
+
],
|
|
688
|
+
};
|
|
689
|
+
}
|
|
690
|
+
if (!config.integrations.notion.enabled) {
|
|
691
|
+
return {
|
|
692
|
+
content: [
|
|
693
|
+
{
|
|
694
|
+
type: 'text',
|
|
695
|
+
text: JSON.stringify({
|
|
696
|
+
error: true,
|
|
697
|
+
message: 'Notion統合が有効になっていません。update_configでNotion設定を更新してください。',
|
|
698
|
+
}, null, 2),
|
|
699
|
+
},
|
|
700
|
+
],
|
|
701
|
+
};
|
|
702
|
+
}
|
|
703
|
+
if (!notionService) {
|
|
704
|
+
initializeServices(config);
|
|
705
|
+
}
|
|
706
|
+
try {
|
|
707
|
+
// Check if Notion MCP is available
|
|
708
|
+
const isAvailable = await notionService.isAvailable();
|
|
709
|
+
// Build properties for Notion page
|
|
710
|
+
const properties = notionService.buildNotionProperties({
|
|
711
|
+
title: taskTitle,
|
|
712
|
+
priority,
|
|
713
|
+
deadline: dueDate,
|
|
714
|
+
stakeholders,
|
|
715
|
+
estimatedMinutes,
|
|
716
|
+
description,
|
|
717
|
+
});
|
|
718
|
+
if (!isAvailable) {
|
|
719
|
+
// Generate fallback template for manual copy
|
|
720
|
+
const fallbackText = notionService.generateFallbackTemplate({
|
|
721
|
+
title: taskTitle,
|
|
722
|
+
priority,
|
|
723
|
+
deadline: dueDate,
|
|
724
|
+
stakeholders,
|
|
725
|
+
estimatedMinutes,
|
|
726
|
+
description,
|
|
727
|
+
});
|
|
728
|
+
return {
|
|
729
|
+
content: [
|
|
730
|
+
{
|
|
731
|
+
type: 'text',
|
|
732
|
+
text: JSON.stringify({
|
|
733
|
+
success: false,
|
|
734
|
+
method: 'fallback',
|
|
735
|
+
message: 'Notion MCP統合が利用できません。以下のテンプレートを手動でNotionにコピーしてください。',
|
|
736
|
+
fallbackText,
|
|
737
|
+
task: {
|
|
738
|
+
taskTitle,
|
|
739
|
+
priority: priority ?? 'P3',
|
|
740
|
+
dueDate,
|
|
741
|
+
stakeholders: stakeholders ?? [],
|
|
742
|
+
estimatedMinutes,
|
|
743
|
+
},
|
|
744
|
+
}, null, 2),
|
|
745
|
+
},
|
|
746
|
+
],
|
|
747
|
+
};
|
|
748
|
+
}
|
|
749
|
+
// Create page in Notion via MCP
|
|
750
|
+
const result = await notionService.createPage({
|
|
751
|
+
databaseId: config.integrations.notion.databaseId,
|
|
752
|
+
title: taskTitle,
|
|
753
|
+
properties,
|
|
754
|
+
});
|
|
755
|
+
if (result.success) {
|
|
756
|
+
return {
|
|
757
|
+
content: [
|
|
758
|
+
{
|
|
759
|
+
type: 'text',
|
|
760
|
+
text: JSON.stringify({
|
|
761
|
+
success: true,
|
|
762
|
+
method: 'mcp',
|
|
763
|
+
pageId: result.pageId,
|
|
764
|
+
pageUrl: result.pageUrl,
|
|
765
|
+
message: `Notionにタスクを同期しました: ${taskTitle}`,
|
|
766
|
+
}, null, 2),
|
|
767
|
+
},
|
|
768
|
+
],
|
|
769
|
+
};
|
|
770
|
+
}
|
|
771
|
+
// MCP call failed, provide fallback
|
|
772
|
+
const fallbackText = notionService.generateFallbackTemplate({
|
|
773
|
+
title: taskTitle,
|
|
774
|
+
priority,
|
|
775
|
+
deadline: dueDate,
|
|
776
|
+
stakeholders,
|
|
777
|
+
estimatedMinutes,
|
|
778
|
+
description,
|
|
779
|
+
});
|
|
780
|
+
return {
|
|
781
|
+
content: [
|
|
782
|
+
{
|
|
783
|
+
type: 'text',
|
|
784
|
+
text: JSON.stringify({
|
|
785
|
+
success: false,
|
|
786
|
+
method: 'fallback',
|
|
787
|
+
error: result.error,
|
|
788
|
+
message: 'Notion MCP呼び出しに失敗しました。以下のテンプレートを手動でコピーしてください。',
|
|
789
|
+
fallbackText,
|
|
790
|
+
}, null, 2),
|
|
791
|
+
},
|
|
792
|
+
],
|
|
793
|
+
};
|
|
794
|
+
}
|
|
795
|
+
catch (error) {
|
|
796
|
+
return {
|
|
797
|
+
content: [
|
|
798
|
+
{
|
|
799
|
+
type: 'text',
|
|
800
|
+
text: JSON.stringify({
|
|
801
|
+
error: true,
|
|
802
|
+
message: `Notion同期に失敗しました: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
803
|
+
}, null, 2),
|
|
804
|
+
},
|
|
805
|
+
],
|
|
806
|
+
};
|
|
807
|
+
}
|
|
808
|
+
});
|
|
809
|
+
/**
|
|
810
|
+
* update_config - Update configuration
|
|
811
|
+
* Requirement: 10.1-10.6
|
|
812
|
+
*/
|
|
813
|
+
server.tool('update_config', 'Update sage configuration settings.', {
|
|
814
|
+
section: z
|
|
815
|
+
.enum(['user', 'calendar', 'priorityRules', 'integrations', 'team', 'preferences'])
|
|
816
|
+
.describe('Configuration section to update'),
|
|
817
|
+
updates: z.record(z.unknown()).describe('Key-value pairs to update'),
|
|
818
|
+
}, async ({ section, updates }) => {
|
|
819
|
+
if (!config) {
|
|
820
|
+
return {
|
|
821
|
+
content: [
|
|
822
|
+
{
|
|
823
|
+
type: 'text',
|
|
824
|
+
text: JSON.stringify({
|
|
825
|
+
error: true,
|
|
826
|
+
message: 'sageが設定されていません。check_setup_statusを実行してください。',
|
|
827
|
+
}, null, 2),
|
|
828
|
+
},
|
|
829
|
+
],
|
|
830
|
+
};
|
|
831
|
+
}
|
|
832
|
+
try {
|
|
833
|
+
// Validate section-specific updates
|
|
834
|
+
const validationResult = validateConfigUpdate(section, updates);
|
|
835
|
+
if (!validationResult.valid) {
|
|
836
|
+
return {
|
|
837
|
+
content: [
|
|
838
|
+
{
|
|
839
|
+
type: 'text',
|
|
840
|
+
text: JSON.stringify({
|
|
841
|
+
error: true,
|
|
842
|
+
message: `設定の検証に失敗しました: ${validationResult.error}`,
|
|
843
|
+
invalidFields: validationResult.invalidFields,
|
|
844
|
+
}, null, 2),
|
|
845
|
+
},
|
|
846
|
+
],
|
|
847
|
+
};
|
|
848
|
+
}
|
|
849
|
+
// Apply updates to config
|
|
850
|
+
const updatedConfig = applyConfigUpdates(config, section, updates);
|
|
851
|
+
// Save the updated config
|
|
852
|
+
await ConfigLoader.save(updatedConfig);
|
|
853
|
+
config = updatedConfig;
|
|
854
|
+
// Re-initialize services if integrations changed
|
|
855
|
+
if (section === 'integrations') {
|
|
856
|
+
initializeServices(config);
|
|
857
|
+
}
|
|
858
|
+
return {
|
|
859
|
+
content: [
|
|
860
|
+
{
|
|
861
|
+
type: 'text',
|
|
862
|
+
text: JSON.stringify({
|
|
863
|
+
success: true,
|
|
864
|
+
section,
|
|
865
|
+
updatedFields: Object.keys(updates),
|
|
866
|
+
message: `設定を更新しました: ${section}`,
|
|
867
|
+
}, null, 2),
|
|
868
|
+
},
|
|
869
|
+
],
|
|
870
|
+
};
|
|
871
|
+
}
|
|
872
|
+
catch (error) {
|
|
873
|
+
return {
|
|
874
|
+
content: [
|
|
875
|
+
{
|
|
876
|
+
type: 'text',
|
|
877
|
+
text: JSON.stringify({
|
|
878
|
+
error: true,
|
|
879
|
+
message: `設定の更新に失敗しました: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
880
|
+
}, null, 2),
|
|
881
|
+
},
|
|
882
|
+
],
|
|
883
|
+
};
|
|
884
|
+
}
|
|
885
|
+
});
|
|
886
|
+
return server;
|
|
887
|
+
}
|
|
888
|
+
/**
|
|
889
|
+
* Main entry point
|
|
890
|
+
*/
|
|
891
|
+
async function main() {
|
|
892
|
+
const server = await createServer();
|
|
893
|
+
const transport = new StdioServerTransport();
|
|
894
|
+
await server.connect(transport);
|
|
895
|
+
console.error(`${SERVER_NAME} v${SERVER_VERSION} started`);
|
|
896
|
+
}
|
|
897
|
+
main().catch((error) => {
|
|
898
|
+
console.error('Failed to start sage server:', error);
|
|
899
|
+
process.exit(1);
|
|
900
|
+
});
|
|
901
|
+
//# sourceMappingURL=index.js.map
|