Time Management

com.pyunto由 pyunto.com 发布v0.4.0更新于 Nov 3, 2026

Read your Pyunto Time Management schedule and log time from an AI assistant. E2EE project planning.

已验证STDIO仅桌面效率与工作流数据与分析

概览

AI 生成的概览

把 AI 助手连接到 Pyunto Time Management 账户,可读取日程、汇总已记录工时,并记录或修改时间块与甘特图任务。

可以做什么
  • 查看连接状态:账户、密钥名称、已授予的权限范围以及加密名称能否解密
  • 浏览项目与甘特图任务,把名称解析成其他工具需要的 id
  • 按日期范围读取时间块,并按项目、任务或日期汇总工时
  • 记录、修改、删除单个时间块,并列出或恢复最近删除的时间块
  • 创建、完成、重新打开、重命名、移动或归档任务,并设置每月工时目标
适用场景
适合让助手回答时间花在哪里、根据已记录工时写工作日志或日记,或在不打开应用的情况下记录和调整时间条目、安排任务。面向已经在使用 Pyunto Time Management 的用户,包括自托管实例。
运行要求
以 stdio 在本地运行,通常通过 npx 启动 npm 包。读取或写入数据需要在 PYUNTO_TM_API_KEY 中提供 API 密钥;仅列出工具无需密钥。自托管实例可选 PYUNTO_TM_BASE_URL;要解密端到端加密的名称和备注,需提供 PYUNTO_TM_PASSWORD 或 PYUNTO_TM_PASSWORD_FILE。需要能访问 Pyunto TM 服务器的网络。
安装前请注意
API 密钥是机密,其权限范围决定助手能做什么;tasks:write 与 blocks:write 分开授予,只给需要的权限。多个工具会写入真实日历和计划:log_time、update_time_block、delete_time_block、create_task、update_task、complete_task 和 set_monthly_target 都会改动实际数据。delete_time_block 会永久删除单个时间块及其附件,仅在 14 天内可恢复。移动任务日期会连带移动依赖它的任务。若提供账户密码,它只在本地用于解包私钥。

安装

在 SourceWeft 中

  1. 打开 控制台中的 Time Management,将其添加到工作区。
  2. 为需要使用其工具的对话启用该服务。

Desktop only,通过 STDIO。 STDIO 服务会启动本地进程,因此需要 SourceWeft 桌面宿主。

其他 MCP 客户端

参照 仓库 中的启动说明。

README

@pyunto/tm-mcp

MCP (Model Context Protocol) server for Pyunto Time Management. Connects Claude Code, Claude Desktop, Codex CLI, Cursor and any other MCP client to a Pyunto TM account so an assistant can read the schedule, summarise where the hours went, and log time.

User-facing setup instructions live at https://tm.pyunto.com/docs/mcp.en.md (日本語: https://tm.pyunto.com/docs/mcp.ja.md). This file is the developer view.

Tools

ToolScope
whoami — account, key name, scopes, decryption status—
list_projectsprojects:read
list_tasks (filter by project or by a date the task covers)projects:read
list_time_blocks (range ≤ 92 days)blocks:read
time_summary (grouped by project / task / date)record:read
log_time (writes)blocks:write
update_time_block (writes)blocks:write
delete_time_block — one id, restorable for 14 daysblocks:delete
list_deleted_time_blocks / restore_time_blockblocks:read / blocks:write
create_task — a Gantt task with start and end datestasks:write
complete_task — mark finished, or reopentasks:write
update_task — rename, move dates, archivetasks:write
set_monthly_target — the budget the daily draft divides uptasks:write

Every tool returns JSON as text. Failures come back as isError results whose message names the fix (missing scope, wrong key, locked E2EE) rather than throwing at the transport level — an agent can act on that, a stack trace it cannot.

Install

bash
npx -y @pyunto/tm-mcp        # what an MCP client config runs

Build from source

This package lives in an npm-workspaces repository together with the SDK it depends on; build from the repository root so the workspace link is used:

bash
npm installnpm run build                 # both packagesnpm test                      # sdk crypto round-trips + 25 MCP tool checks

Result: mcp/dist/cli.js, the executable stdio server (dist/index.js stays a side-effect-free module the tests can import).

Configuration

Environment only — MCP clients spawn the process and inject env from their config file.

The server starts without an API key and answers tools/list — a client or a directory can see what it offers before anyone signs up. Calling a tool without one returns the same guidance as an isError rather than an opaque 401, and whoami reports the missing key instead of failing.

VariableRequiredDefaultPurpose
PYUNTO_TM_API_KEYfor data—ptm_…, minted in Settings → Account → API keys. Listing the tools works without it
PYUNTO_TM_BASE_URLhttps://tm.pyunto.comSelf-hosted instances
PYUNTO_TM_PASSWORDnoneUnwraps the private key locally so E2EE names/memos decrypt
PYUNTO_TM_PASSWORD_FILEnoneSame, read from a file instead of the env
json
{  "mcpServers": {    "pyunto-tm": {      "command": "npx",      "args": ["-y", "@pyunto/tm-mcp"],      "env": { "PYUNTO_TM_API_KEY": "ptm_…" }    }  }}

How E2EE is handled

Content fields are encrypted in the user's browser; the server never holds the plaintext, and neither does this process by default. Given a password, the server's password-wrapped private key blob (GET /api/v1/keys, scope keys:read) is unwrapped here, in memory, and used to open each project's sealed DEK. The password is never transmitted and nothing is written to disk.

Without a password the server still answers everything schedule-shaped — ids, dates, times, durations, aggregates — and name fields come back empty with a NOTE: explaining why, so the model reports the limitation instead of inventing names.

Layout

src/index.ts     tool definitions + implementations + server wiringsrc/cli.ts       the bin: calls start()test/tools.mjs   stub /api/v1 server, exercises every tool and error pathtest/cli.mjs     spawns the built bin (directly and via symlink) over real MCP

callTool(name, args) is exported for tests; start() attaches the transport and is called only by cli.ts.

delete_time_block is deliberately single-id: no range or filter delete is exposed, because an automated caller getting a range wrong destroys an unbounded amount of data that has no restore path. Its tool description requires the model to show the user the exact block first and get a yes for that block.

There is no delete_task for the same reason, and a sharper one: removing a task detaches every block that logged time against it (tasks.id ON DELETE SET NULL) and orphans its subtasks, with nothing to restore from. update_task offers archive instead — the task leaves every view and can be brought back.

tasks:write is separate from blocks:write on purpose. Recording time against a plan and rewriting the plan are different powers; a key given to something that only logs work should not be able to move deadlines.

update_task returns an array. Moving a task's dates drags its dependents along, and the caller is told what else moved rather than discovering it on the next read.

来源:mcp/README.md,提交 6add94b

工具

14

whoami

Who this MCP server is connected as: account email, API key name, granted scopes, server URL, and whether end-to-end-encrypted names can be decrypted. Call this first when a tool fails, to see which scopes are missing.

无需输入参数。

list_projects

List every project the account can see, with id, name, colour and role (owner/editor/viewer). Use it to resolve a project name the user typed into the project_id other tools need. Requires the projects:read scope.

无需输入参数。

list_tasks

List Gantt tasks (and subtasks) with their id, project, name and date range. Filter to one project with project_id. Use it to resolve a task name into the task_id that log_time takes. Requires the projects:read scope.

输入参数active_onproject_id

list_time_blocks

The individual time blocks logged between two dates (inclusive, max 92 days), with start/end times, duration, project, task and memo. This is the detailed schedule; use time_summary when you only need totals. Requires blocks:read.

输入参数date_to必填date_from必填

time_summary

Total hours logged between two dates (inclusive, max 92 days), grouped by project, by task, or by day. This is the tool to answer 'how much time did I spend on X' and to write a work log or diary entry. Requires record:read.

输入参数date_to必填group_bydate_from必填

log_time

Record a block of worked time on the calendar. Writes to the user's real schedule, so confirm the project, date and times with them before calling it. Requires the blocks:write scope; for an end-to-end-encrypted project a memo additionally needs PYUNTO_TM_PASSWORD so it can be encrypted locally.

输入参数end必填date必填memostart必填task_idproject_id必填

update_time_block

Change an already-recorded time block: its date, start/end times, memo, or the task it counts towards. Get block_id from list_time_blocks. Only the fields you pass change. Tell the user what the block currently says and what it will say before calling this. Requires the blocks:write scope; changing a memo on an end-to-end-encrypted project also needs PYUNTO_TM_PASSWORD, and project_id so the memo is encrypted under the right key.

输入参数enddatememostarttask_idblock_id必填project_id

delete_time_block

Permanently delete ONE recorded time block, along with any files attached to it. Before calling it you MUST show the user the block you are about to delete (date, start and end time, project, task and memo, from list_time_blocks) and get an explicit yes for that specific block. Never infer the block from a vague instruction, and never delete several blocks because the user described a range — confirm and delete them one at a time. A deleted block can be put back with restore_time_block for 14 days, after which it and its attachments are purged for good and cannot be recovered; say so when you report the deletion, and do not tell the user the deletion is irreversible. Requires the blocks:delete scope, which is separate from blocks:write and has to be granted deliberately.

输入参数block_id必填

list_deleted_time_blocks

Time blocks that were deleted but can still be restored, newest first, each with the date it was deleted and the deadline for putting it back. Use it to find the block_id for restore_time_block when the user says they deleted something by mistake. Requires the blocks:read scope.

无需输入参数。

restore_time_block

Put a deleted time block back on the calendar, along with any files that were attached to it. Get block_id from list_deleted_time_blocks. Works only within the 14-day recovery window; after that the block is gone for good. Requires the blocks:write scope.

输入参数block_id必填

create_task

Add a task to the Gantt chart — a piece of work with a start and end date, which is what time blocks are later logged against. Use this for work that spans days or longer; for time already worked use log_time instead. Writes to the user's real plan, so confirm the project, name and dates with them first. Requires the tasks:write scope; an end-to-end-encrypted project also needs PYUNTO_TM_PASSWORD so the name can be encrypted locally.

输入参数name必填end_date必填project_id必填start_date必填parent_task_id

complete_task

Mark a Gantt task finished, or reopen it. The task stays on the chart, drawn dimmed — nothing is deleted. This is how a finished piece of work shows up on the plan. Get task_id from list_tasks. Requires the tasks:write scope.

输入参数task_id必填completed

update_task

Change a task's name, dates, or whether it is archived. Only the fields you pass change. Moving the dates also moves every task that depends on this one, and the reply lists everything that moved — tell the user about those too. Archiving hides a task from every view and is reversible; there is deliberately no way to delete a task through this server, because that would detach the time already logged against it. Requires the tasks:write scope; renaming an end-to-end-encrypted project's task also needs PYUNTO_TM_PASSWORD.

输入参数nametask_id必填archivedend_datestart_date

set_monthly_target

Set how many hours a task should get in one calendar month. This is what the daily draft (the ✨ button) divides across the month's remaining workdays — a task with no target is skipped entirely, which is the usual reason the draft produces nothing. Requires the tasks:write scope.

输入参数hours必填task_id必填year_month必填

版本历史

3
  1. v0.4.0最新Nov 1, 2026
  2. v0.3.6Oct 18, 2026
  3. v0.3.5Sep 29, 2026

评分

已满足适用检查项的 90%

该评分衡量此收录条目的完整程度,依据注册中心条目、端点与公开来源计算得出。它不是对服务器本身的评价,也不表示服务器安全或当前可用:可连通性是带日期的检查结果,评分也不包含安装量或用户评价。

  • 近 30 天内可连通25 分
  • 已声明认证方式10 分

    请在注册中心条目中声明凭据请求头,或保持端点能够响应探测,以便判定其认证方式。

  • 已列出工具必需15 分
  • 描述必需15 分
  • 图标10 分
  • 文档15 分
  • 许可证10 分