cc-jandi 1.0.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 +658 -0
- package/dist/index.js +7 -0
- package/dist/services/IncomingWebhookService.js +75 -0
- package/dist/services/TeamIncomingWebhookService.js +97 -0
- package/dist/services/base/BaseWebhookService.js +90 -0
- package/dist/services/configService.js +125 -0
- package/dist/services/index.js +4 -0
- package/dist/tools/GenerateWebhookScriptTool.js +232 -0
- package/dist/tools/incoming/SendMessageTool.js +48 -0
- package/dist/tools/incoming/SendRichMessageTool.js +74 -0
- package/dist/tools/incoming/TestWebhookTool.js +95 -0
- package/dist/tools/incoming/ValidateTokenTool.js +54 -0
- package/dist/tools/outgoing/GenerateOutgoingHandlerTool.js +217 -0
- package/dist/tools/outgoing/SimulateOutgoingPayloadTool.js +103 -0
- package/dist/tools/outgoing/ValidateOutgoingResponseTool.js +96 -0
- package/dist/tools/team-incoming/SendTeamMessageTool.js +57 -0
- package/dist/tools/team-incoming/SendTeamRichMessageTool.js +83 -0
- package/dist/tools/team-incoming/ValidateTeamTokenTool.js +56 -0
- package/dist/types/common.js +19 -0
- package/dist/types/incoming.js +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/jandi.js +3 -0
- package/dist/types/outgoing.js +1 -0
- package/dist/types/team-incoming.js +1 -0
- package/dist/utils/index.js +3 -0
- package/dist/utils/resolveTeamToken.js +23 -0
- package/dist/utils/resolveToken.js +30 -0
- package/dist/utils/validateColor.js +9 -0
- package/package.json +61 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 kwag93
|
|
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
ADDED
|
@@ -0,0 +1,658 @@
|
|
|
1
|
+
# cc-jandi
|
|
2
|
+
|
|
3
|
+
[](https://badge.fury.io/js/cc-jandi)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
잔디(Jandi) 웹훅을 위한 **MCP 서버 & Claude Code 플러그인**입니다.
|
|
7
|
+
Claude Desktop, Cursor, VS Code 등 MCP 클라이언트에서 **"잔디에 메시지 보내줘"** 한마디면 동작하고, Claude Code 플러그인으로 설치하면 `/cc-jandi:notify`, `/cc-jandi:alert` 같은 Skills과 Agents까지 사용할 수 있습니다.
|
|
8
|
+
|
|
9
|
+
## 지원하는 웹훅 타입
|
|
10
|
+
|
|
11
|
+
| 타입 | 방향 | 용도 |
|
|
12
|
+
|------|------|------|
|
|
13
|
+
| **Incoming Webhook** | 서버 → 잔디 채널 | 채널에 메시지 보내기 |
|
|
14
|
+
| **Team Incoming Webhook** | 서버 → 특정 사용자 | 이메일로 지정한 사람에게 개인 메시지 보내기 |
|
|
15
|
+
| **Outgoing Webhook** | 잔디 → 외부 서버 | 잔디에서 키워드 입력 시 외부 서버 호출 |
|
|
16
|
+
| **Team Outgoing Webhook** | 잔디 → 외부 서버 | 위와 동일, 작성자 상세 정보(이메일, 전화번호) 포함 |
|
|
17
|
+
|
|
18
|
+
> Outgoing / Team Outgoing Webhook 도구는 핸들러 서버 개발을 돕는 헬퍼입니다. 실제 메시지 수신은 별도 서버가 필요합니다.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## 빠른 시작
|
|
23
|
+
|
|
24
|
+
### 1단계: 잔디에서 웹훅 토큰 발급
|
|
25
|
+
|
|
26
|
+
1. 잔디 앱에서 메시지를 보낼 토픽으로 이동
|
|
27
|
+
2. 토픽 상단의 **플러그 아이콘** 클릭 → **인커밍 웹훅** 선택
|
|
28
|
+
3. 웹훅 이름 입력 후 **커넥트 추가**
|
|
29
|
+
4. 생성된 URL에서 토큰 부분 복사
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
https://wh.jandi.com/connect-api/webhook/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
|
33
|
+
└──────── 이 부분이 토큰 ────────┘
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### 2단계: MCP 클라이언트에 등록
|
|
37
|
+
|
|
38
|
+
**Claude Desktop** — `claude_desktop_config.json` 파일을 열어 아래 내용을 추가합니다.
|
|
39
|
+
|
|
40
|
+
macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
41
|
+
Windows: `%APPDATA%\Claude\claude_desktop_config.json`
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
{
|
|
45
|
+
"mcpServers": {
|
|
46
|
+
"cc-jandi": {
|
|
47
|
+
"command": "npx",
|
|
48
|
+
"args": ["cc-jandi"],
|
|
49
|
+
"env": {
|
|
50
|
+
"JANDI_TOKEN": "발급받은_토큰"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Claude Code 플러그인으로 설치
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
/plugin install cc-jandi
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### 3단계: 사용
|
|
64
|
+
|
|
65
|
+
Claude Desktop을 재시작하고 대화창에 입력하세요.
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
잔디에 "배포 완료!" 메시지 보내줘
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
별도 설치 과정 없이 `npx`가 알아서 처리합니다.
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Skills
|
|
76
|
+
|
|
77
|
+
Claude Code 플러그인에서 사용할 수 있는 슬래시 커맨드입니다.
|
|
78
|
+
|
|
79
|
+
| 커맨드 | 설명 |
|
|
80
|
+
|--------|------|
|
|
81
|
+
| `/cc-jandi:notify` | 빠른 채널 알림 |
|
|
82
|
+
| `/cc-jandi:alert` | 심각도별 알림 (info/success/warning/error) |
|
|
83
|
+
| `/cc-jandi:deploy-notify` | 배포 결과 알림 (git 정보 자동 포함) |
|
|
84
|
+
| `/cc-jandi:daily-report` | 일일 작업 리포트 |
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Agents
|
|
89
|
+
|
|
90
|
+
Claude Code 플러그인에서 사용할 수 있는 에이전트입니다.
|
|
91
|
+
|
|
92
|
+
| 에이전트 | 설명 |
|
|
93
|
+
|----------|------|
|
|
94
|
+
| `notification-composer` | 리치 메시지 작성 도우미 |
|
|
95
|
+
| `webhook-debugger` | 웹훅 연결 진단 |
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## 사용 예시
|
|
100
|
+
|
|
101
|
+
| 하고 싶은 것 | 이렇게 말하면 됩니다 |
|
|
102
|
+
|-------------|---------------------|
|
|
103
|
+
| 기본 메시지 | "잔디에 빌드 완료 메시지 보내줘" |
|
|
104
|
+
| 색상 + 상세정보 | "잔디에 빨간색으로 서버 경고 보내줘. 제목은 CPU, 설명은 80% 초과" |
|
|
105
|
+
| 특정 채널 | "dev 채널 잔디에 테스트 결과 보내줘" (토큰 별칭 사용) |
|
|
106
|
+
| 개인 메시지 | "hong@company.com 에게 잔디 메시지 보내줘" (팀 웹훅 사용) |
|
|
107
|
+
| 토큰 확인 | "잔디 토큰 유효한지 확인해줘" |
|
|
108
|
+
| 스크립트 생성 | "Python으로 잔디 메시지 보내는 스크립트 만들어줘" |
|
|
109
|
+
| 핸들러 생성 | "Express로 잔디 아웃고잉 웹훅 핸들러 만들어줘" |
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## 도구 목록
|
|
114
|
+
|
|
115
|
+
### 채널 메시지 (Incoming Webhook)
|
|
116
|
+
|
|
117
|
+
| 도구명 | 설명 |
|
|
118
|
+
|--------|------|
|
|
119
|
+
| `send_message` | 채널에 텍스트 메시지 전송 |
|
|
120
|
+
| `send_rich_message` | 색상, 제목, 설명, 이미지가 포함된 리치 메시지 전송 |
|
|
121
|
+
| `validate_token` | 웹훅 토큰이 유효한지 확인 |
|
|
122
|
+
| `test_webhook` | 웹훅 연결 상태 종합 테스트 |
|
|
123
|
+
|
|
124
|
+
### 개인 메시지 (Team Incoming Webhook)
|
|
125
|
+
|
|
126
|
+
| 도구명 | 설명 |
|
|
127
|
+
|--------|------|
|
|
128
|
+
| `send_team_message` | 이메일로 지정한 사용자에게 메시지 전송 (최대 100명) |
|
|
129
|
+
| `send_team_rich_message` | 특정 사용자에게 리치 메시지 전송 |
|
|
130
|
+
| `validate_team_token` | 팀 웹훅 토큰 유효성 확인 |
|
|
131
|
+
|
|
132
|
+
### 아웃고잉 웹훅 개발 헬퍼 (Outgoing / Team Outgoing Webhook)
|
|
133
|
+
|
|
134
|
+
| 도구명 | 설명 |
|
|
135
|
+
|--------|------|
|
|
136
|
+
| `simulate_outgoing_payload` | 테스트용 페이로드 JSON 생성 (`outgoing` / `team-outgoing` 선택) |
|
|
137
|
+
| `generate_outgoing_handler` | 핸들러 서버 코드 생성 (Express, FastAPI, Flask) |
|
|
138
|
+
| `validate_outgoing_response` | 응답 포맷이 잔디 규격에 맞는지 검증 |
|
|
139
|
+
|
|
140
|
+
### 공통
|
|
141
|
+
|
|
142
|
+
| 도구명 | 설명 |
|
|
143
|
+
|--------|------|
|
|
144
|
+
| `generate_webhook_script` | Python / Node.js / curl / bash 스크립트 생성 (incoming, team-incoming) |
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## 토큰 설정
|
|
149
|
+
|
|
150
|
+
### 기본 — 토큰 1개
|
|
151
|
+
|
|
152
|
+
```json
|
|
153
|
+
{
|
|
154
|
+
"mcpServers": {
|
|
155
|
+
"cc-jandi": {
|
|
156
|
+
"command": "npx",
|
|
157
|
+
"args": ["cc-jandi"],
|
|
158
|
+
"env": {
|
|
159
|
+
"JANDI_TOKEN": "your_token"
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### 채널별 토큰 여러 개
|
|
167
|
+
|
|
168
|
+
별칭(alias)을 붙여서 여러 채널에 보낼 수 있습니다.
|
|
169
|
+
`JANDI_TOKEN_별칭` 형식으로 추가하면 도구에서 `tokenAlias`로 선택합니다.
|
|
170
|
+
|
|
171
|
+
```json
|
|
172
|
+
{
|
|
173
|
+
"mcpServers": {
|
|
174
|
+
"cc-jandi": {
|
|
175
|
+
"command": "npx",
|
|
176
|
+
"args": ["cc-jandi"],
|
|
177
|
+
"env": {
|
|
178
|
+
"JANDI_TOKEN": "기본_토큰",
|
|
179
|
+
"JANDI_TOKEN_DEV": "개발채널_토큰",
|
|
180
|
+
"JANDI_TOKEN_DEPLOY": "배포채널_토큰"
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### 팀 웹훅 — 개인 메시지
|
|
188
|
+
|
|
189
|
+
팀 웹훅은 `TEAM_ID`와 `TEAM_TOKEN`이 한 쌍입니다. 별칭이 같아야 매칭됩니다.
|
|
190
|
+
|
|
191
|
+
```json
|
|
192
|
+
{
|
|
193
|
+
"env": {
|
|
194
|
+
"JANDI_TEAM_ID_SALES": "팀_ID",
|
|
195
|
+
"JANDI_TEAM_TOKEN_SALES": "팀_토큰"
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### 아웃고잉 웹훅 검증 토큰
|
|
201
|
+
|
|
202
|
+
```json
|
|
203
|
+
{
|
|
204
|
+
"env": {
|
|
205
|
+
"JANDI_OUTGOING_TOKEN_DEPLOY": "검증_토큰"
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
<details>
|
|
211
|
+
<summary>전체 환경 변수 목록</summary>
|
|
212
|
+
|
|
213
|
+
| 환경 변수 | 용도 | 비고 |
|
|
214
|
+
|-----------|------|------|
|
|
215
|
+
| `JANDI_TOKEN` | 기본 Incoming 웹훅 토큰 | Incoming 사용 시 필수 |
|
|
216
|
+
| `JANDI_TOKEN_{별칭}` | 채널별 Incoming 토큰 | 선택 |
|
|
217
|
+
| `JANDI_URL_{별칭}` | 채널별 커스텀 URL | 선택, 기본: `wh.jandi.com` |
|
|
218
|
+
| `JANDI_TEAM_ID_{별칭}` | Team Incoming 팀 ID | 반드시 TOKEN과 쌍으로 |
|
|
219
|
+
| `JANDI_TEAM_TOKEN_{별칭}` | Team Incoming 토큰 | 반드시 ID와 쌍으로 |
|
|
220
|
+
| `JANDI_TEAM_URL_{별칭}` | Team Incoming 커스텀 URL | 선택 |
|
|
221
|
+
| `JANDI_OUTGOING_TOKEN_{별칭}` | Outgoing 검증 토큰 | 핸들러 개발 시 사용 |
|
|
222
|
+
|
|
223
|
+
별칭은 대소문자 구분 없음 (내부적으로 대문자 변환). 예: `JANDI_TOKEN_dev` = `JANDI_TOKEN_DEV`
|
|
224
|
+
|
|
225
|
+
</details>
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## 다른 MCP 클라이언트에서 사용
|
|
230
|
+
|
|
231
|
+
<details>
|
|
232
|
+
<summary>Claude Code (CLI)</summary>
|
|
233
|
+
|
|
234
|
+
프로젝트 루트에 `.mcp.json` 파일을 만듭니다.
|
|
235
|
+
|
|
236
|
+
```json
|
|
237
|
+
{
|
|
238
|
+
"mcpServers": {
|
|
239
|
+
"cc-jandi": {
|
|
240
|
+
"command": "npx",
|
|
241
|
+
"args": ["cc-jandi"],
|
|
242
|
+
"env": {
|
|
243
|
+
"JANDI_TOKEN": "your_token"
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
또는 CLI로 추가:
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
claude mcp add cc-jandi -- npx cc-jandi
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
</details>
|
|
257
|
+
|
|
258
|
+
<details>
|
|
259
|
+
<summary>Cursor</summary>
|
|
260
|
+
|
|
261
|
+
프로젝트 루트에 `.cursor/mcp.json` 파일을 만듭니다.
|
|
262
|
+
|
|
263
|
+
```json
|
|
264
|
+
{
|
|
265
|
+
"mcpServers": {
|
|
266
|
+
"cc-jandi": {
|
|
267
|
+
"command": "npx",
|
|
268
|
+
"args": ["cc-jandi"],
|
|
269
|
+
"env": {
|
|
270
|
+
"JANDI_TOKEN": "your_token"
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
</details>
|
|
278
|
+
|
|
279
|
+
<details>
|
|
280
|
+
<summary>VS Code (Copilot)</summary>
|
|
281
|
+
|
|
282
|
+
프로젝트 루트에 `.vscode/mcp.json` 파일을 만듭니다.
|
|
283
|
+
|
|
284
|
+
```json
|
|
285
|
+
{
|
|
286
|
+
"servers": {
|
|
287
|
+
"cc-jandi": {
|
|
288
|
+
"command": "npx",
|
|
289
|
+
"args": ["cc-jandi"],
|
|
290
|
+
"env": {
|
|
291
|
+
"JANDI_TOKEN": "your_token"
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
</details>
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
## CLI 사용법
|
|
303
|
+
|
|
304
|
+
`npx`로 직접 실행하거나, AI 에이전트가 자동으로 호출할 수 있습니다.
|
|
305
|
+
|
|
306
|
+
```bash
|
|
307
|
+
# 환경 변수와 함께 실행
|
|
308
|
+
JANDI_TOKEN=your_token npx cc-jandi
|
|
309
|
+
|
|
310
|
+
# 또는 .env 파일 사용
|
|
311
|
+
echo "JANDI_TOKEN=your_token" > .env
|
|
312
|
+
npx cc-jandi
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
**AI 에이전트 연동** — MCP 클라이언트가 stdio로 통신합니다:
|
|
316
|
+
|
|
317
|
+
```bash
|
|
318
|
+
# Claude Code에서 MCP 서버로 등록
|
|
319
|
+
claude mcp add cc-jandi -- npx cc-jandi
|
|
320
|
+
|
|
321
|
+
# 환경 변수 포함 등록
|
|
322
|
+
claude mcp add cc-jandi -e JANDI_TOKEN=your_token -- npx cc-jandi
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
**Claude Code 플러그인으로 설치** (Skills/Agents 포함):
|
|
326
|
+
|
|
327
|
+
```bash
|
|
328
|
+
# 마켓플레이스에서 설치
|
|
329
|
+
/plugin install cc-jandi
|
|
330
|
+
|
|
331
|
+
# 로컬 개발 시
|
|
332
|
+
claude --plugin-dir /path/to/cc-jandi
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
---
|
|
336
|
+
|
|
337
|
+
## 응답 형식
|
|
338
|
+
|
|
339
|
+
모든 도구는 `ToolResult` 형태로 응답합니다:
|
|
340
|
+
|
|
341
|
+
```json
|
|
342
|
+
{
|
|
343
|
+
"success": true,
|
|
344
|
+
"data": {
|
|
345
|
+
"message": "Message sent successfully to Jandi",
|
|
346
|
+
"tokenUsed": "default"
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
실패 시:
|
|
352
|
+
|
|
353
|
+
```json
|
|
354
|
+
{
|
|
355
|
+
"success": false,
|
|
356
|
+
"error": "Invalid webhook token format or inactive/deleted webhook",
|
|
357
|
+
"errorCode": 40000
|
|
358
|
+
}
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
---
|
|
362
|
+
|
|
363
|
+
## 제한사항
|
|
364
|
+
|
|
365
|
+
| 항목 | 제한 |
|
|
366
|
+
|------|------|
|
|
367
|
+
| 메시지 길이 | 최대 5,000자 |
|
|
368
|
+
| 요청 크기 | 최대 256KB |
|
|
369
|
+
| 분당 요청 | 60회 |
|
|
370
|
+
| 10분당 요청 | 500회 |
|
|
371
|
+
| 팀 웹훅 수신자 | 최대 100명 |
|
|
372
|
+
|
|
373
|
+
속도 제한(42900 에러)에 걸리면 서버가 자동으로 지수 백오프 재시도(최대 3회)를 수행합니다.
|
|
374
|
+
|
|
375
|
+
---
|
|
376
|
+
|
|
377
|
+
## 로컬 개발
|
|
378
|
+
|
|
379
|
+
```bash
|
|
380
|
+
git clone https://github.com/kwag93/cc-jandi.git
|
|
381
|
+
cd cc-jandi
|
|
382
|
+
npm install
|
|
383
|
+
cp .env.example .env # 토큰 입력
|
|
384
|
+
npm run build
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
개발 중 Claude Desktop에서 테스트하려면:
|
|
388
|
+
|
|
389
|
+
```json
|
|
390
|
+
{
|
|
391
|
+
"mcpServers": {
|
|
392
|
+
"cc-jandi": {
|
|
393
|
+
"command": "node",
|
|
394
|
+
"args": ["/절대/경로/cc-jandi/dist/index.js"]
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
| 명령어 | 설명 |
|
|
401
|
+
|--------|------|
|
|
402
|
+
| `npm run build` | TypeScript 컴파일 + mcp-build 패키징 |
|
|
403
|
+
| `npm run watch` | 파일 변경 시 자동 컴파일 |
|
|
404
|
+
| `npm run start` | 서버 직접 실행 |
|
|
405
|
+
|
|
406
|
+
---
|
|
407
|
+
|
|
408
|
+
<details>
|
|
409
|
+
<summary><strong>에이전트용: 자동 설정 및 개발 가이드</strong></summary>
|
|
410
|
+
|
|
411
|
+
> 이 섹션은 AI 에이전트가 이 프로젝트를 자동으로 설정하고 도구를 호출할 수 있도록 작성되었습니다.
|
|
412
|
+
|
|
413
|
+
### MCP 서버 자동 설정
|
|
414
|
+
|
|
415
|
+
**필수 조건**: Node.js >= 18.19.0, npx 사용 가능
|
|
416
|
+
|
|
417
|
+
**설치 없이 실행** (npx):
|
|
418
|
+
```bash
|
|
419
|
+
npx cc-jandi
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
**환경 변수 설정 후 실행**:
|
|
423
|
+
```bash
|
|
424
|
+
JANDI_TOKEN=your_32char_hex_token npx cc-jandi
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
서버는 stdio 전송을 사용합니다. MCP 클라이언트가 프로세스를 직접 실행하고 stdin/stdout으로 통신합니다.
|
|
428
|
+
|
|
429
|
+
### 도구 입력 스키마
|
|
430
|
+
|
|
431
|
+
각 도구의 정확한 파라미터 명세입니다. `?`는 선택 파라미터입니다.
|
|
432
|
+
|
|
433
|
+
#### `send_message`
|
|
434
|
+
```
|
|
435
|
+
message: string — 메시지 내용
|
|
436
|
+
token?: string — 직접 토큰 (32자 hex)
|
|
437
|
+
tokenAlias?: string — 환경 변수 별칭 (예: "dev", "prod")
|
|
438
|
+
```
|
|
439
|
+
토큰 해석 순서: `token` → `tokenAlias` → `JANDI_TOKEN` (기본값)
|
|
440
|
+
|
|
441
|
+
#### `send_rich_message`
|
|
442
|
+
```
|
|
443
|
+
message: string — 메시지 본문
|
|
444
|
+
color?: string — 색상 hex (예: "#FF0000")
|
|
445
|
+
connectInfo?: array — [{title?, description?, imageUrl?}]
|
|
446
|
+
token?: string
|
|
447
|
+
tokenAlias?: string
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
#### `validate_token`
|
|
451
|
+
```
|
|
452
|
+
token?: string
|
|
453
|
+
tokenAlias?: string
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
#### `test_webhook`
|
|
457
|
+
```
|
|
458
|
+
token?: string
|
|
459
|
+
tokenAlias?: string
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
#### `send_team_message`
|
|
463
|
+
```
|
|
464
|
+
email: string — 수신자 이메일 (쉼표 구분, 최대 100명)
|
|
465
|
+
message: string — 메시지 내용
|
|
466
|
+
teamId?: string — 팀 ID (tokenAlias 미사용 시 필수)
|
|
467
|
+
token?: string — 팀 토큰 (tokenAlias 미사용 시 필수)
|
|
468
|
+
tokenAlias?: string — 팀 토큰 별칭 (예: "sales")
|
|
469
|
+
```
|
|
470
|
+
팀 토큰 해석: `teamId`+`token` → `tokenAlias`로 `JANDI_TEAM_ID_{alias}` + `JANDI_TEAM_TOKEN_{alias}` 조회
|
|
471
|
+
|
|
472
|
+
#### `send_team_rich_message`
|
|
473
|
+
```
|
|
474
|
+
email: string
|
|
475
|
+
message: string
|
|
476
|
+
color?: string
|
|
477
|
+
connectInfo?: array — [{title?, description?, imageUrl?}]
|
|
478
|
+
teamId?: string
|
|
479
|
+
token?: string
|
|
480
|
+
tokenAlias?: string
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
#### `validate_team_token`
|
|
484
|
+
```
|
|
485
|
+
teamId?: string
|
|
486
|
+
token?: string
|
|
487
|
+
tokenAlias?: string
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
#### `simulate_outgoing_payload`
|
|
491
|
+
```
|
|
492
|
+
text: string — 트리거 메시지
|
|
493
|
+
webhookType?: "outgoing"|"team-outgoing" — 기본: "outgoing"
|
|
494
|
+
keyword?: string — 트리거 키워드 (기본: "test")
|
|
495
|
+
teamName?: string
|
|
496
|
+
roomName?: string
|
|
497
|
+
writerName?: string
|
|
498
|
+
writerEmail?: string
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
#### `generate_outgoing_handler`
|
|
502
|
+
```
|
|
503
|
+
framework: "express"|"fastapi"|"flask" — 서버 프레임워크
|
|
504
|
+
verificationToken?: string — 요청 검증 토큰
|
|
505
|
+
handlerLogic?: string — 커스텀 로직 설명
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
#### `validate_outgoing_response`
|
|
509
|
+
```
|
|
510
|
+
body: string — 응답 body (필수, 최대 5000자)
|
|
511
|
+
connectColor?: string — hex 색상
|
|
512
|
+
connectInfo?: array — [{title?, description?, imageUrl?}]
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
#### `generate_webhook_script`
|
|
516
|
+
```
|
|
517
|
+
language: "python"|"nodejs"|"curl"|"bash" — 언어
|
|
518
|
+
token: string — 웹훅 토큰 (필수)
|
|
519
|
+
message: string — 메시지 내용
|
|
520
|
+
webhookType?: "incoming"|"team-incoming" — 기본: "incoming"
|
|
521
|
+
color?: string
|
|
522
|
+
title?: string
|
|
523
|
+
description?: string
|
|
524
|
+
imageUrl?: string
|
|
525
|
+
teamId?: string — team-incoming 시 필수
|
|
526
|
+
email?: string — team-incoming 시 필수
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
### 기술 스택
|
|
530
|
+
|
|
531
|
+
- **런타임**: Node.js >= 18.19.0
|
|
532
|
+
- **언어**: TypeScript (ES modules, `.js` 확장자 import 필수)
|
|
533
|
+
- **프레임워크**: [mcp-framework](https://github.com/QuantGeekDev/mcp-framework) — `src/tools/` 하위를 재귀 탐색하여 도구 자동 등록
|
|
534
|
+
- **HTTP**: axios
|
|
535
|
+
- **환경변수**: dotenv
|
|
536
|
+
|
|
537
|
+
### 디렉토리 구조
|
|
538
|
+
|
|
539
|
+
```
|
|
540
|
+
src/
|
|
541
|
+
├── index.ts # 진입점 (ConfigService 초기화 → MCPServer 시작)
|
|
542
|
+
├── types/
|
|
543
|
+
│ ├── common.ts # JandiConnectInfo, JandiColors, BaseWebhookConfig, etc.
|
|
544
|
+
│ ├── incoming.ts # IncomingWebhookConfig, IncomingMessage
|
|
545
|
+
│ ├── team-incoming.ts # TeamIncomingWebhookConfig, TeamIncomingMessage
|
|
546
|
+
│ ├── outgoing.ts # OutgoingWebhookPayload, TeamOutgoingWebhookPayload
|
|
547
|
+
│ └── index.ts # 모든 타입 re-export
|
|
548
|
+
├── services/
|
|
549
|
+
│ ├── base/BaseWebhookService.ts # 추상 베이스 (HTTP, 검증, 에러 핸들링)
|
|
550
|
+
│ ├── IncomingWebhookService.ts # POST wh.jandi.com/connect-api/webhook/{token}
|
|
551
|
+
│ ├── TeamIncomingWebhookService.ts # POST wh.jandi.com/connect-api/team-webhook/{teamId}/{token}
|
|
552
|
+
│ ├── configService.ts # 환경 변수 → 토큰 Map 관리
|
|
553
|
+
│ └── index.ts
|
|
554
|
+
├── utils/
|
|
555
|
+
│ ├── resolveToken.ts # Incoming 토큰 해석 (token → alias → default)
|
|
556
|
+
│ ├── resolveTeamToken.ts # Team 토큰 해석 (teamId+token → alias)
|
|
557
|
+
│ ├── validateColor.ts # Hex 색상 검증 (#RRGGBB)
|
|
558
|
+
│ └── index.ts
|
|
559
|
+
└── tools/
|
|
560
|
+
├── incoming/ # 4개: send_message, send_rich_message, validate_token, test_webhook
|
|
561
|
+
├── team-incoming/ # 3개: send_team_message, send_team_rich_message, validate_team_token
|
|
562
|
+
├── outgoing/ # 3개: simulate_outgoing_payload, generate_outgoing_handler, validate_outgoing_response
|
|
563
|
+
└── GenerateWebhookScriptTool.ts # 1개: generate_webhook_script
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
### 도구 추가 방법
|
|
567
|
+
|
|
568
|
+
`src/tools/` 하위에 파일을 만들면 자동 등록됩니다.
|
|
569
|
+
|
|
570
|
+
```typescript
|
|
571
|
+
import { MCPTool } from "mcp-framework";
|
|
572
|
+
import { z } from "zod";
|
|
573
|
+
import { IncomingWebhookService } from "../../services/IncomingWebhookService.js";
|
|
574
|
+
import { resolveIncomingToken } from "../../utils/resolveToken.js";
|
|
575
|
+
import type { ToolResult } from "../../types/common.js";
|
|
576
|
+
|
|
577
|
+
interface MyToolInput {
|
|
578
|
+
message: string;
|
|
579
|
+
tokenAlias?: string;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
class MyTool extends MCPTool<MyToolInput> {
|
|
583
|
+
name = "my_tool";
|
|
584
|
+
description = "도구 설명";
|
|
585
|
+
|
|
586
|
+
schema = {
|
|
587
|
+
message: { type: z.string(), description: "메시지 내용" },
|
|
588
|
+
tokenAlias: { type: z.string().optional(), description: "토큰 별칭" },
|
|
589
|
+
};
|
|
590
|
+
|
|
591
|
+
async execute(input: MyToolInput): Promise<ToolResult> {
|
|
592
|
+
const resolved = resolveIncomingToken(input);
|
|
593
|
+
if (!resolved.success) return { success: false, error: resolved.error };
|
|
594
|
+
|
|
595
|
+
const message = IncomingWebhookService.createBasicMessage(input.message);
|
|
596
|
+
const result = await IncomingWebhookService.sendMessage(resolved.config, message);
|
|
597
|
+
|
|
598
|
+
if (result.success) {
|
|
599
|
+
return { success: true, data: { message: "완료", tokenUsed: resolved.config.alias || 'direct' } };
|
|
600
|
+
}
|
|
601
|
+
return { success: false, error: result.error };
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
export default MyTool;
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
**규칙:**
|
|
609
|
+
- `MCPTool<T>` 상속 + `default export` 필수
|
|
610
|
+
- import 경로에 `.js` 확장자 필수 (ES modules)
|
|
611
|
+
- Incoming 도구: `resolveIncomingToken()` 사용
|
|
612
|
+
- Team 도구: `resolveTeamToken()` 사용
|
|
613
|
+
- Outgoing 도구: 토큰 해석 불필요 (개발 헬퍼)
|
|
614
|
+
|
|
615
|
+
### 잔디 API 포맷
|
|
616
|
+
|
|
617
|
+
**요청** (Incoming / Team Incoming):
|
|
618
|
+
```json
|
|
619
|
+
{
|
|
620
|
+
"body": "메시지 본문",
|
|
621
|
+
"connectColor": "#FAC11B",
|
|
622
|
+
"connectInfo": [{"title": "제목", "description": "설명", "imageUrl": "URL"}]
|
|
623
|
+
}
|
|
624
|
+
```
|
|
625
|
+
Team Incoming은 `"to": "user@example.com"` 필드 추가.
|
|
626
|
+
|
|
627
|
+
**필수 HTTP 헤더**:
|
|
628
|
+
```
|
|
629
|
+
Accept: application/vnd.tosslab.jandi-v2+json
|
|
630
|
+
Content-Type: application/json
|
|
631
|
+
```
|
|
632
|
+
|
|
633
|
+
**에러 코드**: `40000` = 토큰 무효/비활성, `42900` = 속도 제한 초과
|
|
634
|
+
|
|
635
|
+
### 빌드
|
|
636
|
+
|
|
637
|
+
`npm run build` = `tsc` → `mcp-build`. 결과물: `dist/index.js` (진입점)
|
|
638
|
+
|
|
639
|
+
</details>
|
|
640
|
+
|
|
641
|
+
---
|
|
642
|
+
|
|
643
|
+
## 문의 및 지원
|
|
644
|
+
|
|
645
|
+
- [GitHub Issues](https://github.com/kwag93/cc-jandi/issues) — 버그 리포트 및 기능 요청
|
|
646
|
+
- [잔디 커넥트 문서](https://support.jandi.com/ko/categories/%EC%BB%A4%EB%84%A5%ED%8A%B8-fdf97953) — 잔디 웹훅 설정 가이드
|
|
647
|
+
|
|
648
|
+
## 기여하기
|
|
649
|
+
|
|
650
|
+
1. Fork
|
|
651
|
+
2. 브랜치 생성 (`git checkout -b feature/my-feature`)
|
|
652
|
+
3. 커밋 (`git commit -m 'feat: 기능 설명'`)
|
|
653
|
+
4. Push (`git push origin feature/my-feature`)
|
|
654
|
+
5. Pull Request 생성
|
|
655
|
+
|
|
656
|
+
## 라이선스
|
|
657
|
+
|
|
658
|
+
MIT — [LICENSE](LICENSE) 파일 참조
|
package/dist/index.js
ADDED