Laver REST API를 통해 AI agent가 칸반 보드, 티켓, 스프린트 및 워크스페이스 위키를 관리할 수 있게 합니다.
다른 이름 Developyn/laver-mcp, @laver/mcp
- 스타
- 0
- Tools
- 49
- 다운로드, 30일
- —
- 최신 버전
- 0.5.0
- 마지막 커밋
- 2026년 8월 27일
- 라이선스
- MIT
Laver MCP란?
Laver은(는) MCP server입니다. Laver REST API를 통해 AI agent가 칸반 보드, 티켓, 스프린트 및 워크스페이스 위키를 관리할 수 있게 합니다. 49개 도구를 제공합니다. 예: list_workspaces, list_boards, get_board.
프로젝트 README에서 생성된 요약
Laver MCP server 정보
이 서버를 사용하면 개발자가 AI agent를 통해 칸반 보드, 티켓 및 워크스페이스 위키를 관리할 수 있습니다. Laver REST API에 직접 연결되어 로컬 상태나 별도의 캐시를 유지하지 않고도 프로젝트 관리 워크플로우를 읽고 실행할 수 있는 도구를 제공합니다. 모든 도구가 공개 API를 호출하는 경량 방식이므로, agent는 API의 에러 응답을 그대로 전달받아 버전 충돌과 같은 특정 문제에 즉각 대응할 수 있습니다.
서버를 설정하려면 LAVER_API_KEY 환경 변수나 파일 경로를 통해 워크스페이스 범위의 API key를 제공해야 합니다. LAVER_API_URL 설정을 통해 커스텀 호스트를 사용하도록 구성할 수 있습니다. 사용자는 검색 도구로 워크스페이스 전체를 검색하는 것부터 복잡한 보드 구조를 관리하는 것까지 폭넓은 작업을 수행할 수 있습니다. 티켓 흐름 읽기, 하위 작업 관리, 위키 트리 정리 등을 위한 전용 도구가 포함되어 있어, AI 클라이언트에서 직접 트리아지(triage), 스탠드업 미팅, 문서 업데이트를 자동화하려는 팀에 적합합니다.
활용 사례
- 1티켓 업데이트 동기화개발자가 agent에게 최근에 업데이트된 티켓만 요청하도록 지시하여 보드 변경 사항을 추적할 수 있습니다.
프로젝트의 README를 바탕으로 작성되었습니다. 모든 활용 사례는 해당 내용의 인용문과 대조하여 확인합니다.
Laver MCP 설치 방법
형식 확인: 2026년 11월 10일Claude Code에 Laver MCP 설치
$ claude mcp add --env LAVER_API_KEY=<YOUR_LAVER_API_KEY> laver-mcp -- npx -y @laver/mcpCursor에 Laver MCP 설치
~/.cursor/mcp.json
{
"mcpServers": {
"laver-mcp": {
"command": "npx",
"args": [
"-y",
"@laver/mcp"
],
"env": {
"LAVER_API_KEY": "<YOUR_LAVER_API_KEY>"
}
}
}
}VS Code에 Laver MCP 설치
.vscode/mcp.json
{
"servers": {
"laver-mcp": {
"type": "stdio",
"command": "npx",
"args": [
"-y",
"@laver/mcp"
],
"env": {
"LAVER_API_KEY": "<YOUR_LAVER_API_KEY>"
}
}
}
}Claude Desktop에 Laver MCP 설치
claude_desktop_config.json
{
"mcpServers": {
"laver-mcp": {
"command": "npx",
"args": [
"-y",
"@laver/mcp"
],
"env": {
"LAVER_API_KEY": "<YOUR_LAVER_API_KEY>"
}
}
}
}Codex에 Laver MCP 설치
~/.codex/config.toml
[mcp_servers.laver-mcp] command = "npx" args = ["-y", "@laver/mcp"] [mcp_servers.laver-mcp.env] LAVER_API_KEY = "<YOUR_LAVER_API_KEY>"
Gemini CLI에 Laver MCP 설치
~/.gemini/settings.json
{
"mcpServers": {
"laver-mcp": {
"command": "npx",
"args": [
"-y",
"@laver/mcp"
],
"env": {
"LAVER_API_KEY": "<YOUR_LAVER_API_KEY>"
}
}
}
}Laver MCP 도구 49
README에서 감지됨list_workspaceslist_boardsget_boardlist_ticketslist_workspace_ticketsget_ticketget_ticket_commentsget_ticket_flowlist_custom_fieldslist_labelssearchcreate_ticket
| list_workspaces | — | |
| list_boards | — | |
| get_board | — | |
| list_tickets | — | |
| list_workspace_tickets | — | |
| get_ticket | — | |
| get_ticket_comments | — | |
| get_ticket_flow | — | |
| list_custom_fields | — | |
| list_labels | — | |
| search | — | |
| create_ticket | — |
도구 49개 모두 보기도구 수 줄이기
update_ticketmove_ticketcomment_on_ticketupdate_commentdelete_commentmark_comments_readadd_subtaskupdate_subtaskdelete_subtaskarchive_ticketdelete_ticketcreate_boardlink_ticketsunlink_ticketscreate_statusupdate_statusreorder_statusesdelete_statuscreate_groupreorder_groupsdelete_groupcreate_custom_fieldupdate_custom_fielddelete_custom_fieldcreate_labelupdate_labeldelete_labellist_ticket_attachmentsget_ticket_attachmentupload_ticket_attachmentdelete_ticket_attachmentlist_automationsget_automationlist_automation_runscreate_automationupdate_automationdelete_automation
| update_ticket | — | |
| move_ticket | — | |
| comment_on_ticket | — | |
| update_comment | — | |
| delete_comment | — | |
| mark_comments_read | — | |
| add_subtask | — | |
| update_subtask | — | |
| delete_subtask | — | |
| archive_ticket | — | |
| delete_ticket | — | |
| create_board | — | |
| link_tickets | — | |
| unlink_tickets | — | |
| create_status | — | |
| update_status | — | |
| reorder_statuses | — | |
| delete_status | — | |
| create_group | — | |
| reorder_groups | — | |
| delete_group | — | |
| create_custom_field | — | |
| update_custom_field | — | |
| delete_custom_field | — | |
| create_label | — | |
| update_label | — | |
| delete_label | — | |
| list_ticket_attachments | — | |
| get_ticket_attachment | — | |
| upload_ticket_attachment | — | |
| delete_ticket_attachment | — | |
| list_automations | — | |
| get_automation | — | |
| list_automation_runs | — | |
| create_automation | — | |
| update_automation | — | |
| delete_automation | — |
구성
LAVER_API_KEY필수비밀A workspace-scoped API key, created in Laver under Admin → API keys. It acts as the person who created it, so it holds exactly their access and can be revoked without touching their account. Supply this or LAVER_API_KEY_FILE.
LAVER_API_KEY_FILEAlternative to LAVER_API_KEY: a path to a file holding nothing but the key, or a .env-style file carrying a LAVER_API_KEY= line. Use this to keep the key out of your client's config file.
LAVER_API_URLOverrides the API host. Defaults to https://api.laver.app, and must be https except for localhost.
| 이름 | 설명 | 유형 |
|---|---|---|
| LAVER_API_KEY | A workspace-scoped API key, created in Laver under Admin → API keys. It acts as the person who created it, so it holds exactly their access and can be revoked without touching their account. Supply this or LAVER_API_KEY_FILE. | 필수비밀 |
| LAVER_API_KEY_FILE | Alternative to LAVER_API_KEY: a path to a file holding nothing but the key, or a .env-style file carrying a LAVER_API_KEY= line. Use this to keep the key out of your client's config file. | |
| LAVER_API_URL | Overrides the API host. Defaults to https://api.laver.app, and must be https except for localhost. |
자동 검사
2026년 11월 14일에 실행됨전체 보고서 →자동 검사를 통과했습니다. 이는 보안 감사가 아닙니다. 코드를 직접 읽고 서버에 필요한 최소한의 권한만 부여하십시오. 검사 방식
- 소스 저장소 연결 및 접근 가능developyn/laver-mcp is public and reachablegithub.com/developyn/laver-mcp ↗통과
- 패키지가 이 저장소를 소스로 지정@laver/mcp names developyn/laver-mcp as sourcenpmjs.com/package/@laver/mcp ↗통과
- 빌드 출처(Sigstore 증명)No build attestation published for this release검사되지 않음
- 오픈 소스 라이선스 (OSI)MITspdx.org/licenses/MIT.html ↗통과
- 최근 1년 내 커밋 또는 릴리스Last commit or release 2026-08-27github.com/developyn/laver-mcp ↗통과
- OpenSSF Scorecard검사되지 않음
- 패키지 이름이 인기 패키지와 유사하지 않음No popular package one edit away from @laver/mcp통과
- 패키지와 의존성에 심각한 보안 권고 없음99 resolved dependencies checked; no critical advisoryosv.dev ↗통과
- 모델에 대한 숨겨진 지침 없음49 tool descriptions and the README read; no override phrases, credential paths or hidden characters통과
- 파괴적인 도구에 라벨 표시됨검사되지 않음
- 원격 endpoint가 MCP 핸드셰이크에 응답검사되지 않음
README
GitHub에서 보기@laver/mcp
An MCP server for Laver. Gives an agent tools to read and drive kanban boards, tickets and the workspace wiki.
Every tool is a thin call to the same public REST API the web app uses. There is no local state, no cache, and no second implementation of anything — if Laver refuses a write, the refusal comes back verbatim, because an agent can act on "409, re-read and retry" and cannot act on "something went wrong".
Setup
Create a workspace-scoped API key in Laver under Admin → API keys. It acts as the person who created it, so it can do exactly what they can do and nothing more, and it can be revoked without touching their account.
{
"mcpServers": {
"laver": {
"command": "npx",
"args": ["-y", "@laver/mcp"],
"env": { "LAVER_API_KEY": "your key here" }
}
}
}
LAVER_API_URL overrides the API host; it defaults to https://api.laver.app.
LAVER_API_KEY_FILE is an alternative to LAVER_API_KEY: a path to either a
file containing nothing but the key, or a .env-style file with a
LAVER_API_KEY=… line among others (an assignment line wins; quotes and an
export prefix are both fine). That is how the .mcp.json in this repo
registers the server without a secret in a tracked file.
A file with neither — no assignment line, and more than one token in it — yields no key at all, and you get the "key is not set" error. It used to send the whole file as the token, which is fine for a file holding one secret and is a leak for anything else.
LAVER_API_URL must be https, except for localhost.
Working in this repo
.mcp.json at the repo root registers this server for anyone who opens the
project, reading the key from the gitignored .env. Nothing to export.
It runs the published package, npx -y @laver/mcp, rather than the
mcp/server.js beside it. That is deliberate: pointing it at the local file
meant everyone here ran the one code path no user takes, and that is precisely
how 0.1.0 shipped with an entry point that never connected its transport when
started through bin — which is the only way a real client starts it. Running
what we publish means we meet what users meet.
If you are editing this server, that same choice will fool you: your changes do nothing until they are published. Point the client at the working copy while you work on it —
{ "command": "node", "args": ["mcp/server.js"] }
— and put it back before you commit. npm run check and
frontend/tests/check-mcp-bin-entrypoint.mjs both run against the working copy
regardless, so the tests never depend on a publish.
A client only connects to MCP servers at startup. claude mcp add while a
session is already running does not retrofit the tools into that session — the
tool list was built before the server existed. Start a new session (or
reconnect from the client's MCP panel) and the tools appear.
Tools
Reading
| Tool | What it gives you |
|---|---|
list_workspaces |
Where to start when you have no uuids |
list_boards |
The boards in a workspace |
get_board |
A board with its status columns, labels, members and tickets |
list_tickets |
Tickets on a board, filterable, paged — updated_since is how you follow a board |
list_workspace_tickets |
Triage across every board at once — overdue, unassigned, or free text |
get_ticket |
One ticket in full, its subtasks, including its version |
get_ticket_comments |
Comments and activity history |
get_ticket_flow |
How long the ticket has spent in each column |
list_custom_fields |
A board's custom field definitions — the uuids update_ticket writes against |
list_labels |
Every label in the workspace, not only the ones already used on one board |
search |
Boards, tickets, wiki pages and comments across a whole workspace at once |
list_workspace_tickets is the stand-up read: it needs no board_uuid, and it
is the only thing here that answers "what is late" and "what does nobody own"
without walking every board. It is deliberately the small sibling of
list_tickets — one page, 50 by default and 200 at most, no cursor — so narrow
it rather than paging it.
get_ticket_flow derives its numbers from the moves already in a ticket's
history. Read visits rather than the by_status totals if you are adding
several tickets up: tickets worked in one batch overlap, and their totals do not.
gaps says when the history and the ticket's current column disagree, which
makes the totals a floor rather than a measurement.
To follow a board, call list_tickets again with updated_since set to the
server_time the previous call returned; you get back the tickets that changed
and nothing else. There is no tool for the server-sent event stream at
GET /boards/:uuid/events — a tool call is one request and one answer, and a
stream that never ends is neither.
Asking for less. A tool result is charged against the model's context, and
three of these calls are the ones you cannot route around: a status_uuid or a
label_uuid comes from get_board, and a page_uuid comes from
get_wiki_tree. On a busy board they were 154 kB, 119 kB and 180 kB. Each now
takes a parameter that narrows the reply, and every one of them is opt-in — a
call that passes none of them is unchanged.
| Call | Gives you | Measured |
|---|---|---|
get_board(include_tasks: false) |
the structure alone — statuses, labels, fields | 155 kB → 17 kB |
get_wiki_tree(depth: 1) |
the top level, nothing nested under it | 119 kB → 1.8 kB |
get_wiki_tree(parent_page_uuid: …) |
one page and everything beneath it | 119 kB → 9.8 kB |
list_tickets(include_descriptions: false) |
titles and uuids without the bodies | 167 kB → 45 kB |
Reach for get_board(include_tasks: false) whenever you called it for a uuid
rather than for the tickets, and list_tickets when you want the tickets —
that one takes status and limit as well.
Writing
| Tool | Notes |
|---|---|
create_ticket |
status takes the column name, or pass status_uuid; markdown in description is parsed |
update_ticket |
Needs version; markdown in description is parsed; custom_fields is keyed by field uuid and replaces the whole object |
move_ticket |
Needs version, and a column — neither column is a 400 |
comment_on_ticket |
Markdown in body is parsed; no version, so it cannot 409 |
update_comment |
Your own comments only; replaces the whole body and is marked as edited |
delete_comment |
Your own comments only; to the trash, and nothing here restores one |
mark_comments_read |
Clears this ticket's unread badge for the user the key acts as |
add_subtask |
One checklist item, appended — plain text, not markdown |
update_subtask |
Tick it off (is_done), rename it, or move it up the list |
delete_subtask |
Not recoverable — a checklist item has no trash |
archive_ticket |
To the trash — recoverable for 30 days |
delete_ticket |
Destroys one already in the trash — permanent |
create_board |
Optionally from a template — crm or sales-leads |
link_tickets |
"this before that" — direction is blocks or blocked_by |
unlink_tickets |
From either end, and removes every kind of link on the pair |
Comments are not versioned, so none of the three comment writes takes a
version and none of them can 409 — the last edit wins. Only the author may
edit or delete one, and somebody else's is a 404 indistinguishable from a
comment that does not exist, so these never report who wrote what.
get_ticket_comments deliberately marks nothing read; mark_comments_read is
the only call that does, and it marks up to the newest comment that exists at
that moment rather than subscribing.
Subtasks are the checklist on a ticket — a progress count on the card, and where
acceptance criteria belong when they are meant to be ticked off one at a time.
get_ticket returns the items themselves, so there is no separate list tool.
Board structure
| Tool | Notes |
|---|---|
create_status |
A new column, appended to the right-hand end |
update_status |
Rename, recolour, or set is_complete — a board has at most one |
reorder_statuses |
The complete list of uuids, in order; a stale list is a 409 |
delete_status |
The column must be empty, and a board keeps one |
create_group |
A swimlane; there is no rename route, so a wrong name is deleted and remade |
reorder_groups |
Same complete-list contract as reorder_statuses |
delete_group |
Tickets in it survive, ungrouped — and their versions all move |
create_custom_field |
Workspace owner or admin; type cannot be changed afterwards |
update_custom_field |
Rename, reorder, or replace a select's whole options list |
delete_custom_field |
Takes every ticket's value in that field with it — no trash, no restore |
This is what a board an agent creates needs to stop being its template's
defaults. The two reorder tools want the whole list because a partial one is how
two simultaneous reorders silently drop a column: read the uuids off get_board
immediately before calling, and re-read on a 409.
Labels
| Tool | Notes |
|---|---|
list_labels |
Workspace-wide, alphabetical — get_board shows only what a board uses |
create_label |
A hex colour is required; names are unique ignoring case and space |
update_label |
Renames it everywhere — one label, not a copy per board |
delete_label |
Removes it from every ticket that carries it |
A label is a workspace object shared by every board, which is the thing to be
sure of before renaming one: it changes for everybody. The uuids these return
are what update_ticket takes as label_uuids.
Attachments
| Tool | Notes |
|---|---|
list_ticket_attachments |
Name, type, size and uuid — never the bytes |
get_ticket_attachment |
Text inline, an image as an image block, anything else via save_to |
upload_ticket_attachment |
file_path for a file on disk, or text + filename for something written |
delete_ticket_attachment |
To the trash for 30 days; there is no restore tool |
get_ticket reports attachment_total, so you know whether listing is worth a
round trip.
Binary content crosses the tool boundary by not crossing it. A tool result
is text, Laver allows 25 MB per file, and 25 MB of base64 is roughly nine
million tokens — so only text, CSV and images under 4 MB come back inline, and
everything else needs save_to, which writes the file to a path on the machine
running this server (normally the agent's own, since the client starts it as a
subprocess). Uploads go the same way round: file_path reads from that machine
and costs no context.
An image comes back as an MCP image block rather than as text, which is the only form a model can actually look at — that is the whole point of the tool, since the screenshot somebody attached is usually the specification.
Wiki
list_wikis, search_wiki, get_wiki_tree, get_wiki_page,
get_wiki_page_version, append_wiki_page, update_wiki_page,
create_wiki_page — which takes the body as markdown and nests under
parent_page_uuid — and restore_wiki.
Archiving a wiki (done in the browser; there is no tool here for it) takes it
and every page under it out of list_wikis entirely. list_wikis takes
archived: true to see those instead — the only place an archived
wiki_uuid is visible at all — and restore_wiki is the only thing here
that does something with one: it un-archives the wiki, and every page under
it, in one call.
get_wiki_page_version is how you read something that was overwritten. It is a
read: the page does not move. There is deliberately no tool to put an old
version back — read it and write the wording you want with update_wiki_page,
which leaves a version of its own behind.
append_wiki_page adds to the end of a page and update_wiki_page replaces
what is on one; prefer the first when you are adding, because it takes no
version and two agents appending at once both get their text. Replacing takes
the version from get_wiki_page, and a page somebody wrote to meanwhile is a
409 carrying the current version rather than an overwrite — the guard that lets
this exist at all, since wiki pages sit behind a live collaborative editor. A
replace re-seeds that editor from what it just wrote, so a colleague with the
page open sees the new text instead of putting the old text back, and every
previous save stays readable through get_wiki_page_version. There is still no
tool to delete a page.
The markdown goes through the same parser ticket descriptions do. Headings,
lists, tables, code blocks, blockquotes, rules and links survive; so does an
 image, as a reference to that URL — there is no tool here to
upload an attachment, so the URL has to be public already. Raw HTML is kept as
literal text rather than interpreted.
Automations
| Tool | Notes |
|---|---|
list_automations |
The rules on a board, each with its version |
get_automation |
One rule in full — the conditions and actions an edit replaces |
list_automation_runs |
What a rule has actually done, newest first |
create_automation |
Owner or admin only — and see below before calling it |
update_automation |
Needs version; enabled: false is the reversible stop |
delete_automation |
Needs version, and takes the rule's whole run history with it |
An automation rule is a trigger, optional conditions and up to twenty actions, stored against a board. Three things about them are worth knowing before an agent touches these tools:
- A rule created here is live immediately. It fires on its trigger within a
couple of seconds, so read
list_automation_runsafterwards rather than creating one speculatively to see what it would do. - A rule is a standing grant. It runs as the user this key acts as, every time it is triggered, for as long as it exists — not once, like every other write in this server. Revoking the key does not stop it; disabling or deleting the rule does.
- Pause before you delete.
update_automationwithenabled: falsestops a misbehaving rule at once and keeps its history; deleting destroys the history along with the rule.conditionsandactionsreplace the stored lists rather than merging into them, so build them fromget_automationand not from memory.
Both writes take the rule's version and answer 409 with the current one, the
same contract ticket writes have.
Published links
list_published_links is the inventory read: what of this workspace's is on the
public internet right now, each row with its share token, who published it, when,
and how many strangers have looked. live is what a stranger actually gets and
dark_reason says why a row is not, which is the only way to find a published
page that is currently archived and would go straight back online if somebody
restored it. Workspace owner or admin only; an ordinary member is a 403. Taking
a link back down is deliberately not a tool — report what is published and let a
person decide what comes off.
Laver MCP 질문
Laver MCP를 Claude Code에 추가하려면 어떻게 하나요?
터미널에서 claude mcp add --env LAVER_API_KEY=<YOUR_LAVER_API_KEY> laver-mcp -- npx -y @laver/mcp를 실행합니다. 모든 프로젝트에서 사용할 수 있게 하려면 --scope user를 추가합니다.
Laver MCP를 Cursor에 추가하려면 어떻게 하나요?
~/.cursor/mcp.json를 열고 mcpServers 아래에 서버를 추가합니다. 위 설치 섹션에 표시된 Cursor 구성을 사용합니다.
Laver MCP는 어떤 도구를 제공하나요?
49개 도구를 제공합니다. 예: list_workspaces, list_boards, get_board, list_tickets, list_workspace_tickets.
서버에서 로컬 캐시를 유지하나요?
아니요, 모든 도구는 공개 REST API를 직접 호출하므로 로컬 상태나 캐시를 유지하지 않습니다.