Power your agent
Lệnh của chính nhà phát hành. Không phải endpoint của PlugMCP.
Host a copy with our keys and logs.
npx -y [email protected]Claude Code không có liên kết cài đặt cho lệnh cục bộ. Sao chép lệnh bên dưới và chạy trong terminal.
claude mcp add awesome-things -- npx -y awesome-things@1.8.0Chạy trong terminal.
{ "mcpServers": { "awesome-things": { "command": "npx", "args": [ "-y", "[email protected]" ] } }}claude_desktop_config.json
URL từ xa hay một lệnh trên máy của bạn? Hai cách cài đặt khác nhau thế nào
Chạy trên PlugMCP
A new server you create. It is not this listing's official endpoint. Use the publisher install when you want their URL and their credentials.
Mở🤖 MCP server — connect ChatGPT, Claude & Cursor to manage your tasks
⚙️ CLI — automate from the terminal, cron jobs & pipelines (thi, things)
🌐 HTTP REST API — build your own frontend; OpenAPI spec + Swagger UI included
📦 JS/TS API — use Things3 as a fully-typed library
🔗 Built-in tunnels — expose your Mac via localtunnel, ngrok or frp
🔐 Bearer-token auth — protect remote access out of the box
✅ 21 operations — todos, projects, tags, areas, move, delete & batch
🍏 AppleScript-native — talks to Things3 directly, validated with Zod
# Library / programmatic use
npm install awesome-things
yarn add awesome-things
pnpm add awesome-things
bun add awesome-things
# Global CLI — exposes the `awesome-things`, `things` and `thi` binaries
npm install -g awesome-things
Or run any command on demand without installing:
npx awesome-things <command>
Expose Things3 to a remote AI client (ChatGPT, Claude) in one command:
npx awesome-things server --tunnel
# ➜ MCP: https://my-awesome-things3-mcp.loca.lt/mcp/auth/uvhSdaAsd1qmtAnHa895bcjwTAnBxw
What the AI can do once connected:
Local (stdio) — add to your MCP client configuration:
{
"mcpServers": {
"things3": {
"command": "npx -y awesome-things mcp"
}
}
}
Remote (HTTP over a tunnel) — point your client at the URL printed by server --tunnel:
{
"mcpServers": {
"things3": {
"type": "http",
"url": "https://my-awesome-things3-mcp.loca.lt/mcp/auth/uvhSdaAsd1qmtAnHa895bcjwTAnBxw"
}
}
}
ChatGPT — Settings → Beta Features, enable "Developer Mode" and "MCP apps", then add the server in the UI (no file editing).
Claude Desktop — ~/Library/Application Support/Claude/claude_desktop_config.json
Claude Code — .claude/settings.json (project) or ~/.claude/settings.json (global)
Cursor — .cursor/mcp.json (project) or ~/.cursor/mcp.json (global)
Install globally (
npm i -g awesome-things) to avoid thenpxstartup delay.thiandthingsare aliases forawesome-things.
thi list today
thi add "Buy milk" --list today
thi done "Buy cheese"
thi search "report"
thi info # which build is running, and where it came from
Global flags: --json for machine-readable output, --format pretty|table|plain (-f) for the rendering style.
# Create a todo
things add "Buy milk"
things add "Submit report" --notes "Q1" --due 2026-03-01 --tags work urgent --list today
things add "Sand the stool" --when 2026-09-12 # Things' "When", not the deadline
things add "Call the bank" --when tomorrow
things add "Standup" --when 2026-09-12@11:00 # with a time = phone notification
things add "Fix leak" --area Home
things add "Plan sprint" --project "Q1 Planning"
# List todos (inbox | today | anytime | upcoming | someday | logbook)
things list # today (default)
things list inbox
things list today --status open
# Finish a todo — three different things (by name or --id)
things done "Buy milk" # done → Logbook, as completed
things cancel "Buy milk" # cancelled → Logbook, will not happen
things rm "Buy milk" # deleted → Trash (alias: things trash)
# Batch: the same operation over several ids in one go
things rm --ids ABC123 DEF456 GHI789
things done --ids ABC123 DEF456
# Update a todo
things update "Submit report" --new-name "Submit Q1 report"
things update "Submit report" --new-due 2026-03-15
things update "Submit report" --new-due none # clear the deadline
things update "Submit report" --new-when 2026-09-12 # YYYY-MM-DD[@HH:MM] | today | tomorrow | evening
things update "Submit report" --new-when none # clear the When date
things update --ids ABC123 DEF456 --new-tags work # same update, several todos
# Checklists (need AWESOME_THINGS_URL_TOKEN)
things add "Pack" --checklist Passport Tickets Charger
things update "Pack" --new-checklist Passport Tickets
# Search
things search "report"
# Projects
things project add "Q1 Planning" --area Work
things project add "Trip" --todo "Book flight" --todo "Pack" # project + its todos in one call
things project update "Q1 Planning" --new-notes "See https://example.com/roadmap"
things project list
things project todos "Q1 Planning"
things project rm "Q1 Planning" # project + its todos → Trash
# Tags & areas
things tags
things areas
things area Work # todos sitting directly in an area (no project)
# Move (destination: inbox | today | evening | anytime | upcoming | someday)
things move todo "Buy milk" today
things move todo-to-project "Submit report" "Q1 Planning"
things move todo-to-area "Submit report" Work
things move project-to-area "Q1 Planning" Work
# Remove associations
things remove todo-from-project "Submit report"
things remove project-from-area "Q1 Planning"
# Environment info — version, which binary is actually running, where it came from
things info
things info --json
things server # default port 32123, random Bearer token printed on startup
things server --no-token # disable auth (local use)
things server --port 8080 # custom port
The server also serves a home page at /, a health check at /health, Swagger UI at /api, and the raw OpenAPI spec at /api/openapi.json.
curl http://localhost:32123/api/todos?list=today
curl -X POST http://localhost:32123/api/todos \
-H "Content-Type: application/json" \
-d '{"name": "Buy milk", "list": "today"}'
curl "http://localhost:32123/api/todos/search?q=report"
# When auth is enabled, pass the printed token:
curl http://localhost:32123/api/tags -H "Authorization: Bearer <token>"
things server dies with its terminal. things daemon installs the same server as a launchd
user agent: it starts at login, restarts itself after a crash, and writes both streams to log
files you can tail at any time.
things daemon install # write the agent, start it, print URL + token
things daemon install --port 8080 --tunnel frp --domain things.example.com
things daemon install --no-start # only write the plist
things daemon status # running? healthy? which pid, which token
things daemon restart # pick up a new build
things daemon stop # stop without uninstalling
things daemon uninstall # stop and remove the agent (--purge also drops the logs)
The log holds events, not the startup banner: one started line (version, pid, URL, auth,
config file), tunnel when one opens, stopped on SIGTERM, then a line per request. The token
and the MCP client configs are only printed to a terminal.
Watching it work — the daemon has no terminal, so the logs are the way in:
things daemon logs # last 50 lines of both streams
things daemon logs -f # follow live (Ctrl+C to stop)
things daemon logs -f --err # only errors
things daemon logs -n 200 --out # last 200 request lines
things daemon logs --clear # truncate both files
Notes:
--token,
AWESOME_THINGS_TOKEN, the previously installed agent, or generated). A daemonized server
cannot print a fresh random token anywhere you would see it.install is safe to repeat: with the same flags and environment the plist comes out
identical and the running daemon is left alone. Only a real change restarts it.install bakes in PATH, HOME and
every AWESOME_THINGS_* / FRP_* / NGROK_AUTHTOKEN variable from the shell you ran it in —
change one of them and run things daemon install again.awesome-things, not to bun or node. The daemon runs through a
tiny launcher app (~/.local/share/awesome-things/awesome-things.app, built and signed on
install), so macOS asks "awesome-things wants to control Things3" and the permission covers
this app only — never every script on your runtime. Allow it on the first Things3 call; it is
listed under System Settings → Privacy & Security → Automation. Building it needs the Command
Line Tools (xcode-select --install); --no-launcher skips it.install signs the
launcher with it automatically. Pick one with AWESOME_THINGS_SIGN_IDENTITY (SHA-1 or part of the
name), or - for ad-hoc..1 on install/restart once they pass 10 MB — launchd rotates nothing itself.import { createTodo, listTodos, completeTodo } from 'awesome-things';
// Create a todo
await createTodo({ name: 'Buy milk', list: 'today' });
// List today's open todos
const todos = await listTodos({ list: 'today', status: 'open' });
// Complete a todo
await completeTodo({ name: 'Buy milk' });
Built-in tunnel support exposes the local server for remote access — useful for connecting AI clients (ChatGPT, Claude) to Things3 on your Mac. Three providers are supported.
# localtunnel (default) — free, no signup, no config
things server --tunnel
things server --tunnel --domain myapp # custom subdomain
# ngrok — needs an auth token
things server --tunnel=ngrok --ngrok-token=YOUR_TOKEN
NGROK_AUTHTOKEN=YOUR_TOKEN things server --tunnel=ngrok
things server --tunnel=ngrok --domain myapp.ngrok-free.app
# frp — self-hosted reverse proxy; needs the `frpc` binary + your own frp server
FRP_SERVER_ADDR=frp.example.com things server --tunnel=frp
FRP_SERVER_ADDR=frp.example.com things server --tunnel=frp --domain myapp
Settings live in ~/.config/awesome-things/config.json (or $XDG_CONFIG_HOME/awesome-things/,
or any path in AWESOME_THINGS_CONFIG). Every key mirrors an environment variable below:
{
"$schema": "https://unpkg.com/awesome-things/config.schema.json",
"port": 32123,
"token": "your-bearer-token",
"urlToken": "things-url-scheme-token",
"tunnel": "frp",
"domain": "things.example.com",
"frp": { "serverAddr": "frp.example.com", "serverPort": 7000, "token": "frp-token" },
"ngrok": { "authtoken": "ngrok-token" }
}
_ or // is ignored at every level —
"_frp": {...} switches a section off, "_note": "..." explains a value. Any other unknown key
is a typo and an error.config.json > default.$schema gives your editor autocompletion and validation; the schema ships in the npm package.chmod 600: it holds tokens, and a looser mode prints a warning.things daemon restart is
enough. Values that come from the file are never frozen into the launchd plist.All
FRP_*variables also accept theAWESOME_THINGS_FRP_*prefix.
AppleScript is the only programmatic interface to Things3 on macOS. Each operation is a typed function that builds an AppleScript snippet, runs it via osascript, and parses the result. The four interfaces are thin layers over the same set of operations.
All operations identify todos and projects by name or by id, and every input is validated with a Zod schema. Todo operations also accept ids: string[] and then run as a batch.
Done, cancelled and deleted are three different things. completeTodo puts a todo in the
Logbook as done — do not use it to get rid of a todo you created by mistake, or the Logbook ends
up full of work nobody did. Use cancelTodo when the work will not happen, and deleteTodo /
deleteProject (which move the item to the Trash) when it should never have existed.
What Things3 itself cannot do: recurring todos (repeat) and headings inside a project are
exposed by neither AppleScript nor the URL scheme, so this package cannot set them — Repeat has to
be set by hand in Things, and headings need a things:///json payload.
Things3 must be running, and the app that launched the process (Terminal, your editor, an agent
runtime) needs Automation access to Things3 in System Settings → Privacy & Security →
Automation. Without it macOS withholds the scripting dictionary and every call fails with
-2740 / -2741 / -1743 — the package detects that and says so instead of forwarding raw
AppleScript noise. Sandboxed environments without Apple Events access cannot reach Things3 at all.
import {
createTodo,
searchTodos,
updateTodo,
createProject,
moveTodoToProject,
deleteTodo,
} from 'awesome-things';
// Create with all options
await createTodo({
name: 'Submit report',
notes: 'Q1 financials',
due_date: '2026-03-01',
tags: ['work', 'urgent'],
list: 'today',
});
// Create a todo directly in an area
await createTodo({ name: 'Fix leak', area: 'Home' });
// Search across all lists
const results = await searchTodos({ query: 'report' });
// Update a todo
await updateTodo({ name: 'Submit report', new_due_date: '2026-03-15' });
// Create a project together with its todos
await createProject({
name: 'Q1 Planning',
area: 'Work',
todos: ['Draft the roadmap', { name: 'Review with the team', when: 'tomorrow' }],
});
await moveTodoToProject({ todo_name: 'Submit report', project_name: 'Q1 Planning' });
// Trash seven todos in one call — and see which ones failed
const result = await deleteTodo({ ids: ['ABC', 'DEF', 'GHI'] });
result.ids; // the ones that worked
result.results; // [{ id, ok, message }, ...]
Every todo function accepts ids: string[] instead of id to run as a batch.
createTodo({ name, notes?, due_date?, when?, tags?, list?, project?, area?, checklist? }) — create a todolistTodos({ list, status? }) — list todoscompleteTodo({ name?, id?, ids? }) — mark as done (→ Logbook)cancelTodo({ name?, id?, ids? }) — mark as cancelled (→ Logbook)deleteTodo({ name?, id?, ids? }) — delete (→ Trash)updateTodo({ name?, id?, ids?, new_name?, new_notes?, new_due_date?, new_when?, new_tags?, new_checklist? }) — update a todosearchTodos({ query }) — search todos by namecreateProject({ name, notes?, area?, todos? }) — create a project, optionally with its todosupdateProject({ project_name, new_name?, new_notes?, new_due_date?, new_tags?, new_area? }) — update a projectdeleteProject({ project_name }) — delete a project and its todos (→ Trash)listProjects({ area? }) — list projectsgetProjectTodos({ project_name, status? }) — get project todosgetAreaTodos({ area_name, status? }) — todos sitting directly in an arealistTags() — list all tagslistAreas() — list all areasmoveTodo({ todo_name?, id?, ids?, destination }) — move todo to a listmoveTodoToProject({ todo_name?, id?, ids?, project_name }) — move todo to a projectmoveTodoToArea({ todo_name?, id?, ids?, area_name }) — move todo to an areamoveProjectToArea({ project_name, area_name }) — move project to an arearemoveTodoFromProject({ todo_name?, id?, ids? }) — remove todo from its projectremoveProjectFromArea({ project_name }) — remove project from its areaimport { createTodo } from 'awesome-things'; // main aggregator
import { createTodo } from 'awesome-things/api'; // direct API
import 'awesome-things/mcp'; // MCP server
import 'awesome-things/server'; // HTTP server
import 'awesome-things/cli'; // CLI
import { execute, tellThings } from 'awesome-things';
const result = await execute(
tellThings('show quick entry panel with properties {name:"Hello"}'),
);
Add a todo for every .log file in the current directory:
for f in *.log; do thi add "Review $f" --list today; done
List today's open todos as JSON and pipe into jq:
thi list today --status open --json | jq '.todos[].name'
bun run test # lint + types + unit tests + size-limit
bun run test:unit # unit tests only (bun:test)
Unit tests cover the pure functions (AppleScript helpers, Zod schemas) and the HTTP server. Integration tests require macOS with Things3 running.
bun install
bun run build # build (tsdown -> lib/)
bun run dev # watch mode
bun run start # MCP server (stdio)
bun run server # HTTP API server
bun run cli # run the CLI
bun run fix # auto-fix lint & formatting (biome)
id); duplicate names act on the first match.MIT © Igor Suvorov — see LICENSE.
awesome-things — Manage Things3 from anywhere 📋
Awesome swiss knife for Things3 – MCP server for AI, CLI, HTTP API, JS/TS API and Web
{ "mcpServers": { "awesome-things": { "command": "npx", "args": [ "-y", "[email protected]" ] } }}Công cụ
Chưa có danh sách công cụ. Máy chủ từ xa sẽ có danh sách sau lần kiểm tra tình trạng đầu tiên.
Câu hỏi thường gặp
Sao chép cấu hình stdio cho Cursor, Claude Desktop, Claude Code hoặc VS Code trên trang này. Cấu hình dùng bản cài đặt của nhà phát hành, không phải proxy của PlugMCP.
Bản ghi này không liệt kê biến môi trường bắt buộc nào.
Awesome Things chạy cục bộ qua stdio.
Kiểm tra lần cuối: 2026-11-13. Awesome swiss knife for Things3 – MCP server for AI, CLI, HTTP API, JS/TS API and Web