@nocodelab/kadailog-mcp 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +89 -2
- package/dist/index.js +358 -0
- package/package.json +37 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 deed LLC (ノーコードラボ)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,90 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @nocodelab/kadailog-mcp
|
|
2
|
+
|
|
3
|
+
KadaiLog の公式 MCP サーバ。Claude Code / Codex などの AI エージェントから
|
|
4
|
+
KadaiLog の課題を読み書きするためのものです。
|
|
5
|
+
|
|
6
|
+
中身は KadaiLog の外部API(`/api/v1`)を叩くだけの薄いラッパーで、権限・入力検証・
|
|
7
|
+
変更履歴・通知はすべて KadaiLog 本体が担当します。
|
|
8
|
+
|
|
9
|
+
## 準備
|
|
10
|
+
|
|
11
|
+
KadaiLog にサインインし、**個人設定 > APIキー** でキーを発行します
|
|
12
|
+
(`tslg_…` で始まる文字列。表示されるのは発行直後の1回だけです)。
|
|
13
|
+
|
|
14
|
+
キーは**発行した本人の権限で動きます**。参加していないプロジェクトは、
|
|
15
|
+
このMCPサーバからも見えません。
|
|
16
|
+
|
|
17
|
+
## 設定
|
|
18
|
+
|
|
19
|
+
Claude Code の場合、`.mcp.json` か `~/.claude.json` に追加します。
|
|
20
|
+
|
|
21
|
+
```json
|
|
22
|
+
{
|
|
23
|
+
"mcpServers": {
|
|
24
|
+
"kadailog": {
|
|
25
|
+
"command": "npx",
|
|
26
|
+
"args": ["-y", "@nocodelab/kadailog-mcp"],
|
|
27
|
+
"env": {
|
|
28
|
+
"KADAILOG_API_KEY": "tslg_xxxxxxxxxxxx"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
| 環境変数 | 必須 | 説明 |
|
|
36
|
+
| --- | --- | --- |
|
|
37
|
+
| `KADAILOG_API_KEY` | ○ | 個人設定で発行したAPIキー |
|
|
38
|
+
| `KADAILOG_API_URL` | | 接続先の上書き。既定は `https://api.kadailog.com/v1` |
|
|
39
|
+
|
|
40
|
+
改名前の `TASKLOG_API_KEY` / `TASKLOG_API_URL` も当面は読みます(新しい名前が優先)。
|
|
41
|
+
|
|
42
|
+
## ツール
|
|
43
|
+
|
|
44
|
+
| ツール | できること |
|
|
45
|
+
| --- | --- |
|
|
46
|
+
| `get_myself` | 自分(APIキーの持ち主)の名前・権限とスペースの情報 |
|
|
47
|
+
| `list_my_issues` | 自分の担当課題(全プロジェクト・期限が近い順) |
|
|
48
|
+
| `list_notifications` | 自分宛ての通知(未読だけも可) |
|
|
49
|
+
| `mark_notifications_read` | 通知を既読にする(課題ごと/全部) |
|
|
50
|
+
| `list_projects` | 参加しているプロジェクトの一覧 |
|
|
51
|
+
| `get_project_meta` | そのプロジェクトのステータス・種別・カテゴリ・マイルストーン・メンバー一覧 |
|
|
52
|
+
| `list_issues` | 課題の絞り込み(担当者・ステータス・種別・マイルストーン・カテゴリ・更新日時・期限順など) |
|
|
53
|
+
| `search_issues` | 件名・本文のキーワード検索(スペース全体・日本語可) |
|
|
54
|
+
| `get_issue` | 課題1件の詳細(親子・関連課題・ウォッチ中かを含む) |
|
|
55
|
+
| `get_issue_history` | 課題の変更履歴(だれが・いつ・何を変えたか) |
|
|
56
|
+
| `list_recent_activities` | 最近の更新(課題の作成・更新・コメント)。「昨日から何が変わった?」用 |
|
|
57
|
+
| `create_issue` | 課題の作成 |
|
|
58
|
+
| `update_issue` | 課題の更新(ステータス変更・担当変更・期限設定など) |
|
|
59
|
+
| `list_comments` | コメント一覧 |
|
|
60
|
+
| `add_comment` | コメント追加 |
|
|
61
|
+
| `list_wiki_pages` | Wikiページの一覧・キーワード検索 |
|
|
62
|
+
| `get_wiki_page` | Wikiページの本文 |
|
|
63
|
+
| `create_wiki_page` | Wikiページの作成 |
|
|
64
|
+
| `update_wiki_page` | Wikiページのタイトル・本文の更新 |
|
|
65
|
+
|
|
66
|
+
課題は課題キー(`RENEW-12`)、プロジェクトはプロジェクトキー(`RENEW`)、Wikiページは
|
|
67
|
+
プロジェクトキー+タイトルで指定します。
|
|
68
|
+
ステータス・種別などはプロジェクトごとに違うため、名前で指定し、間違えると**候補付きの
|
|
69
|
+
エラー**が返ります(エージェントはそれを見て言い直せます)。
|
|
70
|
+
|
|
71
|
+
一覧系(`list_issues` / `list_my_issues` / `list_notifications` / `list_recent_activities` /
|
|
72
|
+
`list_wiki_pages`)は、結果の `nextCursor` を次の呼び出しの `cursor` に渡すと続きを取れます
|
|
73
|
+
(`null` なら続きなし)。
|
|
74
|
+
|
|
75
|
+
## 注意
|
|
76
|
+
|
|
77
|
+
- 画面から操作したときと同じように、**変更履歴に残り、関係者に通知が飛びます**
|
|
78
|
+
(変更履歴には API 経由であることも記録されます)
|
|
79
|
+
- 添付ファイルには対応していません。Wiki の削除もできません(画面から行ってください)
|
|
80
|
+
- レート制限は 1キーあたり 60リクエスト/分
|
|
81
|
+
|
|
82
|
+
## 開発
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
npm install
|
|
86
|
+
npm run build
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
`KADAILOG_API_URL` に開発環境の URL を渡せば、そちらに向けて動作確認できます。
|
|
2
90
|
|
|
3
|
-
This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { createRequire } from "node:module";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
const DEFAULT_API_URL = "https://api.kadailog.com/v1";
|
|
7
|
+
const API_URL = (process.env.KADAILOG_API_URL ||
|
|
8
|
+
process.env.TASKLOG_API_URL ||
|
|
9
|
+
DEFAULT_API_URL).replace(/\/+$/, "");
|
|
10
|
+
const API_KEY = process.env.KADAILOG_API_KEY || process.env.TASKLOG_API_KEY;
|
|
11
|
+
if (!API_KEY) {
|
|
12
|
+
console.error("KADAILOG_API_KEY が設定されていません。KadaiLog の「個人設定 > APIキー」で発行し、" +
|
|
13
|
+
"MCPサーバの env に設定してください。");
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
function ok(data) {
|
|
17
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
18
|
+
}
|
|
19
|
+
function fail(message) {
|
|
20
|
+
return { content: [{ type: "text", text: message }], isError: true };
|
|
21
|
+
}
|
|
22
|
+
async function request(method, path, options = {}) {
|
|
23
|
+
const url = new URL(API_URL + path);
|
|
24
|
+
for (const [key, value] of Object.entries(options.query ?? {})) {
|
|
25
|
+
if (value !== undefined && value !== null && value !== "") {
|
|
26
|
+
url.searchParams.set(key, String(value));
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
let response;
|
|
30
|
+
try {
|
|
31
|
+
response = await fetch(url, {
|
|
32
|
+
method,
|
|
33
|
+
headers: {
|
|
34
|
+
authorization: `Bearer ${API_KEY}`,
|
|
35
|
+
...(options.body === undefined
|
|
36
|
+
? {}
|
|
37
|
+
: { "content-type": "application/json" }),
|
|
38
|
+
},
|
|
39
|
+
body: options.body === undefined ? undefined : JSON.stringify(options.body),
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
return fail(`KadaiLog に接続できませんでした (${API_URL}): ${err instanceof Error ? err.message : String(err)}`);
|
|
44
|
+
}
|
|
45
|
+
const text = await response.text();
|
|
46
|
+
let parsed;
|
|
47
|
+
try {
|
|
48
|
+
parsed = JSON.parse(text);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return fail(`KadaiLog から予期しない応答が返りました: ${text.slice(0, 300)}`);
|
|
52
|
+
}
|
|
53
|
+
if (!response.ok) {
|
|
54
|
+
const error = parsed
|
|
55
|
+
.error;
|
|
56
|
+
return fail(`${error?.message ?? "エラーが発生しました"} (${error?.code ?? response.status})`);
|
|
57
|
+
}
|
|
58
|
+
return ok(parsed);
|
|
59
|
+
}
|
|
60
|
+
const { version } = createRequire(import.meta.url)("../package.json");
|
|
61
|
+
const server = new McpServer({ name: "kadailog", version });
|
|
62
|
+
const issueKey = z
|
|
63
|
+
.string()
|
|
64
|
+
.describe("課題キー。例: RENEW-12(list_issues や search_issues の key)");
|
|
65
|
+
const projectKey = z
|
|
66
|
+
.string()
|
|
67
|
+
.describe("プロジェクトキー。例: RENEW(list_projects の key)");
|
|
68
|
+
const statusArg = z
|
|
69
|
+
.string()
|
|
70
|
+
.optional()
|
|
71
|
+
.describe("ステータスの名前。プロジェクト固有なので get_project_meta で確認すること");
|
|
72
|
+
const typeArg = z
|
|
73
|
+
.string()
|
|
74
|
+
.optional()
|
|
75
|
+
.describe("種別の名前。プロジェクト固有なので get_project_meta で確認すること");
|
|
76
|
+
const priorityArg = z
|
|
77
|
+
.enum(["high", "medium", "low"])
|
|
78
|
+
.optional()
|
|
79
|
+
.describe("優先度。省略時は medium");
|
|
80
|
+
const assigneeArg = z
|
|
81
|
+
.string()
|
|
82
|
+
.optional()
|
|
83
|
+
.describe("担当者。メールアドレスか表示名、自分自身なら \"me\"。update_issue で空文字を渡すと担当を外す");
|
|
84
|
+
const dueDateArg = z
|
|
85
|
+
.string()
|
|
86
|
+
.optional()
|
|
87
|
+
.describe("期限日 YYYY-MM-DD。update_issue で空文字を渡すと期限を外す");
|
|
88
|
+
const categoriesArg = z
|
|
89
|
+
.array(z.string())
|
|
90
|
+
.optional()
|
|
91
|
+
.describe("カテゴリ名の配列。指定した内容で置き換わる");
|
|
92
|
+
const milestonesArg = z
|
|
93
|
+
.array(z.string())
|
|
94
|
+
.optional()
|
|
95
|
+
.describe("マイルストーン名の配列。指定した内容で置き換わる");
|
|
96
|
+
const versionsArg = z
|
|
97
|
+
.array(z.string())
|
|
98
|
+
.optional()
|
|
99
|
+
.describe("発生バージョン名の配列。指定した内容で置き換わる。マスタはマイルストーンと共通" +
|
|
100
|
+
"(get_project_meta の milestones)。features.versions が ON のプロジェクトで使う");
|
|
101
|
+
const parentArg = z
|
|
102
|
+
.string()
|
|
103
|
+
.optional()
|
|
104
|
+
.describe("親課題の課題キー(例 RENEW-12)。同じプロジェクトの課題のみ・階層は1段まで" +
|
|
105
|
+
"(親を持つ課題は親になれない)。update_issue で空文字を渡すと親子関係を外す");
|
|
106
|
+
const limitArg = (fallback) => z.number().optional().describe(`最大件数(既定${fallback}・上限100)`);
|
|
107
|
+
const cursorArg = z
|
|
108
|
+
.string()
|
|
109
|
+
.optional()
|
|
110
|
+
.describe("続きを取るときに、前回の結果の nextCursor をそのまま渡す。nextCursor が null なら続きはない" +
|
|
111
|
+
"(0件でも nextCursor があれば、まだ続きがある)");
|
|
112
|
+
const wikiTitleArg = z
|
|
113
|
+
.string()
|
|
114
|
+
.describe("Wikiページのタイトル。「/」区切りは階層(例: 10. 概要/01. 用語集)。list_wiki_pages の title をそのまま渡す");
|
|
115
|
+
server.registerTool("get_myself", {
|
|
116
|
+
title: "自分の情報",
|
|
117
|
+
description: "このAPIキーの持ち主(自分)の表示名・メールアドレス・権限と、スペースの名前・URLを返す。" +
|
|
118
|
+
"「自分の〜」を扱うときの起点。",
|
|
119
|
+
inputSchema: {},
|
|
120
|
+
}, async () => await request("GET", "/me"));
|
|
121
|
+
server.registerTool("list_projects", {
|
|
122
|
+
title: "プロジェクト一覧",
|
|
123
|
+
description: "参加しているプロジェクトの一覧を返す。他のツールに渡すプロジェクトキーはここで調べる。",
|
|
124
|
+
inputSchema: {},
|
|
125
|
+
}, async () => await request("GET", "/projects"));
|
|
126
|
+
server.registerTool("get_project_meta", {
|
|
127
|
+
title: "プロジェクトの設定値",
|
|
128
|
+
description: "プロジェクトで使えるステータス・種別・カテゴリ・マイルストーン・メンバーの一覧を返す。" +
|
|
129
|
+
"ステータスや種別はプロジェクトごとに違うので、create_issue / update_issue の前にこれで確認すること。" +
|
|
130
|
+
"どのステータスが「完了」扱いかも isDone で分かる。",
|
|
131
|
+
inputSchema: { project: projectKey },
|
|
132
|
+
}, async ({ project }) => await request("GET", `/projects/${encodeURIComponent(project)}/meta`));
|
|
133
|
+
server.registerTool("list_issues", {
|
|
134
|
+
title: "課題の一覧・絞り込み",
|
|
135
|
+
description: "課題を条件で絞り込んで返す。project を指定するのが基本。" +
|
|
136
|
+
"project を省略できるのは assignee を指定し state が open か closed のときだけ" +
|
|
137
|
+
"(プロジェクト横断で「自分の未完了課題」を見る用途)。" +
|
|
138
|
+
"キーワードで探したいだけなら search_issues の方が適している。",
|
|
139
|
+
inputSchema: {
|
|
140
|
+
project: projectKey.optional(),
|
|
141
|
+
assignee: z
|
|
142
|
+
.string()
|
|
143
|
+
.optional()
|
|
144
|
+
.describe("担当者で絞る。メールアドレス・表示名・\"me\"、未割り当ては \"none\""),
|
|
145
|
+
state: z
|
|
146
|
+
.enum(["open", "closed", "all"])
|
|
147
|
+
.optional()
|
|
148
|
+
.describe("未完了 open(既定) / 完了 closed / 全部 all"),
|
|
149
|
+
status: statusArg,
|
|
150
|
+
type: typeArg,
|
|
151
|
+
milestone: z.string().optional().describe("マイルストーン名で絞る"),
|
|
152
|
+
version: z.string().optional().describe("発生バージョン名で絞る"),
|
|
153
|
+
category: z.string().optional().describe("カテゴリ名で絞る"),
|
|
154
|
+
q: z
|
|
155
|
+
.string()
|
|
156
|
+
.optional()
|
|
157
|
+
.describe("件名・本文の部分一致で絞る(単純な部分一致。全文検索は search_issues)"),
|
|
158
|
+
sort: z
|
|
159
|
+
.enum(["updated", "due", "key"])
|
|
160
|
+
.optional()
|
|
161
|
+
.describe("並び順。updated=更新新しい順(既定) / due=期限近い順 / key=キー順。" +
|
|
162
|
+
"cursor で全件をたどるときは、途中の更新で順番がずれない key を使う"),
|
|
163
|
+
updatedSince: z
|
|
164
|
+
.string()
|
|
165
|
+
.optional()
|
|
166
|
+
.describe("この日時以降に更新された課題だけ(ISO 8601。例 2026-10-11T09:00:00+09:00。時差を省くと日本時間)"),
|
|
167
|
+
limit: limitArg(50),
|
|
168
|
+
cursor: cursorArg,
|
|
169
|
+
},
|
|
170
|
+
}, async (args) => await request("GET", "/issues", { query: args }));
|
|
171
|
+
server.registerTool("list_my_issues", {
|
|
172
|
+
title: "自分の担当課題",
|
|
173
|
+
description: "自分が担当の課題を、参加している全プロジェクトから期限が近い順に返す(期限なしは後ろ)。" +
|
|
174
|
+
"「今日やること」「自分のタスク」を聞かれたらまずこれ。",
|
|
175
|
+
inputSchema: {
|
|
176
|
+
state: z
|
|
177
|
+
.enum(["open", "closed"])
|
|
178
|
+
.optional()
|
|
179
|
+
.describe("未完了 open(既定) / 完了 closed"),
|
|
180
|
+
sort: z
|
|
181
|
+
.enum(["due", "updated", "key"])
|
|
182
|
+
.optional()
|
|
183
|
+
.describe("並び順。due=期限近い順(既定) / updated=更新新しい順 / key=キー順。" +
|
|
184
|
+
"cursor で全件をたどるときは、途中の更新で順番がずれない key を使う"),
|
|
185
|
+
limit: limitArg(50),
|
|
186
|
+
cursor: cursorArg,
|
|
187
|
+
},
|
|
188
|
+
}, async ({ state, sort, limit, cursor }) => await request("GET", "/issues", {
|
|
189
|
+
query: {
|
|
190
|
+
assignee: "me",
|
|
191
|
+
state: state ?? "open",
|
|
192
|
+
sort: sort ?? "due",
|
|
193
|
+
limit,
|
|
194
|
+
cursor,
|
|
195
|
+
},
|
|
196
|
+
}));
|
|
197
|
+
server.registerTool("search_issues", {
|
|
198
|
+
title: "課題の全文検索",
|
|
199
|
+
description: "スペース全体の課題を件名・本文のキーワードで検索する。日本語もそのまま渡してよい(2文字以上)。" +
|
|
200
|
+
"プロジェクトやステータスが分かっているなら list_issues の方が確実。",
|
|
201
|
+
inputSchema: {
|
|
202
|
+
q: z.string().describe("検索キーワード(2文字以上)"),
|
|
203
|
+
limit: z.number().optional().describe("最大件数(既定20・上限100)"),
|
|
204
|
+
},
|
|
205
|
+
}, async ({ q, limit }) => await request("GET", "/search", { query: { q, limit } }));
|
|
206
|
+
server.registerTool("get_issue", {
|
|
207
|
+
title: "課題の詳細",
|
|
208
|
+
description: "課題キーを指定して1件の詳細を返す。親課題(parent)・子課題の一覧(children)・関連課題の一覧(related)・自分がウォッチ中か(watching)も含まれる。",
|
|
209
|
+
inputSchema: { key: issueKey },
|
|
210
|
+
}, async ({ key }) => await request("GET", `/issues/${encodeURIComponent(key)}`));
|
|
211
|
+
server.registerTool("create_issue", {
|
|
212
|
+
title: "課題を作成",
|
|
213
|
+
description: "課題を新規作成する。status / type / categories / milestones はプロジェクト固有の名前なので、" +
|
|
214
|
+
"自信がなければ先に get_project_meta を呼ぶこと。省略時は先頭のステータス・種別・優先度 medium になる。" +
|
|
215
|
+
"担当者を指定すると、その人に通知とメールが飛ぶ。",
|
|
216
|
+
inputSchema: {
|
|
217
|
+
project: projectKey,
|
|
218
|
+
title: z.string().describe("件名"),
|
|
219
|
+
description: z.string().optional().describe("本文(Markdown)"),
|
|
220
|
+
status: statusArg,
|
|
221
|
+
type: typeArg,
|
|
222
|
+
priority: priorityArg,
|
|
223
|
+
assignee: assigneeArg,
|
|
224
|
+
dueDate: dueDateArg,
|
|
225
|
+
categories: categoriesArg,
|
|
226
|
+
milestones: milestonesArg,
|
|
227
|
+
versions: versionsArg,
|
|
228
|
+
parent: parentArg,
|
|
229
|
+
},
|
|
230
|
+
}, async (args) => await request("POST", "/issues", { body: args }));
|
|
231
|
+
server.registerTool("update_issue", {
|
|
232
|
+
title: "課題を更新",
|
|
233
|
+
description: "課題の項目を変更する。渡した項目だけが変わり、省略した項目はそのまま。" +
|
|
234
|
+
"課題を閉じるには status に「完了」扱いのステータス名を渡す(どれが完了扱いかは get_project_meta の isDone)。" +
|
|
235
|
+
"担当・期限・親課題を外したいときは空文字 \"\" を渡す。変更は課題の変更履歴に残り、関係者に通知される。",
|
|
236
|
+
inputSchema: {
|
|
237
|
+
key: issueKey,
|
|
238
|
+
title: z.string().optional().describe("件名"),
|
|
239
|
+
description: z.string().optional().describe("本文(Markdown)。全文が置き換わる"),
|
|
240
|
+
status: statusArg,
|
|
241
|
+
type: typeArg,
|
|
242
|
+
priority: priorityArg,
|
|
243
|
+
assignee: assigneeArg,
|
|
244
|
+
dueDate: dueDateArg,
|
|
245
|
+
categories: categoriesArg,
|
|
246
|
+
milestones: milestonesArg,
|
|
247
|
+
versions: versionsArg,
|
|
248
|
+
parent: parentArg,
|
|
249
|
+
},
|
|
250
|
+
}, async ({ key, ...body }) => await request("PATCH", `/issues/${encodeURIComponent(key)}`, { body }));
|
|
251
|
+
server.registerTool("get_issue_history", {
|
|
252
|
+
title: "課題の変更履歴",
|
|
253
|
+
description: "課題の変更履歴(だれが・いつ・何を変えたか)を古い順に返す。ステータス・担当者・期限日などの変更が分かる。" +
|
|
254
|
+
"via は app=画面から / api=外部API・MCP から。コメントは含まないので list_comments を使う。",
|
|
255
|
+
inputSchema: { key: issueKey },
|
|
256
|
+
}, async ({ key }) => await request("GET", `/issues/${encodeURIComponent(key)}/history`));
|
|
257
|
+
server.registerTool("list_recent_activities", {
|
|
258
|
+
title: "最近の更新",
|
|
259
|
+
description: "スペース(または指定プロジェクト)の最近の更新を新しい順に返す。課題の作成・更新(変更内容つき)とコメント追加。" +
|
|
260
|
+
"「昨日から何が変わった?」は since に昨日の日時を渡す。コメントは先頭300文字まで(全文は list_comments)。" +
|
|
261
|
+
"Backlog から移行したコメントは含まない。" +
|
|
262
|
+
"同じ人が10分以内に続けて変えた内容は1件にまとまり、時刻は最初の変更のもの" +
|
|
263
|
+
"(短い間隔で since を進めながら見張ると取りこぼすことがあるので、見張る用途は少し前から重ねて取る)。",
|
|
264
|
+
inputSchema: {
|
|
265
|
+
project: projectKey.optional().describe("プロジェクトキーで絞る(省略=参加している全プロジェクト)"),
|
|
266
|
+
since: z
|
|
267
|
+
.string()
|
|
268
|
+
.optional()
|
|
269
|
+
.describe("この日時より後の更新だけ(ISO 8601。例 2026-10-10T00:00:00+09:00。時差を省くと日本時間)"),
|
|
270
|
+
limit: limitArg(30),
|
|
271
|
+
cursor: cursorArg,
|
|
272
|
+
},
|
|
273
|
+
}, async (args) => await request("GET", "/activities", { query: args }));
|
|
274
|
+
server.registerTool("list_notifications", {
|
|
275
|
+
title: "自分宛ての通知",
|
|
276
|
+
description: "自分宛ての通知(メンション・担当に設定・ウォッチ中の課題のコメントなど)を新しい順に返す。" +
|
|
277
|
+
"確認し終えたら mark_notifications_read で既読にできる。",
|
|
278
|
+
inputSchema: {
|
|
279
|
+
unread: z.boolean().optional().describe("true=未読だけ"),
|
|
280
|
+
limit: limitArg(20),
|
|
281
|
+
cursor: cursorArg,
|
|
282
|
+
},
|
|
283
|
+
}, async (args) => await request("GET", "/notifications", { query: args }));
|
|
284
|
+
server.registerTool("mark_notifications_read", {
|
|
285
|
+
title: "通知を既読にする",
|
|
286
|
+
description: "通知を既読にする。issue に課題キーを渡すとその課題の通知だけ、all=true で全部。" +
|
|
287
|
+
"画面の未読表示にも反映されるので、利用者が確認したと言ったとき・確認を頼まれたときに使う。",
|
|
288
|
+
inputSchema: {
|
|
289
|
+
issue: issueKey.optional(),
|
|
290
|
+
all: z.boolean().optional().describe("true=すべての未読通知を既読にする"),
|
|
291
|
+
},
|
|
292
|
+
}, async (body) => await request("POST", "/notifications/read", { body }));
|
|
293
|
+
server.registerTool("list_wiki_pages", {
|
|
294
|
+
title: "Wikiページの一覧",
|
|
295
|
+
description: "プロジェクトのWikiページの一覧(タイトル順・本文なし)を返す。タイトルの「/」区切りは階層。" +
|
|
296
|
+
"q でタイトル・本文の部分一致検索もできる。本文は get_wiki_page で読む。",
|
|
297
|
+
inputSchema: {
|
|
298
|
+
project: projectKey,
|
|
299
|
+
q: z.string().optional().describe("タイトル・本文の部分一致で絞る"),
|
|
300
|
+
limit: limitArg(50),
|
|
301
|
+
cursor: cursorArg,
|
|
302
|
+
},
|
|
303
|
+
}, async ({ project, ...query }) => await request("GET", `/projects/${encodeURIComponent(project)}/wiki`, { query }));
|
|
304
|
+
server.registerTool("get_wiki_page", {
|
|
305
|
+
title: "Wikiページを読む",
|
|
306
|
+
description: "Wikiページの本文(Markdown)を返す。ページはプロジェクトキーとタイトルで指定する。",
|
|
307
|
+
inputSchema: { project: projectKey, title: wikiTitleArg },
|
|
308
|
+
}, async ({ project, title }) => await request("GET", `/projects/${encodeURIComponent(project)}/wiki/page`, {
|
|
309
|
+
query: { title },
|
|
310
|
+
}));
|
|
311
|
+
server.registerTool("create_wiki_page", {
|
|
312
|
+
title: "Wikiページを作成",
|
|
313
|
+
description: "Wikiページを新しく作る。同じタイトルのページがあるとエラーになる(そのときは update_wiki_page)。" +
|
|
314
|
+
"階層の下に作るときはタイトルを「親/子」の形にする。",
|
|
315
|
+
inputSchema: {
|
|
316
|
+
project: projectKey,
|
|
317
|
+
title: z.string().describe("タイトル。「/」区切りで階層になる"),
|
|
318
|
+
body: z.string().optional().describe("本文(Markdown)"),
|
|
319
|
+
},
|
|
320
|
+
}, async ({ project, title, body }) => await request("POST", `/projects/${encodeURIComponent(project)}/wiki`, {
|
|
321
|
+
body: { title, body },
|
|
322
|
+
}));
|
|
323
|
+
server.registerTool("update_wiki_page", {
|
|
324
|
+
title: "Wikiページを更新",
|
|
325
|
+
description: "Wikiページのタイトルや本文を変える。body を渡すと本文の全文が置き換わる" +
|
|
326
|
+
"(追記したいときは先に get_wiki_page で読み、全文を組み立てて渡す)。版の管理はないので、" +
|
|
327
|
+
"読んだときの updatedAt を expectedUpdatedAt に渡すこと(その後にだれかが更新していたら止まる)。",
|
|
328
|
+
inputSchema: {
|
|
329
|
+
project: projectKey,
|
|
330
|
+
title: wikiTitleArg,
|
|
331
|
+
newTitle: z.string().optional().describe("新しいタイトル(変えるときだけ)"),
|
|
332
|
+
body: z.string().optional().describe("新しい本文(Markdown)。全文が置き換わる"),
|
|
333
|
+
expectedUpdatedAt: z
|
|
334
|
+
.string()
|
|
335
|
+
.optional()
|
|
336
|
+
.describe("get_wiki_page で読んだときの updatedAt。食い違えば更新せずエラーになる"),
|
|
337
|
+
},
|
|
338
|
+
}, async ({ project, title, newTitle, body, expectedUpdatedAt }) => await request("PATCH", `/projects/${encodeURIComponent(project)}/wiki/page`, {
|
|
339
|
+
query: { title },
|
|
340
|
+
body: { title: newTitle, body, expectedUpdatedAt },
|
|
341
|
+
}));
|
|
342
|
+
server.registerTool("list_comments", {
|
|
343
|
+
title: "コメント一覧",
|
|
344
|
+
description: "課題に付いたコメントを古い順に返す。",
|
|
345
|
+
inputSchema: { key: issueKey },
|
|
346
|
+
}, async ({ key }) => await request("GET", `/issues/${encodeURIComponent(key)}/comments`));
|
|
347
|
+
server.registerTool("add_comment", {
|
|
348
|
+
title: "コメントを追加",
|
|
349
|
+
description: "課題にコメントを追加する。本文で @表示名 と書くとその人にメンション通知が飛ぶ。",
|
|
350
|
+
inputSchema: {
|
|
351
|
+
key: issueKey,
|
|
352
|
+
body: z.string().describe("コメント本文(Markdown)"),
|
|
353
|
+
},
|
|
354
|
+
}, async ({ key, body }) => await request("POST", `/issues/${encodeURIComponent(key)}/comments`, {
|
|
355
|
+
body: { body },
|
|
356
|
+
}));
|
|
357
|
+
const transport = new StdioServerTransport();
|
|
358
|
+
await server.connect(transport);
|
package/package.json
CHANGED
|
@@ -1,6 +1,39 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nocodelab/kadailog-mcp",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "KadaiLog の公式MCPサーバ。Claude Code などのAIエージェントから課題を操作する",
|
|
5
|
+
"homepage": "https://docs.kadailog.com/advanced/ai-agents/",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"mcp",
|
|
8
|
+
"kadailog",
|
|
9
|
+
"model-context-protocol",
|
|
10
|
+
"claude",
|
|
11
|
+
"task-management"
|
|
12
|
+
],
|
|
13
|
+
"type": "module",
|
|
14
|
+
"bin": {
|
|
15
|
+
"kadailog-mcp": "dist/index.js"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist"
|
|
19
|
+
],
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": ">=20"
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "tsc",
|
|
25
|
+
"prepublishOnly": "npm run build"
|
|
26
|
+
},
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
30
|
+
"zod": "^4.4.3"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/node": "^26.2.0",
|
|
34
|
+
"typescript": "^5.9.3"
|
|
35
|
+
},
|
|
36
|
+
"publishConfig": {
|
|
37
|
+
"access": "public"
|
|
38
|
+
}
|
|
39
|
+
}
|