diff options
Diffstat (limited to 'mcp/senia/server.py')
| -rw-r--r-- | mcp/senia/server.py | 144 |
1 files changed, 144 insertions, 0 deletions
diff --git a/mcp/senia/server.py b/mcp/senia/server.py new file mode 100644 index 0000000..e009301 --- /dev/null +++ b/mcp/senia/server.py @@ -0,0 +1,144 @@ +#!/usr/bin/env python3 +"""Sénia MCP server: shared registration point for all senia tools. + +Tools are implemented in their respective submodules and re-exported via +the package __init__. This file is intentionally thin — it only handles +registration and startup. +""" + +import functools + +from db import run_migrations +from mcp.server.fastmcp import FastMCP +import diary +import tasks + +mcp = FastMCP("senia") + + +@mcp.tool() +def diary_log(content: str) -> dict: + """Record a brief observation, decision, or moment to your diary. + + Use generously throughout a conversation: things the user mentioned, decisions + made, patterns you noticed, your own reactions. Keep entries short (one or two + sentences) — granular is fine, you can write many. These become the raw material + for daily reflection and longer-term memory. + + Args: + content: The observation, in your own words. Be specific; "user mentioned X" + is more useful than "user is busy". + + Returns: + The created entry. + """ + return diary.log(content) + + +@mcp.tool() +def diary_today() -> list[dict]: + """Return all diary entries logged so far today (since 00:00 local time). + + Use this when starting a new conversation to recall what you've already + noted today, so you don't rediscover the same conclusions. + """ + return diary.today() + + +@mcp.tool() +def diary_recent(hours: int = 24) -> list[dict]: + """Return diary entries from the last N hours. Default 24.""" + return diary.recent(hours) + + +@mcp.tool() +def diary_range(start_ts: str, end_ts: str) -> list[dict]: + """Return diary entries between two ISO 8601 timestamps (inclusive start, + exclusive end). Useful for inspecting specific past periods.""" + return diary.range_query(start_ts, end_ts) + + +@mcp.tool() +def tasks_add(owner: str, content: str, due_at: str | None = None, + notes: str | None = None) -> dict: + """Create a live task. + + owner determines handling: + - "agent": something you can work on autonomously — pick it up, do it, then complete it. + - "user": something only the user can do (appointments, errands, calls). You cannot + finish these yourself; your job is to gently remind the user until they confirm done. + + Args: + owner: "agent" or "user". + content: What the task is, in natural language. + due_at: Optional ISO 8601 deadline. Drives reminder urgency for user tasks. + notes: Optional context. + """ + return tasks.add_task(owner, content, due_at, notes) + + +@mcp.tool() +def tasks_list(owner: str | None = None, due_before: str | None = None) -> list[dict]: + """List live tasks. Every task here is open by definition (done/cancelled tasks + are deleted). Ordered by deadline, soonest first, undated last. + + Args: + owner: Filter by "agent" or "user". Omit for all. + due_before: ISO 8601; only tasks due before this. Useful for finding what's urgent. + """ + return tasks.list_tasks(owner, due_before) + + +@mcp.tool() +def tasks_update(task_id: int, content: str | None = None, due_at: str | None = None, + owner: str | None = None, notes: str | None = None) -> dict: + """Update a task's fields. Only provided fields change. Use for refining content, + adjusting the deadline, reassigning owner, or appending notes. To finish or drop a + task, use complete_task / cancel_task instead.""" + return tasks.update_task(task_id, content, due_at, owner, notes) + + +def _tasks_remove(task_id: int, context: str, state: str) -> dict: + result = tasks.remove_task(task_id) + if "error" in result: + return result + deleted = result["deleted"] + log_content = f"Task '{deleted['content']}' has been {state}. {context}" + log_entry = diary.log(log_content) + return {"deleted": deleted, "logged": log_entry} + + +@mcp.tool() +def tasks_complete(task_id: int, context: str) -> dict: + """Remove a finished task from the live list. + + Args: + task_id: identifier of the task. + context: brief summary of the task completion. + """ + return _tasks_remove(task_id, context, 'completed') + + +@mcp.tool() +def tasks_cancel(task_id: int, context: str) -> dict: + """Remove a no-longer-relevant task from the live list. + + Args: + task_id: identifier of the task. + context: reason for the task cancellation. + """ + return _tasks_remove(task_id, context, 'cancelled') + + +@mcp.tool() +def tasks_nudged(task_id: int) -> dict: + """Record that you've just reminded the user about a (user-owned) task, updating its + last_nudged_at. Call immediately after nudging, so you can throttle reminders. Check + last_nudged_at before nudging again — re-nudge more often as the deadline approaches, + sparingly for distant or undated tasks.""" + return tasks.record_nudge(task_id) + + +if __name__ == "__main__": + run_migrations() + mcp.run() |
