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 分