@fukuchang/taskchute-mcp 0.1.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,13 +1,80 @@
1
1
  #!/usr/bin/env node
2
2
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
3
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
4
+ import { Entry } from '@napi-rs/keyring';
4
5
  import { argon2id } from 'hash-wasm';
5
6
  import '@scure/bip39';
6
7
  import '@scure/bip39/wordlists/english.js';
7
8
  import { createClient } from '@supabase/supabase-js';
8
- import { Entry } from '@napi-rs/keyring';
9
9
  import { z } from 'zod';
10
10
 
11
+ // ../node-shared/src/app-context-error.ts
12
+ var AppContextError = class extends Error {
13
+ constructor(message) {
14
+ super(message);
15
+ this.name = "AppContextError";
16
+ }
17
+ };
18
+
19
+ // ../node-shared/src/config.ts
20
+ var ENV_URL = "TASKCHUTE_SUPABASE_URL";
21
+ var ENV_KEY = "TASKCHUTE_SUPABASE_PUBLISHABLE_KEY";
22
+ function readSupabaseConfigFromEnv() {
23
+ const url = process.env.TASKCHUTE_SUPABASE_URL;
24
+ const publishableKey = process.env.TASKCHUTE_SUPABASE_PUBLISHABLE_KEY;
25
+ if (!url || !publishableKey) {
26
+ throw new Error(
27
+ `Supabase \u306E\u74B0\u5883\u5909\u6570\u304C\u898B\u3064\u304B\u3089\u306A\u3044\u3002${ENV_URL} \u3068 ${ENV_KEY} \u3092\u8A2D\u5B9A\u3057\u3066\u306D\u301C`
28
+ );
29
+ }
30
+ return { url, publishableKey };
31
+ }
32
+ var KEYRING_SERVICE = "taskchute-cli";
33
+ var KEYRING_ACCOUNT = "passphrase";
34
+ var KeyringPassphraseStore = class {
35
+ get() {
36
+ try {
37
+ const entry = new Entry(KEYRING_SERVICE, KEYRING_ACCOUNT);
38
+ return entry.getPassword();
39
+ } catch {
40
+ return null;
41
+ }
42
+ }
43
+ set(passphrase) {
44
+ const entry = new Entry(KEYRING_SERVICE, KEYRING_ACCOUNT);
45
+ entry.setPassword(passphrase);
46
+ }
47
+ clear() {
48
+ try {
49
+ const entry = new Entry(KEYRING_SERVICE, KEYRING_ACCOUNT);
50
+ entry.deletePassword();
51
+ } catch {
52
+ }
53
+ }
54
+ };
55
+ var KEYRING_SERVICE2 = "taskchute-cli";
56
+ var KeyringSessionStore = class {
57
+ getItem(key) {
58
+ try {
59
+ const entry = new Entry(KEYRING_SERVICE2, key);
60
+ return entry.getPassword();
61
+ } catch {
62
+ return null;
63
+ }
64
+ }
65
+ setItem(key, value) {
66
+ const entry = new Entry(KEYRING_SERVICE2, key);
67
+ entry.setPassword(value);
68
+ }
69
+ removeItem(key) {
70
+ try {
71
+ const entry = new Entry(KEYRING_SERVICE2, key);
72
+ entry.deletePassword();
73
+ } catch {
74
+ }
75
+ }
76
+ };
77
+
11
78
  // ../crypto/src/bytes.ts
12
79
  function allocBytes(length) {
13
80
  return new Uint8Array(new ArrayBuffer(length));
@@ -668,6 +735,39 @@ var SupabaseProjectRepository = class {
668
735
  }
669
736
  };
670
737
 
738
+ // ../infra-supabase/src/supabase-routine-expansion-repository.ts
739
+ function rowToExpansion(row) {
740
+ return {
741
+ routineId: row.routine_id,
742
+ date: row.date,
743
+ status: row.status
744
+ };
745
+ }
746
+ var SupabaseRoutineExpansionRepository = class {
747
+ supabase;
748
+ constructor(supabase) {
749
+ this.supabase = supabase;
750
+ }
751
+ async findByDate(date) {
752
+ const { data, error } = await this.supabase.from("routine_expansions").select("*").eq("date", date);
753
+ if (error) throw error;
754
+ return data.map(rowToExpansion);
755
+ }
756
+ async recordExpanded(routineId, date) {
757
+ const userId = await getCurrentUserId(this.supabase);
758
+ const { error } = await this.supabase.from("routine_expansions").upsert(
759
+ {
760
+ user_id: userId,
761
+ routine_id: routineId,
762
+ date,
763
+ status: "expanded"
764
+ },
765
+ { onConflict: "routine_id,date", ignoreDuplicates: true }
766
+ );
767
+ if (error) throw error;
768
+ }
769
+ };
770
+
671
771
  // ../infra-supabase/src/supabase-routine-repository.ts
672
772
  async function rowToRoutine(row, cryptoSession) {
673
773
  const [title, description] = await Promise.all([
@@ -705,7 +805,6 @@ async function rowToRoutine(row, cryptoSession) {
705
805
  }
706
806
  if (row.start_date !== null) routine.startDate = row.start_date;
707
807
  if (row.end_date !== null) routine.endDate = row.end_date;
708
- if (row.last_expanded_date !== null) routine.lastExpandedDate = row.last_expanded_date;
709
808
  return routine;
710
809
  }
711
810
  function nonEncryptedFieldsToRow3(input) {
@@ -727,9 +826,6 @@ function nonEncryptedFieldsToRow3(input) {
727
826
  if ("autoExpand" in input && input.autoExpand !== void 0) {
728
827
  row.auto_expand = input.autoExpand;
729
828
  }
730
- if ("lastExpandedDate" in input) {
731
- row.last_expanded_date = input.lastExpandedDate ?? null;
732
- }
733
829
  if ("status" in input && input.status !== void 0) row.status = input.status;
734
830
  return row;
735
831
  }
@@ -1286,6 +1382,13 @@ var SupabaseTaskRepository = class {
1286
1382
  const { error } = await this.supabase.from("tasks").delete().eq("id", id);
1287
1383
  if (error) throw error;
1288
1384
  }
1385
+ async deleteWithRoutineSkip(id, today) {
1386
+ const { error } = await this.supabase.rpc("delete_task_and_skip_routine", {
1387
+ p_task_id: id,
1388
+ p_today: today
1389
+ });
1390
+ if (error) throw error;
1391
+ }
1289
1392
  };
1290
1393
 
1291
1394
  // ../infra-supabase/src/supabase-time-block-repository.ts
@@ -1495,6 +1598,13 @@ function toDateString(date) {
1495
1598
  const day = String(date.getDate()).padStart(2, "0");
1496
1599
  return `${year}-${month}-${day}`;
1497
1600
  }
1601
+ function fromDateString(dateString) {
1602
+ const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(dateString);
1603
+ if (!match || !match[1] || !match[2] || !match[3]) {
1604
+ throw new Error(`Invalid date string: ${dateString}`);
1605
+ }
1606
+ return new Date(Number(match[1]), Number(match[2]) - 1, Number(match[3]));
1607
+ }
1498
1608
  function todayDateString() {
1499
1609
  return toDateString(/* @__PURE__ */ new Date());
1500
1610
  }
@@ -1503,8 +1613,40 @@ function toTimeString(date) {
1503
1613
  const minutes = String(date.getMinutes()).padStart(2, "0");
1504
1614
  return `${hours}:${minutes}`;
1505
1615
  }
1616
+ function toTimeStringWithSeconds(date) {
1617
+ const hours = String(date.getHours()).padStart(2, "0");
1618
+ const minutes = String(date.getMinutes()).padStart(2, "0");
1619
+ const seconds = String(date.getSeconds()).padStart(2, "0");
1620
+ return `${hours}:${minutes}:${seconds}`;
1621
+ }
1622
+
1623
+ // ../domain/src/task-utils.ts
1624
+ function sessionDurationSeconds(session) {
1625
+ return (session.finishedAt.getTime() - session.startedAt.getTime()) / 1e3;
1626
+ }
1627
+ function completedSessionsTotalSeconds(task) {
1628
+ return task.sessions.reduce((sum, s) => sum + sessionDurationSeconds(s), 0);
1629
+ }
1630
+ function calculateActualSeconds(task, now = /* @__PURE__ */ new Date()) {
1631
+ const completed = completedSessionsTotalSeconds(task);
1632
+ if (task.currentSession) {
1633
+ return completed + (now.getTime() - task.currentSession.startedAt.getTime()) / 1e3;
1634
+ }
1635
+ return completed;
1636
+ }
1506
1637
 
1507
1638
  // ../domain/src/task-operations.ts
1639
+ var TIME_RE = /^([01]\d|2[0-3]):([0-5]\d)(?::([0-5]\d))?$/;
1640
+ function parseTimeOnDate(dateString, timeString) {
1641
+ const m = TIME_RE.exec(timeString);
1642
+ if (!m || !m[1] || !m[2]) {
1643
+ throw new Error(`Invalid time string: ${timeString}`);
1644
+ }
1645
+ const base = fromDateString(dateString);
1646
+ const seconds = m[3] ? Number(m[3]) : 0;
1647
+ base.setHours(Number(m[1]), Number(m[2]), seconds, 0);
1648
+ return base;
1649
+ }
1508
1650
  async function startTask(repo, taskId) {
1509
1651
  const task = await repo.findById(taskId);
1510
1652
  if (!task) throw new Error(`Task not found: ${taskId}`);
@@ -1515,7 +1657,7 @@ async function startTask(repo, taskId) {
1515
1657
  currentSession: { startedAt: now }
1516
1658
  };
1517
1659
  if (!task.actualStartTime) {
1518
- patch.actualStartTime = toTimeString(now);
1660
+ patch.actualStartTime = toTimeStringWithSeconds(now);
1519
1661
  }
1520
1662
  return repo.update(taskId, patch);
1521
1663
  }
@@ -1535,6 +1677,30 @@ async function pauseTask(repo, taskId) {
1535
1677
  currentSession: void 0
1536
1678
  });
1537
1679
  }
1680
+ async function skipTask(repo, taskId) {
1681
+ const task = await repo.findById(taskId);
1682
+ if (!task) throw new Error(`Task not found: ${taskId}`);
1683
+ const updates = { status: "skipped" };
1684
+ if (task.currentSession) {
1685
+ updates.sessions = [
1686
+ ...task.sessions,
1687
+ {
1688
+ startedAt: task.currentSession.startedAt,
1689
+ finishedAt: /* @__PURE__ */ new Date()
1690
+ }
1691
+ ];
1692
+ updates.currentSession = void 0;
1693
+ }
1694
+ return repo.update(taskId, updates);
1695
+ }
1696
+ async function reopenTask(repo, taskId) {
1697
+ const task = await repo.findById(taskId);
1698
+ if (!task) throw new Error(`Task not found: ${taskId}`);
1699
+ if (task.status !== "completed" && task.status !== "skipped") {
1700
+ return task;
1701
+ }
1702
+ return repo.update(taskId, { status: "planned" });
1703
+ }
1538
1704
  async function completeTask(repo, taskId) {
1539
1705
  const task = await repo.findById(taskId);
1540
1706
  if (!task) throw new Error(`Task not found: ${taskId}`);
@@ -1551,6 +1717,115 @@ async function completeTask(repo, taskId) {
1551
1717
  }
1552
1718
  return repo.update(taskId, updates);
1553
1719
  }
1720
+ async function interruptTask(repo, params) {
1721
+ const target = await repo.findById(params.targetTaskId);
1722
+ if (!target) throw new Error(`Task not found: ${params.targetTaskId}`);
1723
+ if (!target.currentSession) {
1724
+ throw new Error(`Target task is not currently running`);
1725
+ }
1726
+ const now = /* @__PURE__ */ new Date();
1727
+ const closedSession = {
1728
+ startedAt: target.currentSession.startedAt,
1729
+ finishedAt: now
1730
+ };
1731
+ const finishedOriginal = await repo.update(target.id, {
1732
+ status: "completed",
1733
+ sessions: [...target.sessions, closedSession],
1734
+ currentSession: void 0
1735
+ });
1736
+ const elapsed = calculateActualSeconds(finishedOriginal);
1737
+ const interruptionStartedAt = /* @__PURE__ */ new Date();
1738
+ const interruption = await repo.create({
1739
+ date: target.date,
1740
+ title: params.interruption.title,
1741
+ description: params.interruption.description,
1742
+ estimatedSeconds: params.interruption.estimatedSeconds,
1743
+ sessions: [],
1744
+ currentSession: { startedAt: interruptionStartedAt },
1745
+ status: "running",
1746
+ orderIndex: target.orderIndex + 1,
1747
+ actualStartTime: toTimeStringWithSeconds(interruptionStartedAt),
1748
+ projectId: params.interruption.projectId,
1749
+ contextId: params.interruption.contextId,
1750
+ tagIds: params.interruption.tagIds ?? []
1751
+ });
1752
+ const continuation = await repo.create({
1753
+ date: target.date,
1754
+ title: target.title,
1755
+ description: target.description,
1756
+ estimatedSeconds: Math.max(0, Math.floor(target.estimatedSeconds - elapsed)),
1757
+ sessions: [],
1758
+ status: "planned",
1759
+ orderIndex: target.orderIndex + 2,
1760
+ splitFromTaskId: target.id,
1761
+ projectId: target.projectId,
1762
+ contextId: target.contextId,
1763
+ // continuation は「割り込みが片付いた後にやる残作業」。割り込み発生時刻を
1764
+ // 予定時刻にすることで、(1) interruption と同じ effectiveTime になり、
1765
+ // ソート上 orderIndex で interruption の次に並ぶ、(2) 割り込みが TimeBlock
1766
+ // の境界をまたいだ場合は continuation も新しいブロックに移動する、という
1767
+ // 自然な挙動になる。
1768
+ scheduledStartTime: toTimeString(interruptionStartedAt),
1769
+ tagIds: target.tagIds,
1770
+ isBackground: target.isBackground
1771
+ });
1772
+ return { finishedOriginal, interruption, continuation };
1773
+ }
1774
+ async function addParallelTask(repo, input) {
1775
+ const startImmediately = input.startImmediately ?? true;
1776
+ const existing = await repo.findByDate(input.date);
1777
+ const maxOrder = existing.reduce((max, t) => t.orderIndex > max ? t.orderIndex : max, 0);
1778
+ const startedAt = startImmediately ? /* @__PURE__ */ new Date() : void 0;
1779
+ return repo.create({
1780
+ date: input.date,
1781
+ title: input.title,
1782
+ description: input.description,
1783
+ estimatedSeconds: input.estimatedSeconds,
1784
+ sessions: [],
1785
+ currentSession: startedAt ? { startedAt } : void 0,
1786
+ status: startImmediately ? "running" : "planned",
1787
+ orderIndex: maxOrder + 10,
1788
+ actualStartTime: startedAt ? toTimeStringWithSeconds(startedAt) : void 0,
1789
+ projectId: input.projectId,
1790
+ contextId: input.contextId,
1791
+ tagIds: input.tagIds ?? [],
1792
+ isBackground: input.isBackground
1793
+ });
1794
+ }
1795
+ function buildSetActualTimesPatch(task, input) {
1796
+ const startTime = input.startTime?.trim() ? input.startTime.trim() : null;
1797
+ const endTime = input.endTime?.trim() ? input.endTime.trim() : null;
1798
+ if (startTime === null && endTime === null) {
1799
+ return {
1800
+ status: "planned",
1801
+ currentSession: void 0,
1802
+ sessions: [],
1803
+ actualStartTime: void 0
1804
+ };
1805
+ }
1806
+ if (startTime === null && endTime !== null) {
1807
+ throw new Error("endTime requires startTime");
1808
+ }
1809
+ const startedAt = parseTimeOnDate(task.date, startTime);
1810
+ if (endTime === null) {
1811
+ return {
1812
+ status: "running",
1813
+ currentSession: { startedAt },
1814
+ sessions: [],
1815
+ actualStartTime: startTime
1816
+ };
1817
+ }
1818
+ let finishedAt = parseTimeOnDate(task.date, endTime);
1819
+ if (finishedAt.getTime() < startedAt.getTime()) {
1820
+ finishedAt = new Date(finishedAt.getTime() + 24 * 60 * 60 * 1e3);
1821
+ }
1822
+ return {
1823
+ status: "completed",
1824
+ currentSession: void 0,
1825
+ sessions: [{ startedAt, finishedAt }],
1826
+ actualStartTime: startTime
1827
+ };
1828
+ }
1554
1829
 
1555
1830
  // ../domain/src/task-reorder.ts
1556
1831
  function computeNextOrderIndex(existingTasks) {
@@ -1562,6 +1837,70 @@ function computeNextOrderIndex(existingTasks) {
1562
1837
  return max + 10;
1563
1838
  }
1564
1839
 
1840
+ // ../domain/src/task-suggest.ts
1841
+ function normalizeForSearch(str) {
1842
+ return str.trim().toLowerCase().replace(
1843
+ /[ぁ-ゖ]/g,
1844
+ (ch) => String.fromCharCode(ch.charCodeAt(0) + 96)
1845
+ );
1846
+ }
1847
+ function fuzzyScore(query, target) {
1848
+ const q = query;
1849
+ const t = target;
1850
+ if (q.length === 0) return 0;
1851
+ if (t.length === 0) return null;
1852
+ let qi = 0;
1853
+ let score = 0;
1854
+ let lastMatchIndex = -2;
1855
+ let consecutiveRun = 0;
1856
+ for (let ti = 0; ti < t.length && qi < q.length; ti++) {
1857
+ if (t[ti] === q[qi]) {
1858
+ if (lastMatchIndex === ti - 1) {
1859
+ consecutiveRun += 1;
1860
+ score += 5 + consecutiveRun * 3;
1861
+ } else {
1862
+ consecutiveRun = 0;
1863
+ score += 2;
1864
+ }
1865
+ if (qi === 0) {
1866
+ score += Math.max(0, 8 - ti);
1867
+ }
1868
+ lastMatchIndex = ti;
1869
+ qi += 1;
1870
+ }
1871
+ }
1872
+ if (qi < q.length) return null;
1873
+ score -= Math.max(0, t.length - q.length) / 4;
1874
+ return score;
1875
+ }
1876
+ function findSimilarTasks(query, tasks, limit = 5) {
1877
+ const q = normalizeForSearch(query);
1878
+ if (q.length === 0) return [];
1879
+ const bestByTitle = /* @__PURE__ */ new Map();
1880
+ for (const task of tasks) {
1881
+ const titleTrimmed = task.title.trim();
1882
+ if (titleTrimmed.length === 0) continue;
1883
+ const titleNormalized = normalizeForSearch(titleTrimmed);
1884
+ const score = fuzzyScore(q, titleNormalized);
1885
+ if (score === null) continue;
1886
+ const existing = bestByTitle.get(titleNormalized);
1887
+ if (!existing) {
1888
+ bestByTitle.set(titleNormalized, { task, score });
1889
+ continue;
1890
+ }
1891
+ const existingNewer = existing.task.updatedAt.getTime();
1892
+ const candidateNewer = task.updatedAt.getTime();
1893
+ if (candidateNewer > existingNewer) {
1894
+ bestByTitle.set(titleNormalized, { task, score });
1895
+ }
1896
+ }
1897
+ const sorted = Array.from(bestByTitle.values()).sort((a, b) => {
1898
+ if (b.score !== a.score) return b.score - a.score;
1899
+ return b.task.updatedAt.getTime() - a.task.updatedAt.getTime();
1900
+ });
1901
+ return sorted.slice(0, limit).map((s) => s.task);
1902
+ }
1903
+
1565
1904
  // ../domain/src/task-change-log.ts
1566
1905
  function describeStatusTransition(from, to) {
1567
1906
  if (from === "planned" && to === "running") return "\u958B\u59CB";
@@ -1796,6 +2135,80 @@ function diffTasksToNoteInputs(before, after, lookups) {
1796
2135
  }
1797
2136
  return result;
1798
2137
  }
2138
+ function titleOrPlaceholder(task) {
2139
+ return task.title || "\u7121\u984C\u306E\u30BF\u30B9\u30AF";
2140
+ }
2141
+ async function interruptTaskWithAudit(taskRepo, taskNoteRepo, params) {
2142
+ const result = await interruptTask(taskRepo, params);
2143
+ const notes = buildSplitNoteInputs(
2144
+ result.finishedOriginal,
2145
+ result.interruption,
2146
+ result.continuation
2147
+ );
2148
+ try {
2149
+ await Promise.all(notes.map((input) => taskNoteRepo.create(input)));
2150
+ } catch (err) {
2151
+ console.error("[audit] failed to record split notes", {
2152
+ sourceTaskId: result.finishedOriginal.id,
2153
+ err
2154
+ });
2155
+ }
2156
+ return result;
2157
+ }
2158
+ function buildSplitNoteInputs(source, interruption, continuation) {
2159
+ const sourceTitle = titleOrPlaceholder(source);
2160
+ const interruptionTitle = titleOrPlaceholder(interruption);
2161
+ return [
2162
+ {
2163
+ taskId: source.id,
2164
+ type: "split",
2165
+ body: `\u300C${interruptionTitle}\u300D\u306E\u5272\u308A\u8FBC\u307F\u3067\u5206\u5272\u3002\u6B8B\u4F5C\u696D\u306F\u5225\u30BF\u30B9\u30AF\u3067\u7D99\u7D9A\u3002`,
2166
+ metadata: {
2167
+ event: "split-source",
2168
+ interruptionTaskId: interruption.id,
2169
+ continuationTaskId: continuation.id
2170
+ }
2171
+ },
2172
+ {
2173
+ taskId: interruption.id,
2174
+ type: "split",
2175
+ body: `\u300C${sourceTitle}\u300D\u306E\u4F5C\u696D\u4E2D\u306B\u5272\u308A\u8FBC\u307F\u3068\u3057\u3066\u8D77\u7968\u3002`,
2176
+ metadata: {
2177
+ event: "split-interruption",
2178
+ sourceTaskId: source.id,
2179
+ continuationTaskId: continuation.id
2180
+ }
2181
+ },
2182
+ {
2183
+ taskId: continuation.id,
2184
+ type: "split",
2185
+ body: `\u300C${sourceTitle}\u300D\u306E\u6B8B\u4F5C\u696D\u3068\u3057\u3066\u7D99\u7D9A\u3002`,
2186
+ metadata: {
2187
+ event: "split-continuation",
2188
+ sourceTaskId: source.id,
2189
+ interruptionTaskId: interruption.id
2190
+ }
2191
+ }
2192
+ ];
2193
+ }
2194
+
2195
+ // ../domain/src/routine-expansion.ts
2196
+ function buildRoutineTaskPatch(routine) {
2197
+ return {
2198
+ title: routine.title,
2199
+ description: routine.description,
2200
+ estimatedSeconds: routine.estimatedSeconds,
2201
+ projectId: routine.projectId,
2202
+ contextId: routine.contextId,
2203
+ tagIds: routine.tagIds,
2204
+ scheduledStartTime: routine.defaultScheduledStartTime
2205
+ };
2206
+ }
2207
+ function pickPropagationTargets(routine, tasks, today) {
2208
+ return tasks.filter(
2209
+ (t) => t.routineId === routine.id && t.date >= today && t.status === "planned"
2210
+ );
2211
+ }
1799
2212
 
1800
2213
  // ../infra-supabase/src/audit-task-repository.ts
1801
2214
  var AuditingTaskRepository = class {
@@ -1863,17 +2276,23 @@ var AuditingTaskRepository = class {
1863
2276
  delete(id) {
1864
2277
  return this.inner.delete(id);
1865
2278
  }
2279
+ deleteWithRoutineSkip(id, today) {
2280
+ return this.inner.deleteWithRoutineSkip(id, today);
2281
+ }
1866
2282
  };
1867
2283
 
1868
2284
  // ../infra-supabase/src/composition-root.ts
1869
2285
  function buildRepositories(supabaseClient, cryptoSession, getLookups) {
1870
2286
  const taskNote = new SupabaseTaskNoteRepository(supabaseClient, cryptoSession);
1871
2287
  const rawTask = new SupabaseTaskRepository(supabaseClient, cryptoSession);
1872
- const task = getLookups ? new AuditingTaskRepository(rawTask, taskNote, getLookups) : rawTask;
2288
+ const auditingTask = getLookups ? new AuditingTaskRepository(rawTask, taskNote, getLookups) : void 0;
2289
+ const task = auditingTask ?? rawTask;
1873
2290
  return {
1874
2291
  task,
2292
+ auditingTask,
1875
2293
  taskNote,
1876
2294
  routine: new SupabaseRoutineRepository(supabaseClient, cryptoSession),
2295
+ routineExpansion: new SupabaseRoutineExpansionRepository(supabaseClient),
1877
2296
  project: new SupabaseProjectRepository(supabaseClient, cryptoSession),
1878
2297
  tag: new SupabaseTagRepository(supabaseClient, cryptoSession),
1879
2298
  context: new SupabaseContextRepository(supabaseClient, cryptoSession),
@@ -1881,124 +2300,109 @@ function buildRepositories(supabaseClient, cryptoSession, getLookups) {
1881
2300
  };
1882
2301
  }
1883
2302
 
1884
- // src/config.ts
1885
- var ENV_URL = "TASKCHUTE_SUPABASE_URL";
1886
- var ENV_KEY = "TASKCHUTE_SUPABASE_PUBLISHABLE_KEY";
1887
- function readSupabaseConfigFromEnv() {
1888
- const url = process.env.TASKCHUTE_SUPABASE_URL;
1889
- const publishableKey = process.env.TASKCHUTE_SUPABASE_PUBLISHABLE_KEY;
1890
- if (!url || !publishableKey) {
1891
- throw new Error(
1892
- `Supabase \u306E\u74B0\u5883\u5909\u6570\u304C\u898B\u3064\u304B\u3089\u306A\u3044\u3002${ENV_URL} \u3068 ${ENV_KEY} \u3092 MCP server \u306E env \u306B\u8A2D\u5B9A\u3057\u3066\u306D\u301C`
1893
- );
1894
- }
1895
- return { url, publishableKey };
1896
- }
1897
- var KEYRING_SERVICE = "taskchute-cli";
1898
- var KEYRING_ACCOUNT = "passphrase";
1899
- var KeyringPassphraseStore = class {
1900
- get() {
1901
- try {
1902
- const entry = new Entry(KEYRING_SERVICE, KEYRING_ACCOUNT);
1903
- return entry.getPassword();
1904
- } catch {
1905
- return null;
1906
- }
1907
- }
1908
- set(passphrase) {
1909
- const entry = new Entry(KEYRING_SERVICE, KEYRING_ACCOUNT);
1910
- entry.setPassword(passphrase);
1911
- }
1912
- clear() {
1913
- try {
1914
- const entry = new Entry(KEYRING_SERVICE, KEYRING_ACCOUNT);
1915
- entry.deletePassword();
1916
- } catch {
1917
- }
1918
- }
1919
- };
1920
- var KEYRING_SERVICE2 = "taskchute-cli";
1921
- var KeyringSessionStore = class {
1922
- getItem(key) {
1923
- try {
1924
- const entry = new Entry(KEYRING_SERVICE2, key);
1925
- return entry.getPassword();
1926
- } catch {
1927
- return null;
1928
- }
1929
- }
1930
- setItem(key, value) {
1931
- const entry = new Entry(KEYRING_SERVICE2, key);
1932
- entry.setPassword(value);
1933
- }
1934
- removeItem(key) {
1935
- try {
1936
- const entry = new Entry(KEYRING_SERVICE2, key);
1937
- entry.deletePassword();
1938
- } catch {
1939
- }
1940
- }
1941
- };
1942
-
1943
- // src/crypto-context.ts
1944
- var McpToolError = class extends Error {
1945
- constructor(message) {
1946
- super(message);
1947
- this.name = "McpToolError";
1948
- }
1949
- };
1950
- function resolvePassphrase(store) {
2303
+ // ../node-shared/src/create-node-app-context.ts
2304
+ async function resolvePassphrase(store, promptPassphrase) {
1951
2305
  const envPw = process.env.TASKCHUTE_PASSPHRASE;
1952
2306
  if (envPw) return envPw;
1953
2307
  const stored = store.get();
1954
2308
  if (stored) return stored;
1955
- throw new McpToolError(
1956
- "No passphrase available. Set TASKCHUTE_PASSPHRASE env or run `taskchute unlock` from the CLI first."
1957
- );
2309
+ if (!promptPassphrase) {
2310
+ throw new AppContextError(
2311
+ "No passphrase available. Set TASKCHUTE_PASSPHRASE env or run `taskchute unlock` from the CLI first."
2312
+ );
2313
+ }
2314
+ return await promptPassphrase();
1958
2315
  }
1959
- async function createAppContext(opts = {}) {
2316
+ async function createNodeAppContext(opts = {}) {
1960
2317
  const passphraseStore = opts.passphraseStore ?? new KeyringPassphraseStore();
2318
+ const sessionStorage = opts.sessionStorage ?? new KeyringSessionStore();
1961
2319
  const supabase = createNodeSupabaseClient(readSupabaseConfigFromEnv(), {
1962
- storage: new KeyringSessionStore()
2320
+ storage: sessionStorage
1963
2321
  });
1964
2322
  const {
1965
2323
  data: { user },
1966
2324
  error: authError
1967
2325
  } = await supabase.auth.getUser();
1968
2326
  if (authError || !user || !user.email) {
1969
- throw new McpToolError(
2327
+ throw new AppContextError(
1970
2328
  "Not logged in. Run `taskchute login` from the CLI first (session is shared via OS keyring)."
1971
2329
  );
1972
2330
  }
1973
2331
  const userKeysRepo = new SupabaseUserKeysRepository(supabase);
1974
2332
  const snapshot = await userKeysRepo.getOwn();
1975
2333
  if (!snapshot) {
1976
- throw new McpToolError(
2334
+ throw new AppContextError(
1977
2335
  "E2EE not set up. Complete setup from the web app first (Settings \u2192 \u30C7\u30FC\u30BF\u306E\u6697\u53F7\u5316)."
1978
2336
  );
1979
2337
  }
1980
- const passphrase = resolvePassphrase(passphraseStore);
2338
+ const passphrase = await resolvePassphrase(passphraseStore, opts.promptPassphrase);
1981
2339
  let masterKey;
1982
2340
  try {
1983
2341
  masterKey = await unlockMasterKey(snapshot, { kind: "passphrase", passphrase });
1984
2342
  } catch (err) {
1985
2343
  if (err instanceof UnlockError) {
1986
- throw new McpToolError(`Unlock failed: ${err.message}`);
2344
+ throw new AppContextError(`Unlock failed: ${err.message}`);
1987
2345
  }
1988
2346
  throw err;
1989
2347
  }
1990
2348
  const cryptoSession = new CryptoSession();
1991
2349
  cryptoSession.setMasterKey(masterKey, snapshot.currentKeyId);
1992
- const repos = buildRepositories(supabase, cryptoSession);
1993
- return {
1994
- supabase,
1995
- cryptoSession,
1996
- repos,
2350
+ const lookupsRef = {
2351
+ current: { projects: [], tags: [], contexts: [] }
2352
+ };
2353
+ const repos = buildRepositories(supabase, cryptoSession, () => lookupsRef.current);
2354
+ const refreshLookups = async () => {
2355
+ const [projects, tags, contexts] = await Promise.all([
2356
+ repos.project.findAll(),
2357
+ repos.tag.findAll(),
2358
+ repos.context.findAll()
2359
+ ]);
2360
+ lookupsRef.current = { projects, tags, contexts };
2361
+ };
2362
+ try {
2363
+ await refreshLookups();
2364
+ } catch (err) {
2365
+ console.error("[createNodeAppContext] initial refreshLookups failed:", err);
2366
+ }
2367
+ return {
2368
+ supabase,
2369
+ cryptoSession,
2370
+ repos,
1997
2371
  userId: user.id,
1998
- email: user.email
2372
+ email: user.email,
2373
+ refreshLookups
1999
2374
  };
2000
2375
  }
2001
2376
 
2377
+ // src/crypto-context.ts
2378
+ var McpToolError = class extends Error {
2379
+ constructor(message) {
2380
+ super(message);
2381
+ this.name = "McpToolError";
2382
+ }
2383
+ };
2384
+ async function wrapDomainErrors(op) {
2385
+ try {
2386
+ return await op();
2387
+ } catch (err) {
2388
+ if (err instanceof McpToolError) throw err;
2389
+ if (err instanceof Error) throw new McpToolError(err.message);
2390
+ throw err;
2391
+ }
2392
+ }
2393
+ async function createAppContext(opts = {}) {
2394
+ try {
2395
+ return await createNodeAppContext({
2396
+ passphraseStore: opts.passphraseStore
2397
+ });
2398
+ } catch (err) {
2399
+ if (err instanceof AppContextError) {
2400
+ throw new McpToolError(err.message);
2401
+ }
2402
+ throw err;
2403
+ }
2404
+ }
2405
+
2002
2406
  // src/format/task-summary.ts
2003
2407
  function taskSummaryFrom(task) {
2004
2408
  const summary = {
@@ -2033,15 +2437,81 @@ function taskSummaryFrom(task) {
2033
2437
  return summary;
2034
2438
  }
2035
2439
 
2036
- // src/tools/add-task.ts
2440
+ // src/tools/add-parallel-task.ts
2441
+ var addParallelTaskInputSchema = z.object({
2442
+ title: z.string().min(1, "title must not be empty"),
2443
+ estimated_seconds: z.number().int().positive(),
2444
+ date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "date must be YYYY-MM-DD").optional().describe("\u5BFE\u8C61\u65E5\u3002\u7701\u7565\u6642\u306F\u4ECA\u65E5"),
2445
+ description: z.string().optional(),
2446
+ project_id: z.string().uuid().optional(),
2447
+ context_id: z.string().uuid().optional(),
2448
+ tag_ids: z.array(z.string().uuid()).optional(),
2449
+ is_background: z.boolean().optional(),
2450
+ start_immediately: z.boolean().optional().describe("true (default) \u3067 running \u72B6\u614B + \u5373\u8A08\u6E2C\u958B\u59CB\u3002false \u3067 planned \u4F5C\u6210\u306E\u307F")
2451
+ });
2452
+ async function addParallelTaskHandler(ctx, input) {
2453
+ if (input.project_id) {
2454
+ const p = await ctx.repos.project.findById(input.project_id);
2455
+ if (!p) throw new McpToolError(`Project not found: ${input.project_id}`);
2456
+ }
2457
+ if (input.context_id) {
2458
+ const c = await ctx.repos.context.findById(input.context_id);
2459
+ if (!c) throw new McpToolError(`Context not found: ${input.context_id}`);
2460
+ }
2461
+ if (input.tag_ids && input.tag_ids.length > 0) {
2462
+ const checked = await Promise.all(
2463
+ input.tag_ids.map(async (id) => ({
2464
+ id,
2465
+ exists: await ctx.repos.tag.findById(id) !== null
2466
+ }))
2467
+ );
2468
+ const missing = checked.filter((c) => !c.exists).map((c) => c.id);
2469
+ if (missing.length > 0) throw new McpToolError(`Tag(s) not found: ${missing.join(", ")}`);
2470
+ }
2471
+ const task = await addParallelTask(ctx.repos.task, {
2472
+ date: input.date ?? todayDateString(),
2473
+ title: input.title,
2474
+ estimatedSeconds: input.estimated_seconds,
2475
+ ...input.description !== void 0 && { description: input.description },
2476
+ ...input.project_id !== void 0 && { projectId: input.project_id },
2477
+ ...input.context_id !== void 0 && { contextId: input.context_id },
2478
+ ...input.tag_ids !== void 0 && { tagIds: input.tag_ids },
2479
+ ...input.is_background !== void 0 && { isBackground: input.is_background },
2480
+ ...input.start_immediately !== void 0 && { startImmediately: input.start_immediately }
2481
+ });
2482
+ return { task: taskSummaryFrom(task) };
2483
+ }
2037
2484
  var addTaskInputSchema = z.object({
2038
2485
  title: z.string().min(1, "title must not be empty"),
2039
2486
  date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "date must be YYYY-MM-DD").optional().describe("\u5BFE\u8C61\u65E5 (YYYY-MM-DD)\u3002\u7701\u7565\u6642\u306F\u30B5\u30FC\u30D0\u30FC\u306E\u4ECA\u65E5 (Asia/Tokyo) \u3092\u4F7F\u3046"),
2040
2487
  estimated_seconds: z.number().int().positive().optional().describe("\u898B\u7A4D\u3082\u308A\u6642\u9593 (\u79D2)\u3002\u7701\u7565\u6642\u306F 1800 (= 30 \u5206)"),
2041
- description: z.string().optional()
2488
+ description: z.string().optional(),
2489
+ scheduled_start_time: z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/, "scheduled_start_time must be HH:MM").optional().describe("\u4E88\u5B9A\u958B\u59CB\u6642\u523B (HH:MM)\u3002\u30BF\u30A4\u30E0\u30D6\u30ED\u30C3\u30AF / \u30BD\u30FC\u30C8\u9806\u306B\u5F71\u97FF"),
2490
+ project_id: z.string().uuid().optional(),
2491
+ context_id: z.string().uuid().optional(),
2492
+ tag_ids: z.array(z.string().uuid()).optional(),
2493
+ notify_at_scheduled_start: z.boolean().optional().describe("true \u306A\u3089 scheduled_start_time \u5230\u9054\u6642\u306B\u901A\u77E5 (Web \u306E\u307F\u52D5\u4F5C)"),
2494
+ is_background: z.boolean().optional().describe("true \u306A\u3089\u4E26\u884C\u9032\u884C\u4E2D\u306E\u30D0\u30C3\u30AF\u30B0\u30E9\u30A6\u30F3\u30C9\u30BF\u30B9\u30AF\u6271\u3044 (\u898B\u7A4D\u3082\u308A\u96C6\u8A08\u304B\u3089\u9664\u5916)")
2042
2495
  });
2043
2496
  var DEFAULT_ESTIMATED_SECONDS = 30 * 60;
2044
2497
  async function addTaskHandler(ctx, input) {
2498
+ if (input.project_id) {
2499
+ const project = await ctx.repos.project.findById(input.project_id);
2500
+ if (!project) throw new McpToolError(`Project not found: ${input.project_id}`);
2501
+ }
2502
+ if (input.context_id) {
2503
+ const context = await ctx.repos.context.findById(input.context_id);
2504
+ if (!context) throw new McpToolError(`Context not found: ${input.context_id}`);
2505
+ }
2506
+ if (input.tag_ids && input.tag_ids.length > 0) {
2507
+ const checked = await Promise.all(
2508
+ input.tag_ids.map(async (id) => ({ id, exists: await ctx.repos.tag.findById(id) !== null }))
2509
+ );
2510
+ const missing = checked.filter((c) => !c.exists).map((c) => c.id);
2511
+ if (missing.length > 0) {
2512
+ throw new McpToolError(`Tag(s) not found: ${missing.join(", ")}`);
2513
+ }
2514
+ }
2045
2515
  const date = input.date ?? todayDateString();
2046
2516
  const estimatedSeconds = input.estimated_seconds ?? DEFAULT_ESTIMATED_SECONDS;
2047
2517
  const existing = await ctx.repos.task.findByDate(date);
@@ -2054,10 +2524,430 @@ async function addTaskHandler(ctx, input) {
2054
2524
  sessions: [],
2055
2525
  status: "planned",
2056
2526
  orderIndex,
2057
- tagIds: []
2527
+ tagIds: input.tag_ids ?? [],
2528
+ projectId: input.project_id,
2529
+ contextId: input.context_id,
2530
+ scheduledStartTime: input.scheduled_start_time,
2531
+ notifyAtScheduledStart: input.notify_at_scheduled_start,
2532
+ isBackground: input.is_background
2058
2533
  });
2059
2534
  return { task: taskSummaryFrom(task) };
2060
2535
  }
2536
+
2537
+ // src/format/context-summary.ts
2538
+ function contextSummaryFrom(context) {
2539
+ const summary = {
2540
+ id: context.id,
2541
+ name: context.name,
2542
+ status: context.status,
2543
+ created_at: context.createdAt.toISOString(),
2544
+ updated_at: context.updatedAt.toISOString()
2545
+ };
2546
+ if (context.description !== void 0) summary.description = context.description;
2547
+ if (context.icon !== void 0) summary.icon = context.icon;
2548
+ if (context.color !== void 0) summary.color = context.color;
2549
+ return summary;
2550
+ }
2551
+
2552
+ // src/tools/archive-context.ts
2553
+ var archiveContextInputSchema = z.object({
2554
+ id: z.string().uuid()
2555
+ });
2556
+ async function archiveContextHandler(ctx, input) {
2557
+ const existing = await ctx.repos.context.findById(input.id);
2558
+ if (!existing) {
2559
+ throw new McpToolError(`Context not found: ${input.id}`);
2560
+ }
2561
+ const archived = await ctx.repos.context.archive(input.id);
2562
+ return { context: contextSummaryFrom(archived) };
2563
+ }
2564
+
2565
+ // src/format/project-summary.ts
2566
+ function projectSummaryFrom(project) {
2567
+ const summary = {
2568
+ id: project.id,
2569
+ name: project.name,
2570
+ status: project.status,
2571
+ created_at: project.createdAt.toISOString(),
2572
+ updated_at: project.updatedAt.toISOString()
2573
+ };
2574
+ if (project.description !== void 0) summary.description = project.description;
2575
+ if (project.color !== void 0) summary.color = project.color;
2576
+ if (project.parentProjectId !== void 0) summary.parent_project_id = project.parentProjectId;
2577
+ if (project.goalId !== void 0) summary.goal_id = project.goalId;
2578
+ if (project.startedAt !== void 0) summary.started_at = project.startedAt.toISOString();
2579
+ if (project.targetEndDate !== void 0) {
2580
+ summary.target_end_date = project.targetEndDate.toISOString();
2581
+ }
2582
+ if (project.archivedAt !== void 0) summary.archived_at = project.archivedAt.toISOString();
2583
+ return summary;
2584
+ }
2585
+
2586
+ // src/tools/archive-project.ts
2587
+ var archiveProjectInputSchema = z.object({
2588
+ id: z.string().uuid()
2589
+ });
2590
+ async function archiveProjectHandler(ctx, input) {
2591
+ const existing = await ctx.repos.project.findById(input.id);
2592
+ if (!existing) {
2593
+ throw new McpToolError(`Project not found: ${input.id}`);
2594
+ }
2595
+ const archived = await ctx.repos.project.archive(input.id);
2596
+ return { project: projectSummaryFrom(archived) };
2597
+ }
2598
+
2599
+ // src/format/routine-summary.ts
2600
+ function routineSummaryFrom(routine) {
2601
+ const summary = {
2602
+ id: routine.id,
2603
+ title: routine.title,
2604
+ estimated_seconds: routine.estimatedSeconds,
2605
+ tag_ids: routine.tagIds,
2606
+ frequency: routine.frequency,
2607
+ auto_expand: routine.autoExpand,
2608
+ status: routine.status,
2609
+ created_at: routine.createdAt.toISOString(),
2610
+ updated_at: routine.updatedAt.toISOString()
2611
+ };
2612
+ if (routine.description !== void 0) summary.description = routine.description;
2613
+ if (routine.projectId !== void 0) summary.project_id = routine.projectId;
2614
+ if (routine.contextId !== void 0) summary.context_id = routine.contextId;
2615
+ if (routine.defaultScheduledStartTime !== void 0) {
2616
+ summary.default_scheduled_start_time = routine.defaultScheduledStartTime;
2617
+ }
2618
+ if (routine.startDate !== void 0) summary.start_date = routine.startDate;
2619
+ if (routine.endDate !== void 0) summary.end_date = routine.endDate;
2620
+ return summary;
2621
+ }
2622
+
2623
+ // src/tools/archive-routine.ts
2624
+ var archiveRoutineInputSchema = z.object({
2625
+ id: z.string().uuid()
2626
+ });
2627
+ async function archiveRoutineHandler(ctx, input) {
2628
+ const existing = await ctx.repos.routine.findById(input.id);
2629
+ if (!existing) {
2630
+ throw new McpToolError(`Routine not found: ${input.id}`);
2631
+ }
2632
+ const archived = await ctx.repos.routine.archive(input.id);
2633
+ return { routine: routineSummaryFrom(archived) };
2634
+ }
2635
+
2636
+ // src/format/tag-summary.ts
2637
+ function tagSummaryFrom(tag) {
2638
+ const summary = {
2639
+ id: tag.id,
2640
+ name: tag.name,
2641
+ status: tag.status,
2642
+ created_at: tag.createdAt.toISOString(),
2643
+ updated_at: tag.updatedAt.toISOString()
2644
+ };
2645
+ if (tag.description !== void 0) summary.description = tag.description;
2646
+ if (tag.color !== void 0) summary.color = tag.color;
2647
+ return summary;
2648
+ }
2649
+
2650
+ // src/tools/archive-tag.ts
2651
+ var archiveTagInputSchema = z.object({
2652
+ id: z.string().uuid()
2653
+ });
2654
+ async function archiveTagHandler(ctx, input) {
2655
+ const existing = await ctx.repos.tag.findById(input.id);
2656
+ if (!existing) {
2657
+ throw new McpToolError(`Tag not found: ${input.id}`);
2658
+ }
2659
+ const archived = await ctx.repos.tag.archive(input.id);
2660
+ return { tag: tagSummaryFrom(archived) };
2661
+ }
2662
+
2663
+ // src/format/time-block-summary.ts
2664
+ function timeBlockSummaryFrom(timeBlock) {
2665
+ const summary = {
2666
+ id: timeBlock.id,
2667
+ name: timeBlock.name,
2668
+ start_time: timeBlock.startTime,
2669
+ end_time: timeBlock.endTime,
2670
+ weekdays: timeBlock.weekdays,
2671
+ status: timeBlock.status,
2672
+ created_at: timeBlock.createdAt.toISOString(),
2673
+ updated_at: timeBlock.updatedAt.toISOString()
2674
+ };
2675
+ if (timeBlock.defaultProjectId !== void 0) {
2676
+ summary.default_project_id = timeBlock.defaultProjectId;
2677
+ }
2678
+ if (timeBlock.defaultContextId !== void 0) {
2679
+ summary.default_context_id = timeBlock.defaultContextId;
2680
+ }
2681
+ if (timeBlock.defaultTagIds !== void 0) summary.default_tag_ids = timeBlock.defaultTagIds;
2682
+ if (timeBlock.color !== void 0) summary.color = timeBlock.color;
2683
+ if (timeBlock.startDate !== void 0) summary.start_date = timeBlock.startDate;
2684
+ if (timeBlock.endDate !== void 0) summary.end_date = timeBlock.endDate;
2685
+ return summary;
2686
+ }
2687
+
2688
+ // src/tools/archive-time-block.ts
2689
+ var archiveTimeBlockInputSchema = z.object({
2690
+ id: z.string().uuid()
2691
+ });
2692
+ async function archiveTimeBlockHandler(ctx, input) {
2693
+ const existing = await ctx.repos.timeBlock.findById(input.id);
2694
+ if (!existing) {
2695
+ throw new McpToolError(`TimeBlock not found: ${input.id}`);
2696
+ }
2697
+ const archived = await ctx.repos.timeBlock.archive(input.id);
2698
+ return { time_block: timeBlockSummaryFrom(archived) };
2699
+ }
2700
+ var createContextInputSchema = z.object({
2701
+ name: z.string().min(1, "name must not be empty"),
2702
+ description: z.string().optional(),
2703
+ icon: z.string().optional().describe("\u7D75\u6587\u5B57\u307E\u305F\u306F\u30A2\u30A4\u30B3\u30F3\u540D (\u4F8B: \u{1F3E0} / mdi-home)"),
2704
+ color: z.string().optional().describe("UI \u8272 (\u4F8B: #ff6b6b)")
2705
+ });
2706
+ async function createContextHandler(ctx, input) {
2707
+ const createInput = {
2708
+ name: input.name,
2709
+ status: "active"
2710
+ };
2711
+ if (input.description !== void 0) createInput.description = input.description;
2712
+ if (input.icon !== void 0) createInput.icon = input.icon;
2713
+ if (input.color !== void 0) createInput.color = input.color;
2714
+ const context = await ctx.repos.context.create(createInput);
2715
+ return { context: contextSummaryFrom(context) };
2716
+ }
2717
+ var createProjectInputSchema = z.object({
2718
+ name: z.string().min(1, "name must not be empty"),
2719
+ description: z.string().optional(),
2720
+ color: z.string().optional().describe("UI \u8272 (\u4F8B: #ff6b6b)"),
2721
+ parent_project_id: z.string().uuid().optional(),
2722
+ goal_id: z.string().uuid().optional(),
2723
+ started_at: z.string().datetime().optional().describe("ISO 8601 datetime"),
2724
+ target_end_date: z.string().datetime().optional().describe("ISO 8601 datetime")
2725
+ });
2726
+ async function createProjectHandler(ctx, input) {
2727
+ if (input.parent_project_id) {
2728
+ const parent = await ctx.repos.project.findById(input.parent_project_id);
2729
+ if (!parent) {
2730
+ throw new McpToolError(`Parent project not found: ${input.parent_project_id}`);
2731
+ }
2732
+ }
2733
+ const createInput = {
2734
+ name: input.name,
2735
+ status: "active"
2736
+ };
2737
+ if (input.description !== void 0) createInput.description = input.description;
2738
+ if (input.color !== void 0) createInput.color = input.color;
2739
+ if (input.parent_project_id !== void 0) createInput.parentProjectId = input.parent_project_id;
2740
+ if (input.goal_id !== void 0) createInput.goalId = input.goal_id;
2741
+ if (input.started_at !== void 0) createInput.startedAt = new Date(input.started_at);
2742
+ if (input.target_end_date !== void 0) {
2743
+ createInput.targetEndDate = new Date(input.target_end_date);
2744
+ }
2745
+ const project = await ctx.repos.project.create(createInput);
2746
+ return { project: projectSummaryFrom(project) };
2747
+ }
2748
+ var frequencySchema = z.discriminatedUnion("type", [
2749
+ z.object({ type: z.literal("daily") }),
2750
+ z.object({
2751
+ type: z.literal("weekly"),
2752
+ weekdays: z.array(z.number().int().min(0).max(6)).min(1, "at least 1 weekday")
2753
+ }),
2754
+ z.object({
2755
+ type: z.literal("monthly"),
2756
+ days: z.array(z.number().int().min(1).max(31)).min(1, "at least 1 day")
2757
+ }),
2758
+ z.object({ type: z.literal("custom"), cron: z.string().min(1) })
2759
+ ]);
2760
+ var createRoutineInputSchema = z.object({
2761
+ title: z.string().min(1, "title must not be empty"),
2762
+ description: z.string().optional(),
2763
+ estimated_seconds: z.number().int().positive(),
2764
+ frequency: frequencySchema,
2765
+ project_id: z.string().uuid().optional(),
2766
+ context_id: z.string().uuid().optional(),
2767
+ tag_ids: z.array(z.string().uuid()).optional(),
2768
+ default_scheduled_start_time: z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/, "must be HH:MM").optional(),
2769
+ auto_expand: z.boolean().optional().describe("true \u306A\u3089 Today \u3092\u958B\u3044\u305F\u6642\u306B\u81EA\u52D5\u5C55\u958B (default true)"),
2770
+ start_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional().describe("\u3053\u306E\u65E5\u4ED8\u4EE5\u964D\u306B\u6709\u52B9\u3002YYYY-MM-DD"),
2771
+ end_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional()
2772
+ });
2773
+ async function createRoutineHandler(ctx, input) {
2774
+ if (input.project_id) {
2775
+ const p = await ctx.repos.project.findById(input.project_id);
2776
+ if (!p) throw new McpToolError(`Project not found: ${input.project_id}`);
2777
+ }
2778
+ if (input.context_id) {
2779
+ const c = await ctx.repos.context.findById(input.context_id);
2780
+ if (!c) throw new McpToolError(`Context not found: ${input.context_id}`);
2781
+ }
2782
+ if (input.tag_ids && input.tag_ids.length > 0) {
2783
+ const checked = await Promise.all(
2784
+ input.tag_ids.map(async (id) => ({
2785
+ id,
2786
+ exists: await ctx.repos.tag.findById(id) !== null
2787
+ }))
2788
+ );
2789
+ const missing = checked.filter((c) => !c.exists).map((c) => c.id);
2790
+ if (missing.length > 0) throw new McpToolError(`Tag(s) not found: ${missing.join(", ")}`);
2791
+ }
2792
+ const createInput = {
2793
+ title: input.title,
2794
+ estimatedSeconds: input.estimated_seconds,
2795
+ tagIds: input.tag_ids ?? [],
2796
+ frequency: input.frequency,
2797
+ autoExpand: input.auto_expand ?? true,
2798
+ status: "active"
2799
+ };
2800
+ if (input.description !== void 0) createInput.description = input.description;
2801
+ if (input.project_id !== void 0) createInput.projectId = input.project_id;
2802
+ if (input.context_id !== void 0) createInput.contextId = input.context_id;
2803
+ if (input.default_scheduled_start_time !== void 0) {
2804
+ createInput.defaultScheduledStartTime = input.default_scheduled_start_time;
2805
+ }
2806
+ if (input.start_date !== void 0) createInput.startDate = input.start_date;
2807
+ if (input.end_date !== void 0) createInput.endDate = input.end_date;
2808
+ const routine = await ctx.repos.routine.create(createInput);
2809
+ return { routine: routineSummaryFrom(routine) };
2810
+ }
2811
+ var createTagInputSchema = z.object({
2812
+ name: z.string().min(1, "name must not be empty"),
2813
+ description: z.string().optional(),
2814
+ color: z.string().optional().describe("UI \u8272 (\u4F8B: #ff6b6b)")
2815
+ });
2816
+ async function createTagHandler(ctx, input) {
2817
+ const createInput = {
2818
+ name: input.name,
2819
+ status: "active"
2820
+ };
2821
+ if (input.description !== void 0) createInput.description = input.description;
2822
+ if (input.color !== void 0) createInput.color = input.color;
2823
+ const tag = await ctx.repos.tag.create(createInput);
2824
+ return { tag: tagSummaryFrom(tag) };
2825
+ }
2826
+ var HHMM = /^([01]\d|2[0-3]):[0-5]\d$/;
2827
+ var createTimeBlockInputSchema = z.object({
2828
+ name: z.string().min(1, "name must not be empty"),
2829
+ start_time: z.string().regex(HHMM, "start_time must be HH:MM"),
2830
+ end_time: z.string().regex(HHMM, "end_time must be HH:MM"),
2831
+ weekdays: z.array(z.number().int().min(0).max(6)).min(1, "at least 1 weekday"),
2832
+ default_project_id: z.string().uuid().optional(),
2833
+ default_context_id: z.string().uuid().optional(),
2834
+ default_tag_ids: z.array(z.string().uuid()).optional(),
2835
+ color: z.string().optional(),
2836
+ start_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional(),
2837
+ end_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional()
2838
+ });
2839
+ async function createTimeBlockHandler(ctx, input) {
2840
+ if (input.default_project_id) {
2841
+ const p = await ctx.repos.project.findById(input.default_project_id);
2842
+ if (!p) throw new McpToolError(`Project not found: ${input.default_project_id}`);
2843
+ }
2844
+ if (input.default_context_id) {
2845
+ const c = await ctx.repos.context.findById(input.default_context_id);
2846
+ if (!c) throw new McpToolError(`Context not found: ${input.default_context_id}`);
2847
+ }
2848
+ if (input.default_tag_ids && input.default_tag_ids.length > 0) {
2849
+ const checked = await Promise.all(
2850
+ input.default_tag_ids.map(async (id) => ({
2851
+ id,
2852
+ exists: await ctx.repos.tag.findById(id) !== null
2853
+ }))
2854
+ );
2855
+ const missing = checked.filter((c) => !c.exists).map((c) => c.id);
2856
+ if (missing.length > 0) throw new McpToolError(`Tag(s) not found: ${missing.join(", ")}`);
2857
+ }
2858
+ const createInput = {
2859
+ name: input.name,
2860
+ startTime: input.start_time,
2861
+ endTime: input.end_time,
2862
+ weekdays: input.weekdays,
2863
+ status: "active"
2864
+ };
2865
+ if (input.default_project_id !== void 0) {
2866
+ createInput.defaultProjectId = input.default_project_id;
2867
+ }
2868
+ if (input.default_context_id !== void 0) {
2869
+ createInput.defaultContextId = input.default_context_id;
2870
+ }
2871
+ if (input.default_tag_ids !== void 0) createInput.defaultTagIds = input.default_tag_ids;
2872
+ if (input.color !== void 0) createInput.color = input.color;
2873
+ if (input.start_date !== void 0) createInput.startDate = input.start_date;
2874
+ if (input.end_date !== void 0) createInput.endDate = input.end_date;
2875
+ const block = await ctx.repos.timeBlock.create(createInput);
2876
+ return { time_block: timeBlockSummaryFrom(block) };
2877
+ }
2878
+ var deleteContextInputSchema = z.object({
2879
+ id: z.string().uuid()
2880
+ });
2881
+ async function deleteContextHandler(ctx, input) {
2882
+ const existing = await ctx.repos.context.findById(input.id);
2883
+ if (!existing) {
2884
+ throw new McpToolError(`Context not found: ${input.id}`);
2885
+ }
2886
+ await ctx.repos.context.delete(input.id);
2887
+ return { id: input.id, deleted: true };
2888
+ }
2889
+ var deleteProjectInputSchema = z.object({
2890
+ id: z.string().uuid()
2891
+ });
2892
+ async function deleteProjectHandler(ctx, input) {
2893
+ const existing = await ctx.repos.project.findById(input.id);
2894
+ if (!existing) {
2895
+ throw new McpToolError(`Project not found: ${input.id}`);
2896
+ }
2897
+ await ctx.repos.project.delete(input.id);
2898
+ return { id: input.id, deleted: true };
2899
+ }
2900
+ var deleteRoutineInputSchema = z.object({
2901
+ id: z.string().uuid()
2902
+ });
2903
+ async function deleteRoutineHandler(ctx, input) {
2904
+ const existing = await ctx.repos.routine.findById(input.id);
2905
+ if (!existing) {
2906
+ throw new McpToolError(`Routine not found: ${input.id}`);
2907
+ }
2908
+ await ctx.repos.routine.delete(input.id);
2909
+ return { id: input.id, deleted: true };
2910
+ }
2911
+ var deleteTagInputSchema = z.object({
2912
+ id: z.string().uuid()
2913
+ });
2914
+ async function deleteTagHandler(ctx, input) {
2915
+ const existing = await ctx.repos.tag.findById(input.id);
2916
+ if (!existing) {
2917
+ throw new McpToolError(`Tag not found: ${input.id}`);
2918
+ }
2919
+ await ctx.repos.tag.delete(input.id);
2920
+ return { id: input.id, deleted: true };
2921
+ }
2922
+ var deleteTaskInputSchema = z.object({
2923
+ id: z.string().uuid({ message: "id must be a UUID" }),
2924
+ hard_delete: z.boolean().optional().describe(
2925
+ "true \u306A\u3089 routine_expansions \u306B skip \u8A18\u9332\u305B\u305A\u7269\u7406\u524A\u9664\u306E\u307F\u3002Routine \u7531\u6765\u30BF\u30B9\u30AF\u3092\u5B8C\u5168\u30EA\u30BB\u30C3\u30C8\u3057\u305F\u3044\u4E0A\u7D1A\u8005\u5411\u3051"
2926
+ )
2927
+ });
2928
+ async function deleteTaskHandler(ctx, input) {
2929
+ const existing = await ctx.repos.task.findById(input.id);
2930
+ if (!existing) {
2931
+ throw new McpToolError(`Task not found: ${input.id}`);
2932
+ }
2933
+ if (input.hard_delete === true) {
2934
+ await ctx.repos.task.delete(input.id);
2935
+ } else {
2936
+ await ctx.repos.task.deleteWithRoutineSkip(input.id, todayDateString());
2937
+ }
2938
+ return { id: input.id, deleted: true };
2939
+ }
2940
+ var deleteTimeBlockInputSchema = z.object({
2941
+ id: z.string().uuid()
2942
+ });
2943
+ async function deleteTimeBlockHandler(ctx, input) {
2944
+ const existing = await ctx.repos.timeBlock.findById(input.id);
2945
+ if (!existing) {
2946
+ throw new McpToolError(`TimeBlock not found: ${input.id}`);
2947
+ }
2948
+ await ctx.repos.timeBlock.delete(input.id);
2949
+ return { id: input.id, deleted: true };
2950
+ }
2061
2951
  var doneTaskInputSchema = z.object({
2062
2952
  id: z.string().uuid({ message: "id must be a UUID" })
2063
2953
  });
@@ -2069,6 +2959,127 @@ async function doneTaskHandler(ctx, input) {
2069
2959
  const updated = await completeTask(ctx.repos.task, input.id);
2070
2960
  return { task: taskSummaryFrom(updated) };
2071
2961
  }
2962
+ var interruptTaskInputSchema = z.object({
2963
+ target_task_id: z.string().uuid().describe("\u5272\u308A\u8FBC\u307F\u5BFE\u8C61 (running \u4E2D) \u306E\u30BF\u30B9\u30AF id"),
2964
+ title: z.string().min(1, "interruption title must not be empty"),
2965
+ estimated_seconds: z.number().int().positive(),
2966
+ description: z.string().optional(),
2967
+ project_id: z.string().uuid().optional(),
2968
+ context_id: z.string().uuid().optional(),
2969
+ tag_ids: z.array(z.string().uuid()).optional()
2970
+ });
2971
+ async function interruptTaskHandler(ctx, input) {
2972
+ if (input.project_id) {
2973
+ const p = await ctx.repos.project.findById(input.project_id);
2974
+ if (!p) throw new McpToolError(`Project not found: ${input.project_id}`);
2975
+ }
2976
+ if (input.context_id) {
2977
+ const c = await ctx.repos.context.findById(input.context_id);
2978
+ if (!c) throw new McpToolError(`Context not found: ${input.context_id}`);
2979
+ }
2980
+ if (input.tag_ids && input.tag_ids.length > 0) {
2981
+ const checked = await Promise.all(
2982
+ input.tag_ids.map(async (id) => ({
2983
+ id,
2984
+ exists: await ctx.repos.tag.findById(id) !== null
2985
+ }))
2986
+ );
2987
+ const missing = checked.filter((c) => !c.exists).map((c) => c.id);
2988
+ if (missing.length > 0) throw new McpToolError(`Tag(s) not found: ${missing.join(", ")}`);
2989
+ }
2990
+ const result = await wrapDomainErrors(
2991
+ () => interruptTaskWithAudit(ctx.repos.task, ctx.repos.taskNote, {
2992
+ targetTaskId: input.target_task_id,
2993
+ interruption: {
2994
+ title: input.title,
2995
+ estimatedSeconds: input.estimated_seconds,
2996
+ ...input.description !== void 0 && { description: input.description },
2997
+ ...input.project_id !== void 0 && { projectId: input.project_id },
2998
+ ...input.context_id !== void 0 && { contextId: input.context_id },
2999
+ ...input.tag_ids !== void 0 && { tagIds: input.tag_ids }
3000
+ }
3001
+ })
3002
+ );
3003
+ return {
3004
+ finished_original: taskSummaryFrom(result.finishedOriginal),
3005
+ interruption: taskSummaryFrom(result.interruption),
3006
+ continuation: taskSummaryFrom(result.continuation)
3007
+ };
3008
+ }
3009
+ var DEFAULT_LIMIT = 100;
3010
+ var listContextsInputSchema = z.object({
3011
+ status: z.enum(["active", "archived"]).optional().describe("\u7701\u7565\u6642\u306F active \u306E\u307F"),
3012
+ limit: z.number().int().positive().max(1e3).optional().describe(`\u8FD4\u5374\u4EF6\u6570\u306E\u4E0A\u9650 (default ${DEFAULT_LIMIT})`)
3013
+ });
3014
+ async function listContextsHandler(ctx, input) {
3015
+ const limit = input.limit ?? DEFAULT_LIMIT;
3016
+ const filter = input.status === void 0 ? { status: "active" } : { status: input.status };
3017
+ const all = await ctx.repos.context.findAll(filter);
3018
+ const truncated = all.length > limit;
3019
+ const contexts = (truncated ? all.slice(0, limit) : all).map(contextSummaryFrom);
3020
+ return { contexts, total: all.length, truncated };
3021
+ }
3022
+ var DEFAULT_LIMIT2 = 100;
3023
+ var listProjectsInputSchema = z.object({
3024
+ status: z.enum(["active", "archived"]).optional().describe('\u7701\u7565\u6642\u306F active \u306E\u307F\u3002"archived" \u3067 archived \u306E\u307F\u3001\u4E21\u65B9\u304C\u8981\u308B\u306A\u3089 2 \u56DE\u547C\u3073\u51FA\u3059'),
3025
+ limit: z.number().int().positive().max(1e3).optional().describe(`\u8FD4\u5374\u4EF6\u6570\u306E\u4E0A\u9650 (default ${DEFAULT_LIMIT2})`)
3026
+ });
3027
+ async function listProjectsHandler(ctx, input) {
3028
+ const limit = input.limit ?? DEFAULT_LIMIT2;
3029
+ const filter = input.status === void 0 ? { status: "active" } : { status: input.status };
3030
+ const all = await ctx.repos.project.findAll(filter);
3031
+ const truncated = all.length > limit;
3032
+ const projects = (truncated ? all.slice(0, limit) : all).map(projectSummaryFrom);
3033
+ return { projects, total: all.length, truncated };
3034
+ }
3035
+ var DEFAULT_DAYS = 90;
3036
+ var DEFAULT_LIMIT3 = 100;
3037
+ var listRecentTasksInputSchema = z.object({
3038
+ days: z.number().int().positive().max(365).optional().describe(`\u4ECA\u65E5\u304B\u3089\u9061\u308B\u65E5\u6570 (default ${DEFAULT_DAYS})`),
3039
+ query: z.string().optional().describe("\u6307\u5B9A\u3059\u308B\u3068 fuzzy match \u3067\u985E\u4F3C\u30BF\u30A4\u30C8\u30EB\u3092\u4E0A\u4F4D\u306B\u8FD4\u3059\u3002\u3072\u3089\u304C\u306A\u2194\u30AB\u30BF\u30AB\u30CA\u6B63\u898F\u5316\u8FBC\u307F"),
3040
+ limit: z.number().int().positive().max(1e3).optional().describe(`\u8FD4\u5374\u4EF6\u6570\u306E\u4E0A\u9650 (default ${DEFAULT_LIMIT3})`)
3041
+ });
3042
+ async function listRecentTasksHandler(ctx, input) {
3043
+ const days = input.days ?? DEFAULT_DAYS;
3044
+ const limit = input.limit ?? DEFAULT_LIMIT3;
3045
+ const today = todayDateString();
3046
+ const fromDate = (() => {
3047
+ const d = fromDateString(today);
3048
+ d.setDate(d.getDate() - days);
3049
+ return toDateString(d);
3050
+ })();
3051
+ const all = await ctx.repos.task.findFromDate(fromDate);
3052
+ const ordered = input.query ? findSimilarTasks(input.query, all, limit) : [...all].sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime());
3053
+ const truncated = ordered.length > limit;
3054
+ const tasks = (truncated ? ordered.slice(0, limit) : ordered).map(taskSummaryFrom);
3055
+ return { tasks, total: ordered.length, truncated };
3056
+ }
3057
+ var DEFAULT_LIMIT4 = 100;
3058
+ var listRoutinesInputSchema = z.object({
3059
+ status: z.enum(["active", "paused", "archived"]).optional().describe('\u7701\u7565\u6642\u306F active \u306E\u307F\u3002"paused" \u306F\u4E00\u6642\u505C\u6B62\u4E2D (\u5C55\u958B\u505C\u6B62), "archived" \u306F\u5B8C\u5168\u505C\u6B62'),
3060
+ limit: z.number().int().positive().max(1e3).optional().describe(`\u8FD4\u5374\u4EF6\u6570\u306E\u4E0A\u9650 (default ${DEFAULT_LIMIT4})`)
3061
+ });
3062
+ async function listRoutinesHandler(ctx, input) {
3063
+ const limit = input.limit ?? DEFAULT_LIMIT4;
3064
+ const filter = input.status === void 0 ? { status: "active" } : { status: input.status };
3065
+ const all = await ctx.repos.routine.findAll(filter);
3066
+ const truncated = all.length > limit;
3067
+ const routines = (truncated ? all.slice(0, limit) : all).map(routineSummaryFrom);
3068
+ return { routines, total: all.length, truncated };
3069
+ }
3070
+ var DEFAULT_LIMIT5 = 100;
3071
+ var listTagsInputSchema = z.object({
3072
+ status: z.enum(["active", "archived"]).optional().describe("\u7701\u7565\u6642\u306F active \u306E\u307F"),
3073
+ limit: z.number().int().positive().max(1e3).optional().describe(`\u8FD4\u5374\u4EF6\u6570\u306E\u4E0A\u9650 (default ${DEFAULT_LIMIT5})`)
3074
+ });
3075
+ async function listTagsHandler(ctx, input) {
3076
+ const limit = input.limit ?? DEFAULT_LIMIT5;
3077
+ const filter = input.status === void 0 ? { status: "active" } : { status: input.status };
3078
+ const all = await ctx.repos.tag.findAll(filter);
3079
+ const truncated = all.length > limit;
3080
+ const tags = (truncated ? all.slice(0, limit) : all).map(tagSummaryFrom);
3081
+ return { tags, total: all.length, truncated };
3082
+ }
2072
3083
  var listTasksInputSchema = z.object({
2073
3084
  date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "date must be YYYY-MM-DD").optional().describe("\u5BFE\u8C61\u65E5 (YYYY-MM-DD)\u3002\u7701\u7565\u6642\u306F\u30B5\u30FC\u30D0\u30FC\u306E\u4ECA\u65E5 (Asia/Tokyo) \u3092\u4F7F\u3046")
2074
3085
  });
@@ -2077,6 +3088,19 @@ async function listTasksHandler(ctx, input) {
2077
3088
  const tasks = await ctx.repos.task.findByDate(date);
2078
3089
  return { date, tasks: tasks.map(taskSummaryFrom) };
2079
3090
  }
3091
+ var DEFAULT_LIMIT6 = 100;
3092
+ var listTimeBlocksInputSchema = z.object({
3093
+ status: z.enum(["active", "archived"]).optional().describe("\u7701\u7565\u6642\u306F active \u306E\u307F"),
3094
+ limit: z.number().int().positive().max(1e3).optional().describe(`\u8FD4\u5374\u4EF6\u6570\u306E\u4E0A\u9650 (default ${DEFAULT_LIMIT6})`)
3095
+ });
3096
+ async function listTimeBlocksHandler(ctx, input) {
3097
+ const limit = input.limit ?? DEFAULT_LIMIT6;
3098
+ const filter = input.status === void 0 ? { status: "active" } : { status: input.status };
3099
+ const all = await ctx.repos.timeBlock.findAll(filter);
3100
+ const truncated = all.length > limit;
3101
+ const blocks = (truncated ? all.slice(0, limit) : all).map(timeBlockSummaryFrom);
3102
+ return { time_blocks: blocks, total: all.length, truncated };
3103
+ }
2080
3104
  var pauseTaskInputSchema = z.object({
2081
3105
  id: z.string().uuid({ message: "id must be a UUID" })
2082
3106
  });
@@ -2088,6 +3112,69 @@ async function pauseTaskHandler(ctx, input) {
2088
3112
  const updated = await pauseTask(ctx.repos.task, input.id);
2089
3113
  return { task: taskSummaryFrom(updated) };
2090
3114
  }
3115
+ var reopenTaskInputSchema = z.object({
3116
+ id: z.string().uuid({ message: "id must be a UUID" })
3117
+ });
3118
+ async function reopenTaskHandler(ctx, input) {
3119
+ const existing = await ctx.repos.task.findById(input.id);
3120
+ if (!existing) {
3121
+ throw new McpToolError(`Task not found: ${input.id}`);
3122
+ }
3123
+ const updated = await reopenTask(ctx.repos.task, input.id);
3124
+ return { task: taskSummaryFrom(updated) };
3125
+ }
3126
+ var reorderTaskInputSchema = z.object({
3127
+ id: z.string().uuid(),
3128
+ order_index: z.number().int(),
3129
+ scheduled_start_time: z.union([z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/, "must be HH:MM"), z.null()]).optional().describe(
3130
+ "\u6307\u5B9A\u3059\u308B\u3068\u540C\u6642\u306B\u66F8\u304D\u63DB\u3048\u308B\u3002null=\u672A\u5272\u5F53\u30B0\u30EB\u30FC\u30D7\u306B\u79FB\u52D5\u3002HH:MM=\u8A72\u5F53\u30D6\u30ED\u30C3\u30AF\u306B\u79FB\u52D5\u3002\u7701\u7565=\u5909\u66F4\u306A\u3057"
3131
+ )
3132
+ });
3133
+ async function reorderTaskHandler(ctx, input) {
3134
+ const existing = await ctx.repos.task.findById(input.id);
3135
+ if (!existing) {
3136
+ throw new McpToolError(`Task not found: ${input.id}`);
3137
+ }
3138
+ const patch = { orderIndex: input.order_index };
3139
+ if (input.scheduled_start_time !== void 0) {
3140
+ patch.scheduledStartTime = input.scheduled_start_time === null ? void 0 : input.scheduled_start_time;
3141
+ }
3142
+ const updated = await ctx.repos.task.update(input.id, patch);
3143
+ return { task: taskSummaryFrom(updated) };
3144
+ }
3145
+ var ACTUAL_TIME_RE = /^([01]\d|2[0-3]):[0-5]\d(?::[0-5]\d)?$/;
3146
+ var setActualTimesInputSchema = z.object({
3147
+ id: z.string().uuid(),
3148
+ start_time: z.union([z.string().regex(ACTUAL_TIME_RE, "must be HH:MM or HH:MM:SS"), z.null()]).describe("HH:MM \u307E\u305F\u306F HH:MM:SS\u3002null \u306A\u3089\u5B9F\u7E3E\u958B\u59CB\u306A\u3057\u3002end_time \u306E\u307F\u6307\u5B9A\u306F\u30A8\u30E9\u30FC"),
3149
+ end_time: z.union([z.string().regex(ACTUAL_TIME_RE, "must be HH:MM or HH:MM:SS"), z.null()]).describe("HH:MM \u307E\u305F\u306F HH:MM:SS\u3002null \u306A\u3089\u672A\u5B8C\u4E86 (running \u6271\u3044)")
3150
+ });
3151
+ async function setActualTimesHandler(ctx, input) {
3152
+ const existing = await ctx.repos.task.findById(input.id);
3153
+ if (!existing) {
3154
+ throw new McpToolError(`Task not found: ${input.id}`);
3155
+ }
3156
+ const patch = await wrapDomainErrors(
3157
+ () => Promise.resolve(
3158
+ buildSetActualTimesPatch(existing, {
3159
+ startTime: input.start_time,
3160
+ endTime: input.end_time
3161
+ })
3162
+ )
3163
+ );
3164
+ const updated = await ctx.repos.task.update(input.id, patch);
3165
+ return { task: taskSummaryFrom(updated) };
3166
+ }
3167
+ var skipTaskInputSchema = z.object({
3168
+ id: z.string().uuid({ message: "id must be a UUID" })
3169
+ });
3170
+ async function skipTaskHandler(ctx, input) {
3171
+ const existing = await ctx.repos.task.findById(input.id);
3172
+ if (!existing) {
3173
+ throw new McpToolError(`Task not found: ${input.id}`);
3174
+ }
3175
+ const updated = await skipTask(ctx.repos.task, input.id);
3176
+ return { task: taskSummaryFrom(updated) };
3177
+ }
2091
3178
  var startTaskInputSchema = z.object({
2092
3179
  id: z.string().uuid({ message: "id must be a UUID" })
2093
3180
  });
@@ -2099,6 +3186,332 @@ async function startTaskHandler(ctx, input) {
2099
3186
  const updated = await startTask(ctx.repos.task, input.id);
2100
3187
  return { task: taskSummaryFrom(updated) };
2101
3188
  }
3189
+ var updateContextInputSchema = z.object({
3190
+ id: z.string().uuid(),
3191
+ name: z.string().min(1).optional(),
3192
+ description: z.union([z.string(), z.null()]).optional(),
3193
+ icon: z.union([z.string(), z.null()]).optional(),
3194
+ color: z.union([z.string(), z.null()]).optional(),
3195
+ status: z.enum(["active", "archived"]).optional().describe("archived \u5316\u306F archive_context tool \u63A8\u5968")
3196
+ });
3197
+ async function updateContextHandler(ctx, input) {
3198
+ const existing = await ctx.repos.context.findById(input.id);
3199
+ if (!existing) {
3200
+ throw new McpToolError(`Context not found: ${input.id}`);
3201
+ }
3202
+ const patch = {};
3203
+ if (input.name !== void 0) patch.name = input.name;
3204
+ if (input.description !== void 0) {
3205
+ patch.description = input.description === null ? void 0 : input.description;
3206
+ }
3207
+ if (input.icon !== void 0) {
3208
+ patch.icon = input.icon === null ? void 0 : input.icon;
3209
+ }
3210
+ if (input.color !== void 0) {
3211
+ patch.color = input.color === null ? void 0 : input.color;
3212
+ }
3213
+ if (input.status !== void 0) patch.status = input.status;
3214
+ const updated = await ctx.repos.context.update(input.id, patch);
3215
+ return { context: contextSummaryFrom(updated) };
3216
+ }
3217
+ var updateProjectInputSchema = z.object({
3218
+ id: z.string().uuid(),
3219
+ name: z.string().min(1).optional(),
3220
+ description: z.union([z.string(), z.null()]).optional(),
3221
+ color: z.union([z.string(), z.null()]).optional(),
3222
+ parent_project_id: z.union([z.string().uuid(), z.null()]).optional(),
3223
+ goal_id: z.union([z.string().uuid(), z.null()]).optional(),
3224
+ started_at: z.union([z.string().datetime(), z.null()]).optional(),
3225
+ target_end_date: z.union([z.string().datetime(), z.null()]).optional(),
3226
+ status: z.enum(["active", "archived"]).optional().describe("archived \u5316\u306F archive_project tool \u63A8\u5968 (\u4E21\u65B9\u3068\u3082 archived_at \u3092\u8A2D\u5B9A\u3059\u308B)")
3227
+ });
3228
+ async function updateProjectHandler(ctx, input) {
3229
+ const existing = await ctx.repos.project.findById(input.id);
3230
+ if (!existing) {
3231
+ throw new McpToolError(`Project not found: ${input.id}`);
3232
+ }
3233
+ if (input.parent_project_id !== void 0 && input.parent_project_id !== null) {
3234
+ const parent = await ctx.repos.project.findById(input.parent_project_id);
3235
+ if (!parent) {
3236
+ throw new McpToolError(`Parent project not found: ${input.parent_project_id}`);
3237
+ }
3238
+ if (parent.id === input.id) {
3239
+ throw new McpToolError("A project cannot be its own parent");
3240
+ }
3241
+ }
3242
+ const patch = {};
3243
+ if (input.name !== void 0) patch.name = input.name;
3244
+ if (input.description !== void 0) {
3245
+ patch.description = input.description === null ? void 0 : input.description;
3246
+ }
3247
+ if (input.color !== void 0) {
3248
+ patch.color = input.color === null ? void 0 : input.color;
3249
+ }
3250
+ if (input.parent_project_id !== void 0) {
3251
+ patch.parentProjectId = input.parent_project_id === null ? void 0 : input.parent_project_id;
3252
+ }
3253
+ if (input.goal_id !== void 0) {
3254
+ patch.goalId = input.goal_id === null ? void 0 : input.goal_id;
3255
+ }
3256
+ if (input.started_at !== void 0) {
3257
+ patch.startedAt = input.started_at === null ? void 0 : new Date(input.started_at);
3258
+ }
3259
+ if (input.target_end_date !== void 0) {
3260
+ patch.targetEndDate = input.target_end_date === null ? void 0 : new Date(input.target_end_date);
3261
+ }
3262
+ if (input.status !== void 0) patch.status = input.status;
3263
+ const updated = await ctx.repos.project.update(input.id, patch);
3264
+ return { project: projectSummaryFrom(updated) };
3265
+ }
3266
+ var frequencySchema2 = z.discriminatedUnion("type", [
3267
+ z.object({ type: z.literal("daily") }),
3268
+ z.object({
3269
+ type: z.literal("weekly"),
3270
+ weekdays: z.array(z.number().int().min(0).max(6)).min(1)
3271
+ }),
3272
+ z.object({
3273
+ type: z.literal("monthly"),
3274
+ days: z.array(z.number().int().min(1).max(31)).min(1)
3275
+ }),
3276
+ z.object({ type: z.literal("custom"), cron: z.string().min(1) })
3277
+ ]);
3278
+ var updateRoutineInputSchema = z.object({
3279
+ id: z.string().uuid(),
3280
+ title: z.string().min(1).optional(),
3281
+ description: z.union([z.string(), z.null()]).optional(),
3282
+ estimated_seconds: z.number().int().positive().optional(),
3283
+ frequency: frequencySchema2.optional(),
3284
+ project_id: z.union([z.string().uuid(), z.null()]).optional(),
3285
+ context_id: z.union([z.string().uuid(), z.null()]).optional(),
3286
+ tag_ids: z.array(z.string().uuid()).optional(),
3287
+ default_scheduled_start_time: z.union([z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/, "must be HH:MM"), z.null()]).optional(),
3288
+ auto_expand: z.boolean().optional(),
3289
+ start_date: z.union([z.string().regex(/^\d{4}-\d{2}-\d{2}$/), z.null()]).optional(),
3290
+ end_date: z.union([z.string().regex(/^\d{4}-\d{2}-\d{2}$/), z.null()]).optional(),
3291
+ status: z.enum(["active", "paused", "archived"]).optional()
3292
+ });
3293
+ async function updateRoutineHandler(ctx, input) {
3294
+ const existing = await ctx.repos.routine.findById(input.id);
3295
+ if (!existing) {
3296
+ throw new McpToolError(`Routine not found: ${input.id}`);
3297
+ }
3298
+ if (input.project_id !== void 0 && input.project_id !== null) {
3299
+ const p = await ctx.repos.project.findById(input.project_id);
3300
+ if (!p) throw new McpToolError(`Project not found: ${input.project_id}`);
3301
+ }
3302
+ if (input.context_id !== void 0 && input.context_id !== null) {
3303
+ const c = await ctx.repos.context.findById(input.context_id);
3304
+ if (!c) throw new McpToolError(`Context not found: ${input.context_id}`);
3305
+ }
3306
+ if (input.tag_ids !== void 0 && input.tag_ids.length > 0) {
3307
+ const checked = await Promise.all(
3308
+ input.tag_ids.map(async (id) => ({
3309
+ id,
3310
+ exists: await ctx.repos.tag.findById(id) !== null
3311
+ }))
3312
+ );
3313
+ const missing = checked.filter((c) => !c.exists).map((c) => c.id);
3314
+ if (missing.length > 0) throw new McpToolError(`Tag(s) not found: ${missing.join(", ")}`);
3315
+ }
3316
+ const patch = {};
3317
+ if (input.title !== void 0) patch.title = input.title;
3318
+ if (input.description !== void 0) {
3319
+ patch.description = input.description === null ? void 0 : input.description;
3320
+ }
3321
+ if (input.estimated_seconds !== void 0) patch.estimatedSeconds = input.estimated_seconds;
3322
+ if (input.frequency !== void 0) patch.frequency = input.frequency;
3323
+ if (input.project_id !== void 0) {
3324
+ patch.projectId = input.project_id === null ? void 0 : input.project_id;
3325
+ }
3326
+ if (input.context_id !== void 0) {
3327
+ patch.contextId = input.context_id === null ? void 0 : input.context_id;
3328
+ }
3329
+ if (input.tag_ids !== void 0) patch.tagIds = input.tag_ids;
3330
+ if (input.default_scheduled_start_time !== void 0) {
3331
+ patch.defaultScheduledStartTime = input.default_scheduled_start_time === null ? void 0 : input.default_scheduled_start_time;
3332
+ }
3333
+ if (input.auto_expand !== void 0) patch.autoExpand = input.auto_expand;
3334
+ if (input.start_date !== void 0) {
3335
+ patch.startDate = input.start_date === null ? void 0 : input.start_date;
3336
+ }
3337
+ if (input.end_date !== void 0) {
3338
+ patch.endDate = input.end_date === null ? void 0 : input.end_date;
3339
+ }
3340
+ if (input.status !== void 0) patch.status = input.status;
3341
+ const updated = await ctx.repos.routine.update(input.id, patch);
3342
+ const today = todayDateString();
3343
+ const futureTasks = await ctx.repos.task.findByRoutineIdFromDate(input.id, today);
3344
+ const targets = pickPropagationTargets(updated, futureTasks, today);
3345
+ if (targets.length > 0) {
3346
+ const { auditingTask } = ctx.repos;
3347
+ if (!auditingTask) {
3348
+ throw new McpToolError(
3349
+ "auditingTask is not configured. Routine propagation requires audit lookups to be enabled (createNodeAppContext sets this up automatically)."
3350
+ );
3351
+ }
3352
+ const taskPatch = buildRoutineTaskPatch(updated);
3353
+ await Promise.all(
3354
+ targets.map(
3355
+ (task) => auditingTask.updateWithCause(task.id, taskPatch, "routine-propagation")
3356
+ )
3357
+ );
3358
+ }
3359
+ return {
3360
+ routine: routineSummaryFrom(updated),
3361
+ propagated_task_count: targets.length
3362
+ };
3363
+ }
3364
+ var updateTagInputSchema = z.object({
3365
+ id: z.string().uuid(),
3366
+ name: z.string().min(1).optional(),
3367
+ description: z.union([z.string(), z.null()]).optional(),
3368
+ color: z.union([z.string(), z.null()]).optional(),
3369
+ status: z.enum(["active", "archived"]).optional().describe("archived \u5316\u306F archive_tag tool \u63A8\u5968")
3370
+ });
3371
+ async function updateTagHandler(ctx, input) {
3372
+ const existing = await ctx.repos.tag.findById(input.id);
3373
+ if (!existing) {
3374
+ throw new McpToolError(`Tag not found: ${input.id}`);
3375
+ }
3376
+ const patch = {};
3377
+ if (input.name !== void 0) patch.name = input.name;
3378
+ if (input.description !== void 0) {
3379
+ patch.description = input.description === null ? void 0 : input.description;
3380
+ }
3381
+ if (input.color !== void 0) {
3382
+ patch.color = input.color === null ? void 0 : input.color;
3383
+ }
3384
+ if (input.status !== void 0) patch.status = input.status;
3385
+ const updated = await ctx.repos.tag.update(input.id, patch);
3386
+ return { tag: tagSummaryFrom(updated) };
3387
+ }
3388
+ var updateTaskInputSchema = z.object({
3389
+ id: z.string().uuid({ message: "id must be a UUID" }),
3390
+ title: z.string().min(1).optional(),
3391
+ description: z.union([z.string(), z.null()]).optional(),
3392
+ estimated_seconds: z.number().int().positive().optional(),
3393
+ scheduled_start_time: z.union([z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/, "must be HH:MM"), z.null()]).optional(),
3394
+ project_id: z.union([z.string().uuid(), z.null()]).optional(),
3395
+ context_id: z.union([z.string().uuid(), z.null()]).optional(),
3396
+ tag_ids: z.array(z.string().uuid()).optional(),
3397
+ notify_at_scheduled_start: z.boolean().optional(),
3398
+ is_background: z.boolean().optional()
3399
+ });
3400
+ async function validateReferences(ctx, refs) {
3401
+ if (refs.projectId) {
3402
+ const project = await ctx.repos.project.findById(refs.projectId);
3403
+ if (!project) throw new McpToolError(`Project not found: ${refs.projectId}`);
3404
+ }
3405
+ if (refs.contextId) {
3406
+ const context = await ctx.repos.context.findById(refs.contextId);
3407
+ if (!context) throw new McpToolError(`Context not found: ${refs.contextId}`);
3408
+ }
3409
+ if (refs.tagIds && refs.tagIds.length > 0) {
3410
+ const checked = await Promise.all(
3411
+ refs.tagIds.map(async (id) => ({ id, exists: await ctx.repos.tag.findById(id) !== null }))
3412
+ );
3413
+ const missing = checked.filter((c) => !c.exists).map((c) => c.id);
3414
+ if (missing.length > 0) {
3415
+ throw new McpToolError(`Tag(s) not found: ${missing.join(", ")}`);
3416
+ }
3417
+ }
3418
+ }
3419
+ async function updateTaskHandler(ctx, input) {
3420
+ const existing = await ctx.repos.task.findById(input.id);
3421
+ if (!existing) {
3422
+ throw new McpToolError(`Task not found: ${input.id}`);
3423
+ }
3424
+ await validateReferences(ctx, {
3425
+ projectId: input.project_id ?? void 0,
3426
+ contextId: input.context_id ?? void 0,
3427
+ tagIds: input.tag_ids
3428
+ });
3429
+ const patch = {};
3430
+ if (input.title !== void 0) patch.title = input.title;
3431
+ if (input.description !== void 0) {
3432
+ patch.description = input.description === null ? void 0 : input.description;
3433
+ }
3434
+ if (input.estimated_seconds !== void 0) patch.estimatedSeconds = input.estimated_seconds;
3435
+ if (input.scheduled_start_time !== void 0) {
3436
+ patch.scheduledStartTime = input.scheduled_start_time === null ? void 0 : input.scheduled_start_time;
3437
+ }
3438
+ if (input.project_id !== void 0) {
3439
+ patch.projectId = input.project_id === null ? void 0 : input.project_id;
3440
+ }
3441
+ if (input.context_id !== void 0) {
3442
+ patch.contextId = input.context_id === null ? void 0 : input.context_id;
3443
+ }
3444
+ if (input.tag_ids !== void 0) patch.tagIds = input.tag_ids;
3445
+ if (input.notify_at_scheduled_start !== void 0) {
3446
+ patch.notifyAtScheduledStart = input.notify_at_scheduled_start;
3447
+ }
3448
+ if (input.is_background !== void 0) patch.isBackground = input.is_background;
3449
+ const updated = await ctx.repos.task.update(input.id, patch);
3450
+ return { task: taskSummaryFrom(updated) };
3451
+ }
3452
+ var HHMM2 = /^([01]\d|2[0-3]):[0-5]\d$/;
3453
+ var updateTimeBlockInputSchema = z.object({
3454
+ id: z.string().uuid(),
3455
+ name: z.string().min(1).optional(),
3456
+ start_time: z.string().regex(HHMM2, "start_time must be HH:MM").optional(),
3457
+ end_time: z.string().regex(HHMM2, "end_time must be HH:MM").optional(),
3458
+ weekdays: z.array(z.number().int().min(0).max(6)).min(1).optional(),
3459
+ default_project_id: z.union([z.string().uuid(), z.null()]).optional(),
3460
+ default_context_id: z.union([z.string().uuid(), z.null()]).optional(),
3461
+ default_tag_ids: z.array(z.string().uuid()).optional(),
3462
+ color: z.union([z.string(), z.null()]).optional(),
3463
+ start_date: z.union([z.string().regex(/^\d{4}-\d{2}-\d{2}$/), z.null()]).optional(),
3464
+ end_date: z.union([z.string().regex(/^\d{4}-\d{2}-\d{2}$/), z.null()]).optional(),
3465
+ status: z.enum(["active", "archived"]).optional()
3466
+ });
3467
+ async function updateTimeBlockHandler(ctx, input) {
3468
+ const existing = await ctx.repos.timeBlock.findById(input.id);
3469
+ if (!existing) {
3470
+ throw new McpToolError(`TimeBlock not found: ${input.id}`);
3471
+ }
3472
+ if (input.default_project_id !== void 0 && input.default_project_id !== null) {
3473
+ const p = await ctx.repos.project.findById(input.default_project_id);
3474
+ if (!p) throw new McpToolError(`Project not found: ${input.default_project_id}`);
3475
+ }
3476
+ if (input.default_context_id !== void 0 && input.default_context_id !== null) {
3477
+ const c = await ctx.repos.context.findById(input.default_context_id);
3478
+ if (!c) throw new McpToolError(`Context not found: ${input.default_context_id}`);
3479
+ }
3480
+ if (input.default_tag_ids !== void 0 && input.default_tag_ids.length > 0) {
3481
+ const checked = await Promise.all(
3482
+ input.default_tag_ids.map(async (id) => ({
3483
+ id,
3484
+ exists: await ctx.repos.tag.findById(id) !== null
3485
+ }))
3486
+ );
3487
+ const missing = checked.filter((c) => !c.exists).map((c) => c.id);
3488
+ if (missing.length > 0) throw new McpToolError(`Tag(s) not found: ${missing.join(", ")}`);
3489
+ }
3490
+ const patch = {};
3491
+ if (input.name !== void 0) patch.name = input.name;
3492
+ if (input.start_time !== void 0) patch.startTime = input.start_time;
3493
+ if (input.end_time !== void 0) patch.endTime = input.end_time;
3494
+ if (input.weekdays !== void 0) patch.weekdays = input.weekdays;
3495
+ if (input.default_project_id !== void 0) {
3496
+ patch.defaultProjectId = input.default_project_id === null ? void 0 : input.default_project_id;
3497
+ }
3498
+ if (input.default_context_id !== void 0) {
3499
+ patch.defaultContextId = input.default_context_id === null ? void 0 : input.default_context_id;
3500
+ }
3501
+ if (input.default_tag_ids !== void 0) patch.defaultTagIds = input.default_tag_ids;
3502
+ if (input.color !== void 0) {
3503
+ patch.color = input.color === null ? void 0 : input.color;
3504
+ }
3505
+ if (input.start_date !== void 0) {
3506
+ patch.startDate = input.start_date === null ? void 0 : input.start_date;
3507
+ }
3508
+ if (input.end_date !== void 0) {
3509
+ patch.endDate = input.end_date === null ? void 0 : input.end_date;
3510
+ }
3511
+ if (input.status !== void 0) patch.status = input.status;
3512
+ const updated = await ctx.repos.timeBlock.update(input.id, patch);
3513
+ return { time_block: timeBlockSummaryFrom(updated) };
3514
+ }
2102
3515
 
2103
3516
  // src/server.ts
2104
3517
  function createDefaultContextFactory() {
@@ -2144,11 +3557,29 @@ function createMcpServer(opts) {
2144
3557
  "add_task",
2145
3558
  {
2146
3559
  title: "Add task",
2147
- description: "\u30BF\u30B9\u30AF\u3092\u8FFD\u52A0\u3059\u308B\u3002\u65E5\u4ED8\u7701\u7565\u6642\u306F\u4ECA\u65E5\u3002estimated_seconds \u7701\u7565\u6642\u306F 1800 (30 \u5206)\u3002",
3560
+ description: "\u30BF\u30B9\u30AF\u3092\u8FFD\u52A0\u3059\u308B\u3002\u65E5\u4ED8\u7701\u7565\u6642\u306F\u4ECA\u65E5\u3002estimated_seconds \u7701\u7565\u6642\u306F 1800 (30 \u5206)\u3002project_id / context_id / tag_ids \u306F\u4E8B\u524D\u306B list_projects / list_contexts / list_tags \u3067\u53D6\u5F97\u3057\u305F UUID \u3092\u6E21\u3059\u3002",
2148
3561
  inputSchema: addTaskInputSchema.shape
2149
3562
  },
2150
3563
  async (input) => wrapHandler(ctxFactory, addTaskHandler, input)
2151
3564
  );
3565
+ server.registerTool(
3566
+ "update_task",
3567
+ {
3568
+ title: "Update task",
3569
+ description: "\u30BF\u30B9\u30AF\u306E\u30E6\u30FC\u30B6\u30FC\u7DE8\u96C6\u53EF\u80FD\u30D5\u30A3\u30FC\u30EB\u30C9\u3092\u90E8\u5206\u66F4\u65B0\u3059\u308B\u3002undefined \u306F\u5909\u66F4\u306A\u3057\u3001null \u306F\u660E\u793A\u30AF\u30EA\u30A2\u3002status / order_index / actual_start_time \u7B49\u306E\u30B7\u30B9\u30C6\u30E0\u30D5\u30A3\u30FC\u30EB\u30C9\u306F\u5225 tool (start/pause/done/skip/reopen/reorder) \u7D4C\u7531\u3067\u5909\u66F4\u3059\u308B\u3002tag_ids \u306F\u5168\u7F6E\u63DB\u3002",
3570
+ inputSchema: updateTaskInputSchema.shape
3571
+ },
3572
+ async (input) => wrapHandler(ctxFactory, updateTaskHandler, input)
3573
+ );
3574
+ server.registerTool(
3575
+ "delete_task",
3576
+ {
3577
+ title: "Delete task",
3578
+ description: "\u30BF\u30B9\u30AF\u3092\u524A\u9664\u3059\u308B\u3002Routine \u7531\u6765\u30BF\u30B9\u30AF\u3092\u4ECA\u65E5\u4EE5\u964D\u306B\u6D88\u3059\u5834\u5408\u306F routine_expansions \u306B skip \u8A18\u9332\u3059\u308B (default)\u3002hard_delete: true \u306A\u3089\u7269\u7406\u524A\u9664\u306E\u307F\u3067\u3001\u6B21\u306E reload \u3067\u518D\u5C55\u958B\u3055\u308C\u308B (\u4E0A\u7D1A\u8005\u5411\u3051)\u3002",
3579
+ inputSchema: deleteTaskInputSchema.shape
3580
+ },
3581
+ async (input) => wrapHandler(ctxFactory, deleteTaskHandler, input)
3582
+ );
2152
3583
  server.registerTool(
2153
3584
  "start_task",
2154
3585
  {
@@ -2176,12 +3607,300 @@ function createMcpServer(opts) {
2176
3607
  },
2177
3608
  async (input) => wrapHandler(ctxFactory, doneTaskHandler, input)
2178
3609
  );
3610
+ server.registerTool(
3611
+ "skip_task",
3612
+ {
3613
+ title: "Skip task",
3614
+ description: "\u30BF\u30B9\u30AF\u3092 skipped \u306B\u9077\u79FB\u3055\u305B\u308B (\u3084\u3089\u306A\u3044\u3053\u3068\u304C\u6C7A\u307E\u3063\u305F\u30BF\u30B9\u30AF\u7528)\u3002reopen_task \u3067 planned \u306B\u623B\u305B\u308B\u3002",
3615
+ inputSchema: skipTaskInputSchema.shape
3616
+ },
3617
+ async (input) => wrapHandler(ctxFactory, skipTaskHandler, input)
3618
+ );
3619
+ server.registerTool(
3620
+ "reopen_task",
3621
+ {
3622
+ title: "Reopen task",
3623
+ description: "completed / skipped \u306E\u30BF\u30B9\u30AF\u3092 planned \u306B\u623B\u3059\u3002planned/running/paused \u306E\u3068\u304D\u306F no-op\u3002",
3624
+ inputSchema: reopenTaskInputSchema.shape
3625
+ },
3626
+ async (input) => wrapHandler(ctxFactory, reopenTaskHandler, input)
3627
+ );
3628
+ server.registerTool(
3629
+ "list_projects",
3630
+ {
3631
+ title: "List projects",
3632
+ description: "\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u4E00\u89A7\u3092\u8FD4\u3059\u3002`add_task` / `update_task` \u306E `project_id` \u306B\u6E21\u3059 UUID \u3092\u53D6\u5F97\u3059\u308B\u305F\u3081\u306B\u4F7F\u3046\u3002\u65E2\u5B9A\u306F active \u306E\u307F\u3001`status: archived` \u3067 archived \u3082\u53D6\u5F97\u53EF\u80FD\u3002`limit` \u65E2\u5B9A 100\u3002",
3633
+ inputSchema: listProjectsInputSchema.shape
3634
+ },
3635
+ async (input) => wrapHandler(ctxFactory, listProjectsHandler, input)
3636
+ );
3637
+ server.registerTool(
3638
+ "list_tags",
3639
+ {
3640
+ title: "List tags",
3641
+ description: "\u30BF\u30B0\u4E00\u89A7\u3092\u8FD4\u3059\u3002`add_task` / `update_task` \u306E `tag_ids` \u306B\u6E21\u3059 UUID \u3092\u53D6\u5F97\u3059\u308B\u305F\u3081\u306B\u4F7F\u3046\u3002`limit` \u65E2\u5B9A 100\u3002",
3642
+ inputSchema: listTagsInputSchema.shape
3643
+ },
3644
+ async (input) => wrapHandler(ctxFactory, listTagsHandler, input)
3645
+ );
3646
+ server.registerTool(
3647
+ "list_contexts",
3648
+ {
3649
+ title: "List contexts",
3650
+ description: "\u30B3\u30F3\u30C6\u30AD\u30B9\u30C8 (\u4F5C\u696D\u74B0\u5883\u30BF\u30B0\u3002\u4F8B: home / office / mobile) \u4E00\u89A7\u3092\u8FD4\u3059\u3002`add_task` / `update_task` \u306E `context_id` \u306B\u6E21\u3059 UUID \u3092\u53D6\u5F97\u3059\u308B\u305F\u3081\u306B\u4F7F\u3046\u3002`limit` \u65E2\u5B9A 100\u3002",
3651
+ inputSchema: listContextsInputSchema.shape
3652
+ },
3653
+ async (input) => wrapHandler(ctxFactory, listContextsHandler, input)
3654
+ );
3655
+ server.registerTool(
3656
+ "list_routines",
3657
+ {
3658
+ title: "List routines",
3659
+ description: '\u7E70\u308A\u8FD4\u3057\u30BF\u30B9\u30AF\u306E\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8 (\u30EB\u30FC\u30C1\u30F3) \u4E00\u89A7\u3092\u8FD4\u3059\u3002`frequency` \u306F discriminated union (`{type: "daily"}` / `{type: "weekly", weekdays: [1,3,5]}` \u7B49) \u306E\u307E\u307E\u3002`limit` \u65E2\u5B9A 100\u3002',
3660
+ inputSchema: listRoutinesInputSchema.shape
3661
+ },
3662
+ async (input) => wrapHandler(ctxFactory, listRoutinesHandler, input)
3663
+ );
3664
+ server.registerTool(
3665
+ "list_time_blocks",
3666
+ {
3667
+ title: "List time blocks",
3668
+ description: "\u30BF\u30A4\u30E0\u30D6\u30ED\u30C3\u30AF (\u6642\u9593\u5E2F\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8\u3002\u4F8B: \u671D\u306E\u96C6\u4E2D\u6642\u9593 09:00-12:00) \u4E00\u89A7\u3092\u8FD4\u3059\u3002`limit` \u65E2\u5B9A 100\u3002",
3669
+ inputSchema: listTimeBlocksInputSchema.shape
3670
+ },
3671
+ async (input) => wrapHandler(ctxFactory, listTimeBlocksHandler, input)
3672
+ );
3673
+ server.registerTool(
3674
+ "list_recent_tasks",
3675
+ {
3676
+ title: "List recent tasks",
3677
+ description: "\u904E\u53BB N \u65E5 (\u65E2\u5B9A 90) \u306E\u30BF\u30B9\u30AF\u3092\u8FD4\u3059\u3002`query` \u3092\u6307\u5B9A\u3059\u308B\u3068 fuzzy match \u3067\u985E\u4F3C\u30BF\u30A4\u30C8\u30EB\u4E0A\u4F4D\u3092\u8FD4\u3059 (\u3072\u3089\u304C\u306A\u2194\u30AB\u30BF\u30AB\u30CA\u6B63\u898F\u5316\u8FBC\u307F)\u3002\u300C\u6700\u8FD1\u3084\u3063\u305F\u4F3C\u305F\u30BF\u30B9\u30AF\u3092\u53C2\u8003\u306B\u65B0\u3057\u3044\u30BF\u30B9\u30AF\u3092\u4F5C\u3063\u3066\u300D\u306E\u3088\u3046\u306A AI \u64CD\u4F5C\u5411\u3051\u3002`limit` \u65E2\u5B9A 100\u3002",
3678
+ inputSchema: listRecentTasksInputSchema.shape
3679
+ },
3680
+ async (input) => wrapHandler(ctxFactory, listRecentTasksHandler, input)
3681
+ );
3682
+ server.registerTool(
3683
+ "create_project",
3684
+ {
3685
+ title: "Create project",
3686
+ description: "\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3092\u65B0\u898F\u4F5C\u6210\u3059\u308B\u3002`name` \u5FC5\u9808\u3001`status` \u306F active \u56FA\u5B9A\u3002`parent_project_id` \u3092\u6307\u5B9A\u3059\u308B\u3068\u30B5\u30D6\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3002",
3687
+ inputSchema: createProjectInputSchema.shape
3688
+ },
3689
+ async (input) => wrapHandler(ctxFactory, createProjectHandler, input)
3690
+ );
3691
+ server.registerTool(
3692
+ "update_project",
3693
+ {
3694
+ title: "Update project",
3695
+ description: "\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3092\u90E8\u5206\u66F4\u65B0\u3059\u308B\u3002undefined=\u5909\u66F4\u306A\u3057\u3001null=\u660E\u793A\u30AF\u30EA\u30A2\u3002`status` \u306E archived \u5316\u306F archive_project tool \u63A8\u5968\u3002\u81EA\u5206\u81EA\u8EAB\u3092 parent \u306B\u3057\u3088\u3046\u3068\u3059\u308B\u3068\u30A8\u30E9\u30FC\u3002",
3696
+ inputSchema: updateProjectInputSchema.shape
3697
+ },
3698
+ async (input) => wrapHandler(ctxFactory, updateProjectHandler, input)
3699
+ );
3700
+ server.registerTool(
3701
+ "archive_project",
3702
+ {
3703
+ title: "Archive project",
3704
+ description: "\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3092\u30A2\u30FC\u30AB\u30A4\u30D6\u3059\u308B (status=archived + archived_at \u30BB\u30C3\u30C8)\u3002\u95A2\u9023\u30BF\u30B9\u30AF\u306E project_id \u306F\u7DAD\u6301\u3055\u308C\u308B\u306E\u3067\u3001\u904E\u53BB\u5C65\u6B74\u304C\u8AAD\u3081\u308B\u3002\u901A\u5E38\u306F delete \u3088\u308A archive \u3092\u63A8\u5968\u3002",
3705
+ inputSchema: archiveProjectInputSchema.shape
3706
+ },
3707
+ async (input) => wrapHandler(ctxFactory, archiveProjectHandler, input)
3708
+ );
3709
+ server.registerTool(
3710
+ "delete_project",
3711
+ {
3712
+ title: "Delete project",
3713
+ description: "\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3092\u7269\u7406\u524A\u9664\u3059\u308B\u3002\u30BF\u30B9\u30AF\u306E project_id \u306F NULL \u5316 (ON DELETE SET NULL)\u3001\u5B50\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u306E parent_project_id \u3082 NULL \u5316\u3002\u904E\u53BB\u5C65\u6B74\u3092\u4FDD\u3061\u305F\u3044\u5834\u5408\u306F archive_project \u3092\u4F7F\u3046\u3002",
3714
+ inputSchema: deleteProjectInputSchema.shape
3715
+ },
3716
+ async (input) => wrapHandler(ctxFactory, deleteProjectHandler, input)
3717
+ );
3718
+ server.registerTool(
3719
+ "create_tag",
3720
+ {
3721
+ title: "Create tag",
3722
+ description: "\u30BF\u30B0\u3092\u65B0\u898F\u4F5C\u6210\u3059\u308B\u3002`name` \u5FC5\u9808\u3001status \u306F active \u56FA\u5B9A\u3002",
3723
+ inputSchema: createTagInputSchema.shape
3724
+ },
3725
+ async (input) => wrapHandler(ctxFactory, createTagHandler, input)
3726
+ );
3727
+ server.registerTool(
3728
+ "update_tag",
3729
+ {
3730
+ title: "Update tag",
3731
+ description: "\u30BF\u30B0\u3092\u90E8\u5206\u66F4\u65B0\u3059\u308B\u3002null=\u660E\u793A\u30AF\u30EA\u30A2\u3002",
3732
+ inputSchema: updateTagInputSchema.shape
3733
+ },
3734
+ async (input) => wrapHandler(ctxFactory, updateTagHandler, input)
3735
+ );
3736
+ server.registerTool(
3737
+ "archive_tag",
3738
+ {
3739
+ title: "Archive tag",
3740
+ description: "\u30BF\u30B0\u3092\u30A2\u30FC\u30AB\u30A4\u30D6\u3059\u308B\u3002\u30BF\u30B9\u30AF\u306E tag_ids \u914D\u5217\u306B id \u306F\u6B8B\u308B\u306E\u3067\u3001\u65E2\u5B58\u30BF\u30B9\u30AF\u306E\u30BF\u30B0\u8868\u793A\u306F\u7DAD\u6301\u3055\u308C\u308B\u3002",
3741
+ inputSchema: archiveTagInputSchema.shape
3742
+ },
3743
+ async (input) => wrapHandler(ctxFactory, archiveTagHandler, input)
3744
+ );
3745
+ server.registerTool(
3746
+ "delete_tag",
3747
+ {
3748
+ title: "Delete tag",
3749
+ description: "\u30BF\u30B0\u3092\u7269\u7406\u524A\u9664\u3059\u308B\u3002\u30BF\u30B9\u30AF\u306E tag_ids \u914D\u5217\u306F UUID[] \u5217\u306A\u306E\u3067\u5B64\u5150 id \u304C\u6B8B\u308B (UI \u8868\u793A\u6642\u306B\u300C\u4E0D\u660E\u306A\u30BF\u30B0\u300D\u6271\u3044)\u3002\u5C65\u6B74\u5C0A\u91CD\u306A\u3089 archive_tag \u63A8\u5968\u3002",
3750
+ inputSchema: deleteTagInputSchema.shape
3751
+ },
3752
+ async (input) => wrapHandler(ctxFactory, deleteTagHandler, input)
3753
+ );
3754
+ server.registerTool(
3755
+ "create_context",
3756
+ {
3757
+ title: "Create context",
3758
+ description: "\u30B3\u30F3\u30C6\u30AD\u30B9\u30C8 (\u4F5C\u696D\u74B0\u5883\u30BF\u30B0\u3002home / office / mobile \u7B49) \u3092\u65B0\u898F\u4F5C\u6210\u3059\u308B\u3002`name` \u5FC5\u9808\u3001icon \u306F\u7D75\u6587\u5B57\u307E\u305F\u306F\u30A2\u30A4\u30B3\u30F3\u540D\u3002",
3759
+ inputSchema: createContextInputSchema.shape
3760
+ },
3761
+ async (input) => wrapHandler(ctxFactory, createContextHandler, input)
3762
+ );
3763
+ server.registerTool(
3764
+ "update_context",
3765
+ {
3766
+ title: "Update context",
3767
+ description: "\u30B3\u30F3\u30C6\u30AD\u30B9\u30C8\u3092\u90E8\u5206\u66F4\u65B0\u3059\u308B\u3002null=\u660E\u793A\u30AF\u30EA\u30A2\u3002",
3768
+ inputSchema: updateContextInputSchema.shape
3769
+ },
3770
+ async (input) => wrapHandler(ctxFactory, updateContextHandler, input)
3771
+ );
3772
+ server.registerTool(
3773
+ "archive_context",
3774
+ {
3775
+ title: "Archive context",
3776
+ description: "\u30B3\u30F3\u30C6\u30AD\u30B9\u30C8\u3092\u30A2\u30FC\u30AB\u30A4\u30D6\u3059\u308B\u3002\u30BF\u30B9\u30AF\u306E context_id \u306F\u7DAD\u6301\u3002",
3777
+ inputSchema: archiveContextInputSchema.shape
3778
+ },
3779
+ async (input) => wrapHandler(ctxFactory, archiveContextHandler, input)
3780
+ );
3781
+ server.registerTool(
3782
+ "delete_context",
3783
+ {
3784
+ title: "Delete context",
3785
+ description: "\u30B3\u30F3\u30C6\u30AD\u30B9\u30C8\u3092\u7269\u7406\u524A\u9664\u3059\u308B\u3002\u30BF\u30B9\u30AF\u306E context_id \u306F NULL \u5316 (ON DELETE SET NULL)\u3002\u5C65\u6B74\u5C0A\u91CD\u306A\u3089 archive_context \u63A8\u5968\u3002",
3786
+ inputSchema: deleteContextInputSchema.shape
3787
+ },
3788
+ async (input) => wrapHandler(ctxFactory, deleteContextHandler, input)
3789
+ );
3790
+ server.registerTool(
3791
+ "create_routine",
3792
+ {
3793
+ title: "Create routine",
3794
+ description: '\u7E70\u308A\u8FD4\u3057\u30BF\u30B9\u30AF\u306E\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8 (\u30EB\u30FC\u30C1\u30F3) \u3092\u65B0\u898F\u4F5C\u6210\u3059\u308B\u3002`frequency` \u306F discriminated union (\u4F8B: {type: "weekly", weekdays: [1,3,5]})\u3002auto_expand=true (default) \u306A\u3089 Today \u3092\u958B\u3044\u305F\u6642\u306B\u81EA\u52D5\u5C55\u958B\u3055\u308C\u308B\u3002',
3795
+ inputSchema: createRoutineInputSchema.shape
3796
+ },
3797
+ async (input) => wrapHandler(ctxFactory, createRoutineHandler, input)
3798
+ );
3799
+ server.registerTool(
3800
+ "update_routine",
3801
+ {
3802
+ title: "Update routine (with propagation)",
3803
+ description: "\u30EB\u30FC\u30C1\u30F3\u3092\u90E8\u5206\u66F4\u65B0\u3059\u308B\u3002**\u672A\u6765 (date >= today) \u306E planned \u30BF\u30B9\u30AF\u306B\u81EA\u52D5\u4F1D\u64AD**\u3055\u308C\u308B (title / estimated / project / context / tags / scheduled_start_time)\u3002\u624B\u52D5\u7DE8\u96C6\u3057\u3066\u3044\u305F\u30BF\u30B9\u30AF\u306E\u8A72\u5F53\u30D5\u30A3\u30FC\u30EB\u30C9\u3082\u4E0A\u66F8\u304D\u3055\u308C\u308B\u70B9\u306B\u6CE8\u610F\u3002\u5B9F\u884C\u4E2D\u30FB\u5B8C\u4E86\u30FB\u30B9\u30AD\u30C3\u30D7\u6E08\u307F\u30FB\u904E\u53BB\u65E5\u306E\u30BF\u30B9\u30AF\u306F\u4FDD\u8B77\u3055\u308C\u308B\u3002propagated_task_count \u3092\u8FD4\u3059\u3002",
3804
+ inputSchema: updateRoutineInputSchema.shape
3805
+ },
3806
+ async (input) => wrapHandler(ctxFactory, updateRoutineHandler, input)
3807
+ );
3808
+ server.registerTool(
3809
+ "archive_routine",
3810
+ {
3811
+ title: "Archive routine",
3812
+ description: "\u30EB\u30FC\u30C1\u30F3\u3092\u30A2\u30FC\u30AB\u30A4\u30D6\u3059\u308B (status=archived)\u3002\u65E2\u5B58\u30BF\u30B9\u30AF\u306E routine_id \u306F\u7DAD\u6301\u3055\u308C\u308B (\u5C65\u6B74\u53EF)\u3002\u65B0\u898F\u5C55\u958B\u306F\u6B62\u307E\u308B\u3002",
3813
+ inputSchema: archiveRoutineInputSchema.shape
3814
+ },
3815
+ async (input) => wrapHandler(ctxFactory, archiveRoutineHandler, input)
3816
+ );
3817
+ server.registerTool(
3818
+ "delete_routine",
3819
+ {
3820
+ title: "Delete routine",
3821
+ description: "\u30EB\u30FC\u30C1\u30F3\u3092\u7269\u7406\u524A\u9664\u3059\u308B\u3002\u30BF\u30B9\u30AF\u306E routine_id \u306F ON DELETE SET NULL \u3067 NULL \u5316 (\u300Croutine \u7531\u6765\u300D\u306E\u30E9\u30D9\u30EB\u304C\u5931\u308F\u308C\u308B)\u3002\u5C65\u6B74\u5C0A\u91CD\u306A\u3089 archive_routine \u63A8\u5968\u3002",
3822
+ inputSchema: deleteRoutineInputSchema.shape
3823
+ },
3824
+ async (input) => wrapHandler(ctxFactory, deleteRoutineHandler, input)
3825
+ );
3826
+ server.registerTool(
3827
+ "create_time_block",
3828
+ {
3829
+ title: "Create time block",
3830
+ description: "\u30BF\u30A4\u30E0\u30D6\u30ED\u30C3\u30AF (\u6642\u9593\u5E2F\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8) \u3092\u65B0\u898F\u4F5C\u6210\u3059\u308B\u3002weekdays \u306F 0=\u65E5\u66DC\uFF5E6=\u571F\u66DC\u306E\u914D\u5217 (\u7A7A\u914D\u5217\u4E0D\u53EF)\u3002default_project_id / default_context_id / default_tag_ids \u3092\u6307\u5B9A\u3059\u308B\u3068\u30D6\u30ED\u30C3\u30AF\u7BC4\u56F2\u306B\u8FFD\u52A0\u3055\u308C\u305F\u30BF\u30B9\u30AF\u306E\u30C7\u30D5\u30A9\u30EB\u30C8\u306B\u4F7F\u308F\u308C\u308B\u3002",
3831
+ inputSchema: createTimeBlockInputSchema.shape
3832
+ },
3833
+ async (input) => wrapHandler(ctxFactory, createTimeBlockHandler, input)
3834
+ );
3835
+ server.registerTool(
3836
+ "update_time_block",
3837
+ {
3838
+ title: "Update time block",
3839
+ description: "\u30BF\u30A4\u30E0\u30D6\u30ED\u30C3\u30AF\u3092\u90E8\u5206\u66F4\u65B0\u3059\u308B\u3002default_tag_ids \u306F\u914D\u5217\u6307\u5B9A\u3067\u5168\u7F6E\u63DB\u3001null \u30AF\u30EA\u30A2\u306F default_project_id / default_context_id / color / start_date / end_date \u306E\u307F\u5BFE\u5FDC\u3002",
3840
+ inputSchema: updateTimeBlockInputSchema.shape
3841
+ },
3842
+ async (input) => wrapHandler(ctxFactory, updateTimeBlockHandler, input)
3843
+ );
3844
+ server.registerTool(
3845
+ "archive_time_block",
3846
+ {
3847
+ title: "Archive time block",
3848
+ description: "\u30BF\u30A4\u30E0\u30D6\u30ED\u30C3\u30AF\u3092\u30A2\u30FC\u30AB\u30A4\u30D6\u3059\u308B (status=archived)\u3002\u904E\u53BB\u65E5\u8868\u793A\u3067\u306F\u5F53\u6642\u306E\u72B6\u614B\u304C\u518D\u73FE\u3055\u308C\u308B\u3002",
3849
+ inputSchema: archiveTimeBlockInputSchema.shape
3850
+ },
3851
+ async (input) => wrapHandler(ctxFactory, archiveTimeBlockHandler, input)
3852
+ );
3853
+ server.registerTool(
3854
+ "delete_time_block",
3855
+ {
3856
+ title: "Delete time block",
3857
+ description: "\u30BF\u30A4\u30E0\u30D6\u30ED\u30C3\u30AF\u3092\u7269\u7406\u524A\u9664\u3059\u308B\u3002\u30BF\u30B9\u30AF\u306F\u6642\u523B\u30D9\u30FC\u30B9\u3067\u52D5\u7684\u306B\u30D6\u30ED\u30C3\u30AF\u3092\u5224\u5B9A\u3059\u308B\u306E\u3067\u53C2\u7167\u4E0D\u6574\u5408\u306F\u767A\u751F\u3057\u306A\u3044\u304C\u3001\u904E\u53BB\u65E5\u8868\u793A\u3067\u3082\u3053\u306E\u30D6\u30ED\u30C3\u30AF\u306F\u51FA\u3066\u3053\u306A\u304F\u306A\u308B\u3002",
3858
+ inputSchema: deleteTimeBlockInputSchema.shape
3859
+ },
3860
+ async (input) => wrapHandler(ctxFactory, deleteTimeBlockHandler, input)
3861
+ );
3862
+ server.registerTool(
3863
+ "set_actual_times",
3864
+ {
3865
+ title: "Set actual start/end times",
3866
+ description: "\u5B9F\u7E3E\u958B\u59CB/\u7D42\u4E86\u6642\u523B\u3092\u76F4\u63A5\u6307\u5B9A\u3059\u308B (Play \u62BC\u3057\u5FD8\u308C\u30EA\u30AB\u30D0\u30EA\u7528)\u3002start+end \u2192 completed\u3001start \u306E\u307F \u2192 running\u3001\u4E21\u65B9 null \u2192 planned \u30EA\u30BB\u30C3\u30C8\u3002end < start \u306A\u3089\u65E5\u8DE8\u304E\u30BB\u30C3\u30B7\u30E7\u30F3\u6271\u3044 (Domain \u3067 finishedAt \u3092\u7FCC\u65E5\u7E70\u4E0A\u3052)\u3002",
3867
+ inputSchema: setActualTimesInputSchema.shape
3868
+ },
3869
+ async (input) => wrapHandler(ctxFactory, setActualTimesHandler, input)
3870
+ );
3871
+ server.registerTool(
3872
+ "interrupt_task",
3873
+ {
3874
+ title: "Interrupt running task",
3875
+ description: "\u8D70\u884C\u4E2D\u30BF\u30B9\u30AF\u3092\u300C\u5272\u308A\u8FBC\u307F\u300D\u3067\u5206\u5272\u3059\u308B\u30021 \u547C\u3073\u51FA\u3057\u3067 3 \u30BF\u30B9\u30AF\u3092\u64CD\u4F5C: (1) \u5143\u30BF\u30B9\u30AF\u3092 completed (\u5B9F\u7E3E\u306F\u305D\u3053\u307E\u3067)\u3001(2) \u5272\u308A\u8FBC\u307F\u30BF\u30B9\u30AF\u3092 running \u3067\u65B0\u898F\u4F5C\u6210 (\u5373\u8A08\u6E2C\u958B\u59CB)\u3001(3) \u5143\u30BF\u30B9\u30AF\u306E\u6B8B\u4F5C\u696D\u3092 planned \u3067\u65B0\u898F\u4F5C\u6210 (splitFromTaskId \u3067\u30EA\u30CD\u30FC\u30B8\u4FDD\u6301)\u3002split note 3 \u4EF6\u304C\u81EA\u52D5\u8A18\u9332\u3055\u308C\u308B\u3002\u5BFE\u8C61\u30BF\u30B9\u30AF\u304C running \u4E2D\u3067\u306A\u3044\u3068\u30A8\u30E9\u30FC\u3002",
3876
+ inputSchema: interruptTaskInputSchema.shape
3877
+ },
3878
+ async (input) => wrapHandler(ctxFactory, interruptTaskHandler, input)
3879
+ );
3880
+ server.registerTool(
3881
+ "add_parallel_task",
3882
+ {
3883
+ title: "Add parallel task",
3884
+ description: "\u9032\u884C\u4E2D\u30BF\u30B9\u30AF\u3068\u4E26\u884C\u3059\u308B\u65B0\u898F\u30BF\u30B9\u30AF\u3092\u8FFD\u52A0\u3059\u308B\u3002start_immediately: true (default) \u3067 running \u72B6\u614B + \u5373\u8A08\u6E2C\u958B\u59CB\u3002false \u3067 planned \u4F5C\u6210\u306E\u307F\u3002is_background: true \u4F75\u7528\u3067\u898B\u7A4D\u3082\u308A\u96C6\u8A08\u304B\u3089\u9664\u5916\u3002",
3885
+ inputSchema: addParallelTaskInputSchema.shape
3886
+ },
3887
+ async (input) => wrapHandler(ctxFactory, addParallelTaskHandler, input)
3888
+ );
3889
+ server.registerTool(
3890
+ "reorder_task",
3891
+ {
3892
+ title: "Reorder task",
3893
+ description: "\u30BF\u30B9\u30AF\u306E\u4E26\u3073\u9806 (order_index) \u3092\u5909\u66F4\u3059\u308B\u3002\u6163\u7FD2\u3067 10 \u5358\u4F4D (\u96A3\u63A5\u4E2D\u9593\u633F\u5165\u306F 5 \u3084 15)\u3002scheduled_start_time \u3082\u540C\u6642\u6307\u5B9A\u53EF\u80FD (null=\u672A\u5272\u5F53\u30B0\u30EB\u30FC\u30D7\u3078\u3001HH:MM=\u8A72\u5F53\u30BF\u30A4\u30E0\u30D6\u30ED\u30C3\u30AF\u3078\u79FB\u52D5)\u3002",
3894
+ inputSchema: reorderTaskInputSchema.shape
3895
+ },
3896
+ async (input) => wrapHandler(ctxFactory, reorderTaskHandler, input)
3897
+ );
2179
3898
  return server;
2180
3899
  }
2181
3900
 
2182
3901
  // src/index.ts
2183
3902
  async function main() {
2184
- const version = "0.1.2" ;
3903
+ const version = "0.3.0" ;
2185
3904
  const server = createMcpServer({ version });
2186
3905
  const transport = new StdioServerTransport();
2187
3906
  await server.connect(transport);