Teamboard Mcp — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited Teamboard Mcp (Agent Skill) and scored it 96/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 1 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 1 flagged
The text {match} tells the agent to skip the normal "ask the user first" gate. Used adversarially it removes the human-in-the-loop check before destructive or sensitive actions, turning a normally-gated agent into a fire-and-forget executor.
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
MCP server for TeamBoard. Lets any MCP-aware client (Claude Code, etc.) read and write TeamBoard tasks over the HTTP API.
You never pass an ObjectId. Tasks are referenced by TASK-42 (any casing or zero-padding) or by title; people by name, email or username; projects by code or name. When a reference is ambiguous the tool returns the candidate list instead of guessing — so an edit never lands on the wrong task or the wrong assignee.
Add this to your Claude Code MCP config (~/.claude.json, or .mcp.json in any project):
{
"mcpServers": {
"teamboard": {
"command": "npx",
"args": ["-y", "github:uplsuman/teamboard-mcp"],
"env": {
"TEAMBOARD_BASE_URL": "https://teamboard.utplco.com",
"TEAMBOARD_TOKEN": "tbp_..."
}
}
}
}Generate the token in TeamBoard → Settings → API Tokens. Grant the scopes for what you want it to do:
| scope | unlocks |
|---|---|
tasks:read tasks:write | search, read, create, edit, subtasks, links, attachments, saved filters, global search |
comments:write | post, edit and delete comments |
projects:read | project list and member rosters (needed to resolve an assignee) |
users:read | people lookup, assignee: "me", people in global search |
tags:read | the tag vocabulary |
time:read time:write | time logs, timers, manual entries |
notifications:read notifications:write | read your inbox, mark items read |
MCP servers only pick up env changes on restart — run /mcp to reconnect after changing the token.
The server sends these to every client as its instructions, so an agent follows them without being told each time. The text lives in INSTRUCTIONS in index.js; change both together.
Writing tickets and comments
now, how to check it. Function names and file paths belong in the PR.
happens now) plus the screenshot, not a report with headings and lists.
description and comment are literal HTML (<h3>, <p>, <ul>/<li>, <strong>,<code>). Send the tags raw. <p> is stored as-is and shows up as text.
endDate is labelled Due Date.turns it into a real mention chip, so the person is notified and added as a watcher. The name is resolved like an assignee, so an ambiguous one fails with the candidates instead of tagging the wrong person. A plain @Name stays plain text.
Screenshots
add_teamboard_attachmentright after create_teamboard_task) and mention it in the description.
(comment_teamboard_task { files }). The same screenshot goes in the PR description.
Creating
search_teamboard_tasks) to catch duplicates.not a new ticket.
Working a ticket
trackerDecision: "foreground", which starts its timer. No ticket for the work? Ask, or create one.
"background". Never pause or stop the other timer. log_teamboard_time is for time that was genuinely missed, not the normal way to record work.
clarification (10+ characters, 2+ words).
resolution.stop_teamboard_timer. The status change alone may not stop it.
| Tool | What it does |
|---|---|
search_teamboard_tasks | Find tasks by keyword and/or filters — assignee (incl. "me"), project, status[], priority[], type[], dueBefore, dueAfter — or raw jql, or a savedFilter by name. Call before creating, to catch duplicates. |
search_teamboard | One query across tasks, projects, people, departments and saved filters. |
get_teamboard_task | Full detail for one task, plus any of include: ["comments","subtasks","links","attachments","history","time","github"]. github lists the task's commits (short hash, message, author, branch, date, link) and PRs (number, title, state, branches, link); read-only. Comment ids are printed for the edit/delete tools. |
create_teamboard_task | Create a task. Project by code or name, assignee/reporters by name. Type defaults to Task, priority to Low. |
edit_teamboard_task | Update title, description, status, priority, type, assignee, dates, progress, tags, project, parent task, watchers. |
create_teamboard_subtask | Add a subtask under a task; inherits the parent's project. |
link_teamboard_tasks / unlink_teamboard_tasks | Typed relationships: blocks, blocked_by, clones, cloned_by, splits_into, splits_from, causes, caused_by, duplicate_of, relates_to. The inverse is implied. |
comment_teamboard_task | Post a comment (HTML), a threaded reply with replyTo, and/or attach local files. |
react_to_teamboard_comment | Toggle an emoji reaction on a comment. |
my_teamboard_notifications | Your inbox — mentions, assignments, due reminders, team digests. |
read_teamboard_notifications | Mark one read, or all. |
edit_teamboard_comment / delete_teamboard_comment | Change or remove a comment by id — your own, or any as an admin. |
add_teamboard_attachment | Upload a local file onto a task. |
download_teamboard_attachment | Save an attachment to a local file. |
delete_teamboard_attachment | Detach a file and delete it from storage. |
log_teamboard_time | Record work already done (minutes). Manual entries start pending approval. |
start_teamboard_timer / stop_teamboard_timer | Run the clock on a task. How many foreground and background timers can run at once is a workspace setting (default 1 and 2). |
my_teamboard_timers | What you are tracking right now, and for how long. |
list_teamboard_filters / save_teamboard_filter / delete_teamboard_filter | Saved JQL queries; run one with search_teamboard_tasks { savedFilter }. |
list_teamboard_tags | The workspace tag vocabulary — reuse a name rather than inventing a variant. |
delete_teamboard_task | Permanently delete a task (admin only, irreversible). |
list_teamboard_projects | Projects you can see, with their codes. |
list_teamboard_users | People by name/email; pass project for just that project's members. |
Notes that bite:
description and comment take literal<h3>/<ul>/<li>/<strong>/<code> markup, not plain text with newlines.
list_teamboard_userswith project shows who is eligible; the assignee lookup is scoped to that roster, so a non-member name fails with the roster rather than picking someone.
assignee, project, parent or type.startup**, so the tool descriptions list your real values and a typo is rejected locally. Resolutions are the one exception to local rejection: a project may override the workspace list, so the value is sent as given and the server judges it.
resolution — moving a task into a testing or done status (In Testing,Completed, Cancelled, Closed…). A status says where a task is; the resolution says how it ended, and the move is refused without one. Testing → done carries the resolution already given, so it isn't asked twice.
trackerDecision — moving a task into an in-progress status, which would startthe assignee's timer. Pass skip to change the status without starting a timer, or foreground to start it. The other values resolve a clash when that person is already tracking something.
clarification (10+ chars, 2+ words) when an active task's due date changes, and a due date before a task can move into in-progress ("Please add the end date first").
pnpm install
node test.mjs # resolution tests, no network (fetch is stubbed)
TEAMBOARD_BASE_URL=http://localhost:3000 TEAMBOARD_TOKEN=tbp_xxx node index.jsresolve.js holds the API client and the reference resolvers; index.js is only tool definitions and formatting.
get_teamboard_task with a full include issues itsfive sub-requests together rather than one after another.
round trip per minute, not one per tool call. The TTL is deliberately short: a stale hit here would address the wrong row. invalidateCache() clears it.
URL in the message instead of hanging the tool call.
with an "…and N more" line, so one busy task cannot bury the rest of a reply.
once per tool, on the field that needs it, not repeated in the description.
Attachments are addressed by name, never by their storage URL, and an ambiguous name lists the candidates rather than picking one — deleting the wrong file is not recoverable.
TeamBoard with personal-access-token support on /api/tasks, /api/tasks/meta, /api/tasks/statuses, /api/tasks/:id/{subtasks,links,activities,github}, /api/projects, /api/projects/:id/members, /api/users, /api/users/me, /api/comments, /api/comments/:id and /api/uploads/* (TB dev after Sep 2026).
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.