rootr-cli 0.5.0 → 0.6.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/README.md +23 -1
- package/lib/client.js +25 -0
- package/lib/mcp-tools/databases.js +98 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -204,7 +204,7 @@ rootr ask "왜 결제 실패가 늘었지?" --workspace ws_123
|
|
|
204
204
|
|
|
205
205
|
## MCP 서버로 사용하기
|
|
206
206
|
|
|
207
|
-
`rootr mcp`는 stdio로 MCP(Model Context Protocol) 서버를 구동합니다. 제공 도구
|
|
207
|
+
`rootr mcp`는 stdio로 MCP(Model Context Protocol) 서버를 구동합니다. 제공 도구 96종:
|
|
208
208
|
|
|
209
209
|
노드를 가리키는 인자(`target`, `databaseId`, `spreadsheetId`, `formId`, `crmId`, `trackerId`,
|
|
210
210
|
`recordingId`, `presentationId`, `whiteboardId`, `logId`, `parentId` 등)는 **노드 id와 경로를 모두**
|
|
@@ -282,6 +282,28 @@ rootr ask "왜 결제 실패가 늘었지?" --workspace ws_123
|
|
|
282
282
|
| `rootr_create_webhook` | 웹훅 생성 — 응답의 secret은 **1회만 노출** |
|
|
283
283
|
| `rootr_delete_webhook` | 웹훅 삭제 |
|
|
284
284
|
|
|
285
|
+
#### 표(DATABASE) — 행과 열 (docs:read/docs:write)
|
|
286
|
+
|
|
287
|
+
| 도구 | 설명 |
|
|
288
|
+
|---|---|
|
|
289
|
+
| `rootr_read_database` | 스키마(열 정의)+행 전체 읽기 — **다른 표 도구를 쓰기 전에 먼저** |
|
|
290
|
+
| `rootr_list_rows` | 행만 조회(스키마 제외) |
|
|
291
|
+
| `rootr_add_row` | 행 추가 (`values`는 열 이름 또는 id로 지정) |
|
|
292
|
+
| `rootr_update_row` | 행 수정 (칸반 카드 이동 = select 열 값 변경) |
|
|
293
|
+
| `rootr_delete_row` | 행 삭제 |
|
|
294
|
+
| `rootr_add_column` | **열 추가** — 기존 열·행은 그대로 |
|
|
295
|
+
| `rootr_update_column` | 열 이름 변경 / select 선택지 교체 |
|
|
296
|
+
| `rootr_delete_column` | 열 삭제(파괴적) |
|
|
297
|
+
|
|
298
|
+
열은 **한 번에 하나씩** 다룹니다. REST의 `PATCH /databases/:id`는 `properties`
|
|
299
|
+
배열을 통째로 교체하기 때문에, 에이전트가 배열을 다시 만들다 빠뜨린 열이 그대로
|
|
300
|
+
사라집니다. 위 세 도구는 서버가 병합하므로 나머지 열이 없어지지 않습니다.
|
|
301
|
+
|
|
302
|
+
- **열을 추가해도 기존 행 데이터는 건드리지 않습니다** — 새 열은 값이 채워지기 전까지 빈 칸으로 읽힙니다.
|
|
303
|
+
- **열의 `type`은 바꿀 수 없습니다**(모든 행의 값을 다시 써야 하므로). 새 열을 만들어 값을 옮기세요.
|
|
304
|
+
- `title` 열은 추가로 만들 수도, 삭제할 수도 없습니다.
|
|
305
|
+
- 삭제한 열의 값은 행 안에 남아 있지만 화면에는 나오지 않습니다 — 사용자 확인 후 호출하세요.
|
|
306
|
+
|
|
285
307
|
#### 문서 버전 / 삭제
|
|
286
308
|
|
|
287
309
|
| 도구 | 설명 |
|
package/lib/client.js
CHANGED
|
@@ -565,6 +565,31 @@ export class RootrClient {
|
|
|
565
565
|
);
|
|
566
566
|
}
|
|
567
567
|
|
|
568
|
+
// ---- DB-COL: one column at a time --------------------------------------
|
|
569
|
+
// PATCH /databases/:id replaces the whole properties array, so rebuilding it
|
|
570
|
+
// client-side drops any column left out. These merge server-side instead.
|
|
571
|
+
|
|
572
|
+
async addColumn(databaseId, { name, type, options } = {}) {
|
|
573
|
+
return this.request(`/databases/${encodeURIComponent(databaseId)}/columns`, {
|
|
574
|
+
method: 'POST',
|
|
575
|
+
body: { name, type, options },
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
async updateColumn(databaseId, propertyId, { name, options } = {}) {
|
|
580
|
+
return this.request(
|
|
581
|
+
`/databases/${encodeURIComponent(databaseId)}/columns/${encodeURIComponent(propertyId)}`,
|
|
582
|
+
{ method: 'PATCH', body: { name, options } },
|
|
583
|
+
);
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
async deleteColumn(databaseId, propertyId) {
|
|
587
|
+
return this.request(
|
|
588
|
+
`/databases/${encodeURIComponent(databaseId)}/columns/${encodeURIComponent(propertyId)}`,
|
|
589
|
+
{ method: 'DELETE' },
|
|
590
|
+
);
|
|
591
|
+
}
|
|
592
|
+
|
|
568
593
|
// ---- CRM ----------------------------------------------------------------
|
|
569
594
|
|
|
570
595
|
/** Create a CRM node. pipelines/fields/config optional (server fills sane defaults). */
|
|
@@ -171,4 +171,102 @@ export function registerDatabaseTools(server) {
|
|
|
171
171
|
}
|
|
172
172
|
},
|
|
173
173
|
);
|
|
174
|
+
|
|
175
|
+
// ---- DB-COL: columns ----------------------------------------------------
|
|
176
|
+
// Row tools above cover the data; these cover the SHAPE. Deliberately one
|
|
177
|
+
// column per call: the underlying PATCH /databases/:id takes the entire
|
|
178
|
+
// properties array, and a model rebuilding that array drops whatever it
|
|
179
|
+
// forgets. Here the server merges, so nothing else in the table can be lost.
|
|
180
|
+
|
|
181
|
+
server.registerTool(
|
|
182
|
+
'rootr_add_column',
|
|
183
|
+
{
|
|
184
|
+
title: 'Add a column to a database',
|
|
185
|
+
description:
|
|
186
|
+
'Add ONE column to a Rootr (루터) DATABASE. The existing columns and every row are left untouched — ' +
|
|
187
|
+
'rows simply read empty for the new column until you fill them with rootr_update_row. ' +
|
|
188
|
+
'type ∈ text | number | select | multi_select | date | checkbox | url | person ' +
|
|
189
|
+
'("title" is not allowed: a database already has exactly one title column). ' +
|
|
190
|
+
'For select/multi_select pass options: [{ name, color }]. Returns the created column WITH ITS id.',
|
|
191
|
+
annotations: { readOnlyHint: false, destructiveHint: false },
|
|
192
|
+
inputSchema: {
|
|
193
|
+
databaseId: z.string().describe(DATABASE_TARGET_DESC),
|
|
194
|
+
name: z.string().describe('Column header, e.g. "담당자"'),
|
|
195
|
+
type: z
|
|
196
|
+
.enum(['text', 'number', 'select', 'multi_select', 'date', 'checkbox', 'url', 'person'])
|
|
197
|
+
.describe('Column type'),
|
|
198
|
+
options: z
|
|
199
|
+
.array(z.object({ name: z.string(), color: z.string() }))
|
|
200
|
+
.optional()
|
|
201
|
+
.describe('select/multi_select only: the choices, e.g. [{ name: "진행중", color: "blue" }]'),
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
async ({ databaseId, name, type, options }) => {
|
|
205
|
+
try {
|
|
206
|
+
const client = makeClient();
|
|
207
|
+
const id = await resolveNodeId(client, databaseId);
|
|
208
|
+
const property = await client.addColumn(id, { name, type, options });
|
|
209
|
+
return jsonResult({ property });
|
|
210
|
+
} catch (err) {
|
|
211
|
+
return errorResult(err);
|
|
212
|
+
}
|
|
213
|
+
},
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
server.registerTool(
|
|
217
|
+
'rootr_update_column',
|
|
218
|
+
{
|
|
219
|
+
title: 'Rename a column / set its options',
|
|
220
|
+
description:
|
|
221
|
+
'Rename ONE column of a Rootr (루터) DATABASE, and/or replace the choices of a select/multi_select column. ' +
|
|
222
|
+
'The column TYPE cannot be changed — that would rewrite every row — so add a new column and move the values instead. ' +
|
|
223
|
+
'Call rootr_read_database first to get the exact propertyId.',
|
|
224
|
+
annotations: { readOnlyHint: false, destructiveHint: false },
|
|
225
|
+
inputSchema: {
|
|
226
|
+
databaseId: z.string().describe(DATABASE_TARGET_DESC),
|
|
227
|
+
propertyId: z.string().describe('Column id from rootr_read_database (NOT the header text)'),
|
|
228
|
+
name: z.string().optional().describe('New header text'),
|
|
229
|
+
options: z
|
|
230
|
+
.array(z.object({ name: z.string(), color: z.string() }))
|
|
231
|
+
.optional()
|
|
232
|
+
.describe('select/multi_select only: REPLACES the full choice list'),
|
|
233
|
+
},
|
|
234
|
+
},
|
|
235
|
+
async ({ databaseId, propertyId, name, options }) => {
|
|
236
|
+
try {
|
|
237
|
+
const client = makeClient();
|
|
238
|
+
const id = await resolveNodeId(client, databaseId);
|
|
239
|
+
const property = await client.updateColumn(id, propertyId, { name, options });
|
|
240
|
+
return jsonResult({ property });
|
|
241
|
+
} catch (err) {
|
|
242
|
+
return errorResult(err);
|
|
243
|
+
}
|
|
244
|
+
},
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
server.registerTool(
|
|
248
|
+
'rootr_delete_column',
|
|
249
|
+
{
|
|
250
|
+
title: 'Delete a column from a database',
|
|
251
|
+
description:
|
|
252
|
+
'Remove ONE column from a Rootr (루터) DATABASE. The values stored under it are NOT erased from the rows, ' +
|
|
253
|
+
'so re-adding a column is not enough to bring them back in the UI — confirm with the user before calling this. ' +
|
|
254
|
+
'The title column cannot be deleted. Views grouped/dated by the column are cleared automatically.',
|
|
255
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
256
|
+
inputSchema: {
|
|
257
|
+
databaseId: z.string().describe(DATABASE_TARGET_DESC),
|
|
258
|
+
propertyId: z.string().describe('Column id from rootr_read_database'),
|
|
259
|
+
},
|
|
260
|
+
},
|
|
261
|
+
async ({ databaseId, propertyId }) => {
|
|
262
|
+
try {
|
|
263
|
+
const client = makeClient();
|
|
264
|
+
const id = await resolveNodeId(client, databaseId);
|
|
265
|
+
const property = await client.deleteColumn(id, propertyId);
|
|
266
|
+
return jsonResult({ deleted: true, property });
|
|
267
|
+
} catch (err) {
|
|
268
|
+
return errorResult(err);
|
|
269
|
+
}
|
|
270
|
+
},
|
|
271
|
+
);
|
|
174
272
|
}
|