Skip to main content
Glama

课表 MCP(mcp-schedule-termux)

面向 LLM 的个人课表与弹性日程服务。Node.js 24+、SQLite 本地持久化,同一端口提供 Streamable HTTP 和传统 SSE,另支持 stdio。

kebiao 就是「课表」的拼音。它管的是课程表、学期校历、调停课和弹性任务,不是什么私有系统;仓库取名 mcp-schedule-termux 只是为了让用途一眼可辨。代码里的 KEBIAO_* 环境变量、start_kebiao 启动函数和 data/kebiao.sqlite 数据库都沿用这个简称。

启动

npm ci
npm start

依赖全部是纯 JS(Node 内置 node:sqlite),没有原生编译步骤,所以在 Termux 上 pkg install nodejs 之后就能直接跑;在电脑上同样如此。克隆目录叫什么名字都可以,脚本按自身位置定位项目,不依赖路径。

默认连接地址:

用途

地址

Streamable HTTP

http://127.0.0.1:3001/mcp

SSE

http://127.0.0.1:3001/sse

健康检查

http://127.0.0.1:3001/health

RikkaHub 添加 MCP 服务时,选择对应传输并填入上方 URL。两种传输同时可用、共享数据。

快捷启动

# 仅当前终端启用,无需修改 bashrc
source scripts/kebiao.bash
start_kebiao
start_kebiao 3002

# 安装到 bashrc(先备份,不覆盖现有启动器)
node scripts/install-shell.js

前台运行,Ctrl-C 停止。安装后新终端可直接 start_kebiao。默认不设置自启动守护进程。

在 Termux 上想让服务在息屏后继续跑,另开一个终端执行 termux-wake-lock;这是 Android 的后台限制,与项目本身无关。

配置

node src/server.js --port 3001 --host 127.0.0.1 --db ./data/kebiao.sqlite
node src/server.js --stdio

支持 KEBIAO_PORTKEBIAO_HOSTKEBIAO_DB;启动函数还支持 KEBIAO_DIR。默认数据库在项目 data/kebiao.sqlite,不随 shell 当前目录改变。

可选 KEBIAO_TOKEN:客户端添加 Authorization: Bearer <token>。监听非回环地址时必须配置 token;公网部署自行提供 HTTPS。不要把 token 写入课表数据。

Related MCP server: kenotex-mcp

给 LLM 的 4 个工具

工具

一次调用能做什么

schedule_query

同时获取 agenda、catalog、tasks、conflicts、free、config、summary、unscheduled、changes;筛选、分页、假设启用

schedule_mutate

原子执行多次 put/patch/enable/set_timetable/switch_timetable,直接返回修改后的视图

schedule_import

按官方稳定来源键去重导入,保留个人启用状态,返回导入结果及视图

schedule_plan

新建任务并安排,支持仅预览或直接提交,返回时间块和剩余未安排原因

工具携带参数 schema 和中文说明;schedule://guide 提供补充规则。不需要先读 guide 才能调用工具。

查询没选上的 C++(一次)

调用 schedule_query

{"views":["catalog"],"state":"all","search":"C++"}

默认 state=enableddisabled 只查已停用,all 包含两者。禁用保留所有数据,不进入默认 agenda,不占用 free,也不产生 conflicts。

假如选上 C++,是否冲突?(一次,不修改数据)

{
  "from":"2026-09-14","to":"2026-09-21",
  "simulateEnable":["cpp"],
  "views":["agenda","conflicts","free"]
}

启用 C++,同时调整教室并查看最新课表(一次)

调用 schedule_mutate

{
  "requestId":"select-cpp-20260915",
  "operations":[
    {"op":"enable","targets":["C++"],"enabled":true},
    {"op":"patch","target":"C++","changes":{"location":"A202"}}
  ],
  "returnQuery":{"from":"2026-09-14","to":"2026-09-21","views":["agenda","conflicts"]}
}

target/targets 接受 ID 或完整唯一标题,包含禁用数据;重名返回候选 ID,绝不随意选一个。put 创建或完整替换;patch 局部修改,数组整体替换,metadata 也整体替换。修改前可 dryRun=true,已经授权的操作可直接写入。

enable=false 可应用于课程、日程、任务、学期和调课例外。禁用学期使所属课程不参与默认课表;禁用任务使关联时间块不占用。禁用课程不会自动禁用关联学习任务。重新启用恢复原数据;显式非 selected 的课程仍需更新选课状态才进入课表。单次取消课程应使用 exception,不要禁用整个课程。

导入官方课表

examples/import.json 是完整的 schedule_import 参数示例,包含已选高数和未选 C++。示例不自动写入真实数据库。

  • source.namespace + source.key 唯一标识来源条目,namespace 由本次参数统一设置。

  • key 使用官方稳定编号,不使用容易重复的课程名称。

  • 新导入课程默认禁用;明确 enabled=true 表示已选。学期默认启用。

  • 重复导入保留个人 enabled,preserveEnabled=false 可覆盖。

  • 导入更新为完整替换:除自动保留的 enabled 和已有 status 外,notes/tags/metadata 等字段都以本次导入内容为准,需要保留的必须在导入条目中携带。

  • 未出现在本次导入的旧记录保留,可在之后批量 disable。

  • 学期和课程可同批写入,通过自定义固定 ID 引用;重新导入已有来源会沿用旧 ID,调用方应继续沿用返回的 ID。

  • 导入接收结构化 JSON。网页、教务登录、PDF、图片解析由客户端先处理。

用原始 Markdown 补齐已有课表

先查询 catalog/config/tasks,使用 state=all 并遍历分页,核对现有 ID、source 和个人选择。缺课程时间时,优先 patch 对应课程的 rules、scheduleStatus、expectedTiming、scheduleSource;原文未写的信息不猜测,明确说是“预计/往年安排在假期”的用 expectedTiming.window 并标 source/confidence,不要写成确定日期。新增课堂才用稳定来源键导入,各课堂独立记录。已有例会可 patch category=activity(原记录若带选课状态或学分字段,完整 put 时移除这些不适用字段)。

整份重新 import 是完整替换,自动保留的只有已有 enabled/status(可分别关闭);任务 completed、作息表、调休日历、备注等需要在导入内容中显式保留。不要拿较旧的 Markdown 恢复后来已经完成的登记任务,或抹去后来设置的冬夏切换和调休。先 dryRun,确认数据和 coverage 后再使用期望版本提交;已获授权时不必额外询问确认。

可交给录入 AI 的指令:

先读取全部现有课表,再根据这份 Markdown 补齐所有课堂的公示时间,包括未选、候选课程。原有课程用原 ID 做 patch;新增课堂用独立稳定 source.key 去重导入。未选状态与时间规则独立保存,默认保留现有选课、启禁用状态。材料未写的时间标 unknown,明确待通知标 tbd,缺部分课次标 partial;已知开课周用 expectedTiming,只有“预计安排在假期”这类说法的用 expectedTiming.window 并标 source/confidence,勿编造节次。个人周期例会标 category=activity。保留已完成任务、现有作息表和调休,不用旧材料覆盖这些后续变更。最后返回 unscheduled、coverage,并模拟 C++ 的新增冲突;缺时间时明确说明无法完整判断,不要重复解释已 acknowledge 的缺口。

未选课程、公示时间和分析覆盖情况

公示课表继续保存在课程的 rules,独立于选课 statusenabled。每个课堂用不同 ID 和稳定 source.key,例如 CPP-01、CPP-02;这样可以分别查询或预演,无需复制一份容易失同步的 expectedRules。只有确实取得公示时间才能录入,不能从空规则推断“学校没有排课”。scheduleSource:{type:"official",reference:"公示网址或材料名称"} 可记录依据(type 也可为 personal、unknown)。

查看尚未选上的课堂的已知时间:

{"from":"2026-09-14","to":"2026-09-21","views":["catalog","agenda","unscheduled"],"state":"all","courseStatus":"not_selected"}

candidate 用于尚在考虑/课堂待选,查询方式相同。agenda 展开已知时间;未选条目不占用 free,也不会产生实际冲突。

时间资料状态独立记录为 scheduleStatus

含义

scheduled

已录完整的时间规则

partial

只录了部分课次时间,仍有缺口

unknown

时间未录入或未知,不代表学校未公布

tbd

已知来源明确说时间待定/待通知

旧数据不改写:有 rules 时查询显示 scheduled,空 rules 显示 unknown。scheduled/partial 必须有规则;unknown/tbd 必须无规则。补齐显式 unknown/tbd 的课程时,同批 patch rules 和 scheduleStatus;只补了一部分应设 partial。

可以用 expectedTiming:{startWeek:9} 表示“第9周起,具体时间未录入”,不虚构结束周、星期和节次。还支持 endWeek、实际 date、durationWeeks;date 与教学周范围互斥。durationWeeks 仅表示持续周数,不会被展开成占用,也不能单凭“持续1周”推断是哪一周。对 partial,这些边界描述尚缺的部分;已知 rules 仍照常参与计算。

预期还可以是学期之外的:expectedTiming.window{from,to,label}(含首尾的当地日期)记录“预计安排在 2027 暑假”这类尚未确定的落点,它是软性预期,不会生成任何占用,也不能替代 rules。window 与 date/startWeek/endWeek/durationWeeks 互斥,长度上限 420 天。同一处可标注 source(official/personal/unknown)、confidence(high/medium/low)和 verifiedAt,说明这条预期来自谁、有多可信、何时核对过;personal 或 low 只能作为预期,不能当成官方排课。

已经知道缺口但暂时不想被反复提醒时,用 acknowledge:{until,note} 记下确认日期。在 until 之前 coverage 仍会报告该缺口并标记 acknowledged(确认到期后自动恢复普通缺口),acknowledgedIssues 给出这类条目的数量;解释原因只需做一次。

unscheduled 独立分页,列出查询日期范围内可能涉及的未知/待定/部分录入课程;state=all 含未选,catalog 可不限日期查所有记录。已知教学周边界会考虑 term.calendar 调休;没有明确边界时保守认为可能影响整个学期,写了 window 的则按该预期窗口判断。

查询 agenda/conflicts/free/unscheduled 或进行模拟时,附带 coverage

  • courses 提供已纳入记录数、有实际课次的记录数、完整时间记录数、缺时间数、部分时间数和 issuesOutsideRange 数;这里“课程记录”也包括 category=activity 的周期活动。

  • issues 给出 ID、标题和原因,reason 是稳定枚举:time_not_imported、time_tbd、partial_schedule,模拟目标另加 term_disabled、outside_term。issues 使用独立 pagination,跟随 limit/offset。

  • 每条 issue 还带上记录自己的解释,避免“学院还没排时间”被读成“用户没录/没选”:status 是选课状态(未设置则不输出)、notes 是该记录 notes 的原文、acknowledgeNote 是 acknowledge.note 的原文(未 acknowledge 或无 note 时不输出);模拟类 issue(term_disabled/outside_term)同样带 status/notes,issuesOutsideRange 的条目结构与 issues 一致。

  • issues 只列可能落在本次范围内的缺口。 学期覆盖本范围、但按预期时间(如暑假 window)不可能落在范围内的已知缺口放在 issuesOutsideRange,不计入 complete;这时 message 会带上“另有 N 条时间不完整的记录按预期不会落在本范围内(见 issuesOutsideRange)”,避免同一口待定课把每次查询都标成不完整,也避免 complete=true 被读成“范围外也什么都没发生”。

  • complete 是本次范围的结论,不是全局结论:它只看 issues,不看 issuesOutsideRange,也不证明学校全量课表已导入。

  • issues[].acknowledged=true 表示该缺口已被 acknowledge 确认,仍需按 complete=false 对待冲突结论,但不必重复解释。

  • complete=false 时,conflicts=[] 只能表示已知时间之间未发现冲突。 free 只扣除了已知占用;plan 会返回 tentative=true,提交的安排仍需等资料补齐后复核。

  • complete 仅针对本次范围内已存的有效数据,不证明学校全量课表已导入;粗略事件另由 approximateEvents 和冲突 certainty 披露精度。

  • coverage 与 free/conflicts 一样,始终检查全部有效占用,不受 search/state/kinds/ids/termId/category/scheduleStatus 等展示筛选或分页缩小。

一键预演仍使用现有参数:

{"from":"2026-09-14","to":"2026-09-21","simulateEnable":["cpp"],"views":["agenda","conflicts","free","unscheduled"]}

新增 simulation 返回 baselineConflictCount、addedConflictCount、removedConflictCount、分页的 addedConflicts 和每个目标的生效状态/课次数。不会写入数据。空规则会明确暴露为缺时间;所属学期禁用时会报告 term_disabled,需要模拟时可同时指定学期 ID。范围内没有课次不代表该课程整个学期没有时间冲突。

选课状态与学分学时

课程可记录 statuscandidate(候选)、selected(已选)、not_selected(未选上)、dropped(已退选)。省略表示未知,旧数据继续按 enabled 工作,不根据启禁用猜测选课历史。

  • status 与 enabled 独立。显式非 selected 的课程不进入默认 agenda、不占用 free;state=all 可查完整资料,courseStatus 可按状态筛选(含 unknown)。state=disabled 包含这些实际不生效的课程。

  • 选上课程时 patch {"status":"selected","enabled":true};仅 enable 不改变 status。simulateEnable 在查询副本中同时模拟已选,不写入数据库。

  • 可用 rules:[] 保存尚无课表的候选/历史课程。

  • 重复导入默认保留已有 status;preserveStatus=false 使用传入状态,省略状态则清除。它与 preserveEnabled 分别控制。

  • credits 为非负数;hours 支持可选的 totaltheorypractice,已知分项之和不得超过 total。缺失表示未知,0 表示明确为零。

查询本学期已选课程的学分学时(包括被手动禁用的已选课程):

{"views":["summary"],"termId":"autumn","courseStatus":"selected","state":"all"}

个人周期例会可用 kind:"course",category:"activity" 复用现有周规则;它仍参与 agenda/conflicts/free,但不设置选课状态/学分/学时,且排除 summary。课程 category 默认为 academic,不能靠缺少 status 猜测它是个人事务。一次性例会使用 event。

summary 按学期与状态分组,遵循查询筛选、独立分页,不按查询日期折算课程学时。每个数值返回 knownTotal(已知部分之和)、missing(未填写课程数)、knownCountavailable;available=false 表示全缺失,客户端可隐藏该列,而不是显示“0学时”;明确填写0时 available=true。有缺失时不能把已知部分当完整总数。默认 state=enabled 只汇总当前生效课程。

只知道考试在下午

{"operations":[{"op":"put","entity":{
  "kind":"event","title":"期末考试","date":"2026-11-28",
  "slot":"afternoon","timezone":"Asia/Shanghai"
}}]}

一次性事件支持三种互斥形式:

  • start + end:精确带时区时间戳。

  • date + slotmorning / afternoon / evening。timezone 默认 Asia/Shanghai,指定 termId 则默认继承学期时区。

  • date + termId + periods:[5,6]:按该学期节次表生成时间。节次必须存在、有序、不重复;使用学期时区。

只有“第13周周六”时,按 term.weekOne 加 12 周再加 5 天得到实际 date;一次性事件不会随 term.calendar 调休映射。指定 termId 时日期必须在学期内。

粗略时段不存储猜测的起止时间。agenda 返回 precision:"slot"occupancy:{start,end,conservative:true},不返回事件 start/end;占用范围按当地 00–12、12–18、18–次日00 点保守避让。它是排程边界,不是考试的实际时间。涉及这些时段的冲突标记 certainty:"possible",精确时间或节次之间的冲突标记 confirmed。free 和 plan 都避让整个占用范围,busy=false 则不占用。

节次事件返回 precision:"periods" 和按作息表解析的 start/end。关联 taskId 的时间块必须使用精确时间。切换事件时间形式请用完整 put 替换,避免 patch 留下原形式的字段。

设置 summer / winter 并切换作息

AI 可以通过同一个 schedule_mutate 工具设置、切换命名作息表,名称可自定义。下面示例假设已有 ID 为 autumn 的学期,且课程引用第 5、6 节;实际使用须填写学校的完整作息表。

{
  "operations":[
    {"op":"set_timetable","target":"autumn","name":"summer","periods":[
      {"number":5,"start":"14:30","end":"15:15"},
      {"number":6,"start":"15:25","end":"16:10"}
    ]},
    {"op":"set_timetable","target":"autumn","name":"winter","periods":[
      {"number":5,"start":"14:00","end":"14:45"},
      {"number":6,"start":"14:55","end":"15:40"}
    ]},
    {"op":"switch_timetable","target":"autumn","timetable":"summer","effectiveFrom":"2026-09-01"},
    {"op":"switch_timetable","target":"autumn","timetable":"winter","effectiveFrom":"2026-10-08"}
  ],
  "returnQuery":{"from":"2026-10-08","to":"2026-10-15","termId":"autumn","views":["config","agenda","conflicts"]}
}

之后只需一条 switch 即可切换到已存的表。比如“今天切换 winter”:AI 根据学期时区填写今天的 effectiveFrom,不必重传节次。生效日期必须在学期内。

  • set 只保存或完整替换该名称的表,不激活,也不影响其他命名表。

  • switch 从学期当地日期零点起生效,持续到下一次切换;提前设置即可自动按日期使用,无需后台定时任务。相同日期再次 switch 会替换该日记录,其他日期的切换保留。

  • 首次切换前使用旧 term.periodstimetable:null 从指定日恢复它。旧数据无需迁移。若默认 periods 为空,须从学期第一天起安排命名表覆盖课程。

  • term.timetables 保存命名表数组,term.timetableSwitches 保存切换记录。可通过 put/patch/import 完整设置。取消某次计划切换时,patch 去掉该条后的完整 timetableSwitches 数组;普通 switch 不删除未来计划。

  • 修改同名表会改变所有引用该表日期的查询结果,包括历史。 只想从某天调整,应 set 新名称(例如 winter-v2),再从该日 switch。

  • 已生效或计划生效的每套表都须包含所属课程引用的节次,包括禁用课程;节次事件校验其实际日期使用的表。缺节次、重复编号、非法时间、未知表名、同日重复配置会使整批回滚。未激活的新表可以先保存。

  • 按实际上课日期选作息表;term.calendar 映射只决定教学周和星期。例如冬季补上夏季某天的课,使用冬季钟点。节次考试同样跟随切换。

  • 写明 start/end 的课程、活动、调课例外及已安排的任务块保持原钟点。新任务排程、free、conflicts 使用切换后的占用;切换后可同次 returnQuery 检查与既有活动的冲突。

  • config 返回全部配置和 activeTimetable:{date,name,effectiveFrom,periods}:按查询起点换算到学期时区的日期选表,name=null 表示默认表;查询起点在学期外则 activeTimetable=null。agenda 中按节次展开的条目返回 timetable 名称。

  • 导入仍按完整替换语义处理学期,重新导入需携带需要保留的命名表及切换配置。

查某日起生效的作息:

{"views":["config"],"termId":"autumn","from":"2026-10-08","to":"2026-10-09"}

调整一次课

agenda 返回 entityIdoriginalDateruleIndex。调用 schedule_mutate

{
  "operations":[{"op":"put","entity":{
    "kind":"exception","id":"cpp-move-20260915","title":"C++ 调课",
    "courseId":"cpp","originalDate":"2026-09-15","ruleIndex":0,
    "start":"2026-09-16T14:00:00+08:00","end":"2026-09-16T15:40:00+08:00"
  }}],
  "returnQuery":{"from":"2026-09-14","to":"2026-09-21","views":["agenda","conflicts"]}
}

取消使用 cancelled=true;同一次课只允许一条启用的 exception,后续 patch 此例外。禁用例外恢复原安排。课程规则重排可能使 ruleIndex 改变,修改规则时应同批调整相关例外。

一次创建并安排三小时复习

调用 schedule_plan

{
  "requestId":"study-math-20260915",
  "from":"2026-09-15","to":"2026-09-21",
  "prefer":"evening","bufferMinutes":10,"commit":true,
  "newTasks":[{
    "kind":"task","title":"高数复习","durationMinutes":180,
    "earliest":"2026-09-15T08:00:00+08:00","deadline":"2026-09-20T22:00:00+08:00",
    "minBlockMinutes":30,"maxBlockMinutes":60,"splittable":true
  }]
}

默认 commit=false 预览。commit=true 原子创建并安排;默认 requireAll=true,不能完整安排则不写入任何数据。requireAll=false 允许部分安排。任务按期限、优先级排序,偏好不满足时回退其他允许时段。既有时间块不被移动,并从剩余任务时长扣除。

如要重新安排,先禁用不需要的旧时间块,再调用 plan;手动定时可直接批量 put event。locked 为手动/后续重排保留标记,当前算法对所有既有时间块都不移动。

截止任务与通知边界

登记截止日期应使用已有 task.deadline,先检查 completed,避免重复创建或催办已完成任务。备注不是可靠的自动执行来源,系统不会自行从文字提取日期后写入提醒。保存任务/查询截止时间不等于后台推送;本服务目前没有通知投递通道,也不会主动发送调休或作息切换提示。

时间与一致性

  • 查询日期使用给定时区(默认 Asia/Shanghai),from 包含、to 不包含,默认从今天查询 7 天,最长 120 天。精确时间戳必须带偏移量;粗略事件使用下述 date/slot 表达。

  • term.endDate 包含;weekOne 是学期开始所在周的周一。课程 weeks 为明确教学周集合,单双周转换成集合。

  • 按节次上课时,占用从第一节开始到最后一节结束(包含课间);不连续课程请拆为多条 rules。

  • term.calendar 将实际日期映射为教学日期,null 表示当天停课,例如 {"2026-09-15":null,"2026-09-19":"2026-09-15"}

  • 单次例外覆盖实例时间/地点;移入查询范围的课也会出现,实例 ID 保留原日期。

  • 时间区间为 [start,end),相邻事件不冲突。free/conflicts 始终计算全部有效占用,不会被 search/state/kinds/ids 缩小;agenda/catalog 等视图才应用这些筛选。

  • state/category/scheduleStatus/courseStatus/kinds/termId/search/ids 都是可选展示过滤器:省略、传 []"" 都等于不筛(空值不会被当成"匹配不到任何东西",空 views 按默认 ["agenda"] 处理)。kinds 过滤的是记录类型(course/event/task/term/exception),不是分类;category/scheduleStatus/courseStatus 只对 course 有意义,并且会顺带排除 term/event/task —— 例如 kinds:["course","event"] 时 config 必然为空。只想问"今天有什么课/几点下课"时只传 from/to/views,别为填满 schema 而猜这些值。

  • termId 与 ids 按引用处理:接受精确 ID 或唯一精确标题(同 patch/simulateEnable)。填了匹配不到的引用(例如模型自造的占位符 ".""*")不会把结果清空,而是被忽略并在响应里报出来:ignoredFilters 给出被忽略的值,coverage.message 开头也会说明(这类占位符是最常见的"我发现字段是必需的于是随便填一个"行为)。ids 里部分能匹配时,只保留能匹配的那些。

  • 每个视图独立分页,检查 pagination.*.hasMore。默认最多 200 条,可到 2000 条;后续用 offset。coverage 的 issues 也独立分页。

  • changes 视图按 revision 升序返回审计日志增量:指定 sinceRevision 后只返回更新的记录,用最后一页的 revision 作为下一次的 sinceRevision 即可增量同步,无需全量重读。在写入响应里(returnQuery),changes 表示本次写入产生的差异,含每条的 before/after。

  • 写入整批事务、版本号、持久化 requestId 去重和审计。重试同一个 requestId 必须使用相同参数;expectedRevision 可防止覆盖旧版本。预览不持久化,不保留 requestId。

  • returnQuery 是同一事务内的视图,其版本号使用外层 revision;dryRun 外层版本不递增。

  • 冲突不会阻止手工录入(课表可能真实冲突),按需在同次写入指定 returnQuery.views 包含 conflicts。

模块与当前范围

src/schema.js  参数和实体结构
src/store.js   SQLite、事务、版本、审计、请求去重
src/engine.js  规则展开、查询、批量修改、导入、空闲与安排
src/timetable.js  命名作息表校验、按日期选表及节次解析
src/coverage.js  时间资料状态、已知边界及查询覆盖情况
src/server.js  MCP 工具、资源、HTTP/SSE/stdio 入口

v0.1 已实现课程按教学周重复、调停课、校历、固定活动、弹性任务、批量导入和修改。暂不包含通用日历 RRULE、自动提醒、教务抓取、双向日历同步、从某次起自动分割系列和 Web UI。可用独立规则/事件表示当前所需安排。

npm test
npm run check

测试覆盖真实 SDK 双传输连接、写入查询、禁用/假设启用、导入保留选择、跨范围调课、事务回滚、请求去重、磁盘重启及弹性安排。

传输基于 官方 TypeScript MCP SDK。优先使用 Streamable HTTP,SSE 用于兼容现有客户端。

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    B
    maintenance
    MCP server for a self-hostable personal planner — exposes memo, todo, calendar event, and cross-resource full-text search tools so Claude, Cursor, or any MCP client can manage your schedule and notes in natural language.
    18
    1
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables task management through natural language: create tasks, list and filter them, and update their status. It uses SQLite for persistence and supports both local stdio and remote SSE transports.
    5 npm
    MIT
  • A
    license
    B
    quality
    A
    maintenance
    A local-first open-source MCP server that acts as a personal secretary, memory manager, and automation hub—it syncs Calendar, Gmail, Canvas, GitHub, and Health into a typed temporal memory graph, and enables approval-gated write actions like creating events, drafting/sending mail, and filing issues, all with telegram/slack/CLI interfaces and a SQLite-backed audit trail.
    33
    Apache 2.0