@tenonhq/dovetail-mcp 0.0.8 → 0.0.10
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/dist/registry.d.ts +8 -0
- package/dist/registry.js +46 -1
- package/package.json +6 -6
package/dist/registry.d.ts
CHANGED
|
@@ -6,8 +6,15 @@
|
|
|
6
6
|
* Dependencies are passed in by the caller — server.ts builds them from
|
|
7
7
|
* loadConfig(), tests inject mocks. This split keeps registry.ts pure of
|
|
8
8
|
* env access.
|
|
9
|
+
*
|
|
10
|
+
* Every descriptor carries MCP tool annotations (readOnlyHint / destructiveHint
|
|
11
|
+
* / idempotentHint) — untrusted behavioural hints per the MCP spec. They are
|
|
12
|
+
* informational to permission UX, but Claude Code already uses readOnlyHint to
|
|
13
|
+
* schedule read-only tools concurrently, and hosts that gate on destructiveHint
|
|
14
|
+
* get correct confirmation behaviour for free.
|
|
9
15
|
*/
|
|
10
16
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
17
|
+
import type { ToolAnnotations } from "@modelcontextprotocol/sdk/types.js";
|
|
11
18
|
import { z } from "zod";
|
|
12
19
|
import type { ClickUpDeps } from "./tools/clickup";
|
|
13
20
|
import type { GmailDeps } from "./tools/gmail";
|
|
@@ -26,6 +33,7 @@ interface ToolDescriptor {
|
|
|
26
33
|
name: ToolName;
|
|
27
34
|
description: string;
|
|
28
35
|
shape: z.ZodRawShape;
|
|
36
|
+
annotations: ToolAnnotations;
|
|
29
37
|
handler: (args: any) => Promise<any>;
|
|
30
38
|
}
|
|
31
39
|
export declare function registerAllTools(server: McpServer, deps: RegistryDeps): void;
|
package/dist/registry.js
CHANGED
|
@@ -7,6 +7,12 @@
|
|
|
7
7
|
* Dependencies are passed in by the caller — server.ts builds them from
|
|
8
8
|
* loadConfig(), tests inject mocks. This split keeps registry.ts pure of
|
|
9
9
|
* env access.
|
|
10
|
+
*
|
|
11
|
+
* Every descriptor carries MCP tool annotations (readOnlyHint / destructiveHint
|
|
12
|
+
* / idempotentHint) — untrusted behavioural hints per the MCP spec. They are
|
|
13
|
+
* informational to permission UX, but Claude Code already uses readOnlyHint to
|
|
14
|
+
* schedule read-only tools concurrently, and hosts that gate on destructiveHint
|
|
15
|
+
* get correct confirmation behaviour for free.
|
|
10
16
|
*/
|
|
11
17
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
18
|
exports.TOOL_NAMES = void 0;
|
|
@@ -41,6 +47,28 @@ exports.TOOL_NAMES = [
|
|
|
41
47
|
"calendar_get_event",
|
|
42
48
|
"servicenow_query_table"
|
|
43
49
|
];
|
|
50
|
+
// Annotation presets. All tools reach external services (ClickUp/Gmail/
|
|
51
|
+
// Calendar/ServiceNow), so openWorldHint stays at its spec default of true.
|
|
52
|
+
var READ_ONLY = { readOnlyHint: true };
|
|
53
|
+
// A write that overwrites existing state (re-running with the same args
|
|
54
|
+
// converges, so it is idempotent but still destructive).
|
|
55
|
+
var WRITE_OVERWRITE = {
|
|
56
|
+
readOnlyHint: false,
|
|
57
|
+
destructiveHint: true,
|
|
58
|
+
idempotentHint: true
|
|
59
|
+
};
|
|
60
|
+
// A purely additive create (re-running produces another record).
|
|
61
|
+
var WRITE_CREATE = {
|
|
62
|
+
readOnlyHint: false,
|
|
63
|
+
destructiveHint: false,
|
|
64
|
+
idempotentHint: false
|
|
65
|
+
};
|
|
66
|
+
// A purely additive, idempotent link (re-running is a no-op).
|
|
67
|
+
var WRITE_ADDITIVE_IDEMPOTENT = {
|
|
68
|
+
readOnlyHint: false,
|
|
69
|
+
destructiveHint: false,
|
|
70
|
+
idempotentHint: true
|
|
71
|
+
};
|
|
44
72
|
function buildDescriptors(deps) {
|
|
45
73
|
var clickupReady = !!deps.clickup;
|
|
46
74
|
var googleReady = !!deps.gmail && !!deps.calendar;
|
|
@@ -49,6 +77,7 @@ function buildDescriptors(deps) {
|
|
|
49
77
|
name: "clickup_list_tasks",
|
|
50
78
|
description: "List ClickUp tasks assigned to the authenticated user, grouped by status. teamId optional if CLICKUP_TEAM_ID is set.",
|
|
51
79
|
shape: clickup_1.clickupListTasksSchema.shape,
|
|
80
|
+
annotations: READ_ONLY,
|
|
52
81
|
handler: requireConfig(clickupReady, "ClickUp", deps.missingDescription, function (args) {
|
|
53
82
|
return (0, clickup_2.clickupListTasks)(args, deps.clickup);
|
|
54
83
|
})
|
|
@@ -57,6 +86,7 @@ function buildDescriptors(deps) {
|
|
|
57
86
|
name: "clickup_get_task",
|
|
58
87
|
description: "Fetch a single ClickUp task by ID.",
|
|
59
88
|
shape: clickup_1.clickupGetTaskSchema.shape,
|
|
89
|
+
annotations: READ_ONLY,
|
|
60
90
|
handler: requireConfig(clickupReady, "ClickUp", deps.missingDescription, function (args) {
|
|
61
91
|
return (0, clickup_2.clickupGetTask)(args, deps.clickup);
|
|
62
92
|
})
|
|
@@ -65,6 +95,7 @@ function buildDescriptors(deps) {
|
|
|
65
95
|
name: "clickup_search_tasks",
|
|
66
96
|
description: "Search team tasks by substring match against task name/description. teamId optional if CLICKUP_TEAM_ID is set.",
|
|
67
97
|
shape: clickup_1.clickupSearchTasksSchema.shape,
|
|
98
|
+
annotations: READ_ONLY,
|
|
68
99
|
handler: requireConfig(clickupReady, "ClickUp", deps.missingDescription, function (args) {
|
|
69
100
|
return (0, clickup_2.clickupSearchTasks)(args, deps.clickup);
|
|
70
101
|
})
|
|
@@ -73,6 +104,7 @@ function buildDescriptors(deps) {
|
|
|
73
104
|
name: "clickup_get_team_sync",
|
|
74
105
|
description: "Returns a structured JSON team sync with the 7-stage pipeline (Blocked, In Progress, In Review, QA, UAT, Ready for Release, Done) plus unmapped statuses and unassigned tasks.",
|
|
75
106
|
shape: clickup_1.clickupGetTeamSyncSchema.shape,
|
|
107
|
+
annotations: READ_ONLY,
|
|
76
108
|
handler: requireConfig(clickupReady, "ClickUp", deps.missingDescription, function (args) {
|
|
77
109
|
return (0, clickup_2.clickupGetTeamSync)(args, deps.clickup);
|
|
78
110
|
})
|
|
@@ -81,6 +113,7 @@ function buildDescriptors(deps) {
|
|
|
81
113
|
name: "clickup_update_task",
|
|
82
114
|
description: "Gated write: update a ClickUp task (name, markdownContent, status, priority). Requires SINC_MCP_WRITES_ENABLE=1; returns a dry-run preview unless confirm:true. Use customTaskIds:true + teamId to target a custom ID like DEV-225.",
|
|
83
115
|
shape: clickup_1.clickupUpdateTaskSchema.shape,
|
|
116
|
+
annotations: WRITE_OVERWRITE,
|
|
84
117
|
handler: requireConfig(clickupReady, "ClickUp", deps.missingDescription, function (args) {
|
|
85
118
|
return (0, clickup_write_1.clickupUpdateTask)(args, deps.clickup);
|
|
86
119
|
})
|
|
@@ -89,6 +122,7 @@ function buildDescriptors(deps) {
|
|
|
89
122
|
name: "clickup_set_custom_field",
|
|
90
123
|
description: "Gated write: set one custom-field value on a task (POST /task/{id}/field/{fieldId}). Requires SINC_MCP_WRITES_ENABLE=1; dry-run unless confirm:true. value shape: string for text/url, option id for drop_down, { add:[], rem:[] } for users.",
|
|
91
124
|
shape: clickup_1.clickupSetCustomFieldSchema.shape,
|
|
125
|
+
annotations: WRITE_OVERWRITE,
|
|
92
126
|
handler: requireConfig(clickupReady, "ClickUp", deps.missingDescription, function (args) {
|
|
93
127
|
return (0, clickup_write_1.clickupSetCustomField)(args, deps.clickup);
|
|
94
128
|
})
|
|
@@ -97,6 +131,7 @@ function buildDescriptors(deps) {
|
|
|
97
131
|
name: "clickup_create_task",
|
|
98
132
|
description: "Gated write: create a ClickUp task in a list (markdownContent, status, priority, assignees, customFields). Requires SINC_MCP_WRITES_ENABLE=1; dry-run unless confirm:true.",
|
|
99
133
|
shape: clickup_1.clickupCreateTaskSchema.shape,
|
|
134
|
+
annotations: WRITE_CREATE,
|
|
100
135
|
handler: requireConfig(clickupReady, "ClickUp", deps.missingDescription, function (args) {
|
|
101
136
|
return (0, clickup_write_1.clickupCreateTask)(args, deps.clickup);
|
|
102
137
|
})
|
|
@@ -105,6 +140,7 @@ function buildDescriptors(deps) {
|
|
|
105
140
|
name: "clickup_link_tasks",
|
|
106
141
|
description: "Gated write: link two ClickUp tasks (POST /task/{id}/link/{linksTo}). Requires SINC_MCP_WRITES_ENABLE=1; dry-run unless confirm:true. Use customTaskIds:true + teamId for custom IDs.",
|
|
107
142
|
shape: clickup_1.clickupLinkTasksSchema.shape,
|
|
143
|
+
annotations: WRITE_ADDITIVE_IDEMPOTENT,
|
|
108
144
|
handler: requireConfig(clickupReady, "ClickUp", deps.missingDescription, function (args) {
|
|
109
145
|
return (0, clickup_write_1.clickupLinkTasks)(args, deps.clickup);
|
|
110
146
|
})
|
|
@@ -113,6 +149,7 @@ function buildDescriptors(deps) {
|
|
|
113
149
|
name: "gmail_get_unread",
|
|
114
150
|
description: "Fetch unread emails from the inbox.",
|
|
115
151
|
shape: gmail_1.gmailGetUnreadSchema.shape,
|
|
152
|
+
annotations: READ_ONLY,
|
|
116
153
|
handler: requireConfig(googleReady, "Google (Gmail)", deps.missingDescription, function (args) {
|
|
117
154
|
return (0, gmail_2.gmailGetUnread)(args, deps.gmail);
|
|
118
155
|
})
|
|
@@ -121,6 +158,7 @@ function buildDescriptors(deps) {
|
|
|
121
158
|
name: "gmail_get_starred",
|
|
122
159
|
description: "Fetch starred emails.",
|
|
123
160
|
shape: gmail_1.gmailGetStarredSchema.shape,
|
|
161
|
+
annotations: READ_ONLY,
|
|
124
162
|
handler: requireConfig(googleReady, "Google (Gmail)", deps.missingDescription, function (args) {
|
|
125
163
|
return (0, gmail_2.gmailGetStarred)(args, deps.gmail);
|
|
126
164
|
})
|
|
@@ -129,6 +167,7 @@ function buildDescriptors(deps) {
|
|
|
129
167
|
name: "gmail_search",
|
|
130
168
|
description: "Search emails using Gmail query syntax (e.g. 'from:alice has:attachment').",
|
|
131
169
|
shape: gmail_1.gmailSearchSchema.shape,
|
|
170
|
+
annotations: READ_ONLY,
|
|
132
171
|
handler: requireConfig(googleReady, "Google (Gmail)", deps.missingDescription, function (args) {
|
|
133
172
|
return (0, gmail_2.gmailSearch)(args, deps.gmail);
|
|
134
173
|
})
|
|
@@ -137,6 +176,7 @@ function buildDescriptors(deps) {
|
|
|
137
176
|
name: "gmail_get_action_required",
|
|
138
177
|
description: "Fetch unread action-required emails matched against subject patterns and labels (defaults: 'action required', 'urgent', 'asap', 'time sensitive').",
|
|
139
178
|
shape: gmail_1.gmailGetActionRequiredSchema.shape,
|
|
179
|
+
annotations: READ_ONLY,
|
|
140
180
|
handler: requireConfig(googleReady, "Google (Gmail)", deps.missingDescription, function (args) {
|
|
141
181
|
return (0, gmail_2.gmailGetActionRequired)(args, deps.gmail);
|
|
142
182
|
})
|
|
@@ -145,6 +185,7 @@ function buildDescriptors(deps) {
|
|
|
145
185
|
name: "calendar_get_today",
|
|
146
186
|
description: "Fetch today's calendar events.",
|
|
147
187
|
shape: calendar_1.calendarGetTodaySchema.shape,
|
|
188
|
+
annotations: READ_ONLY,
|
|
148
189
|
handler: requireConfig(googleReady, "Google (Calendar)", deps.missingDescription, function (args) {
|
|
149
190
|
return (0, calendar_2.calendarGetToday)(args, deps.calendar);
|
|
150
191
|
})
|
|
@@ -153,6 +194,7 @@ function buildDescriptors(deps) {
|
|
|
153
194
|
name: "calendar_get_week",
|
|
154
195
|
description: "Fetch the next 7 days of calendar events.",
|
|
155
196
|
shape: calendar_1.calendarGetWeekSchema.shape,
|
|
197
|
+
annotations: READ_ONLY,
|
|
156
198
|
handler: requireConfig(googleReady, "Google (Calendar)", deps.missingDescription, function (args) {
|
|
157
199
|
return (0, calendar_2.calendarGetWeek)(args, deps.calendar);
|
|
158
200
|
})
|
|
@@ -161,6 +203,7 @@ function buildDescriptors(deps) {
|
|
|
161
203
|
name: "calendar_get_event",
|
|
162
204
|
description: "Fetch a single calendar event by ID.",
|
|
163
205
|
shape: calendar_1.calendarGetEventSchema.shape,
|
|
206
|
+
annotations: READ_ONLY,
|
|
164
207
|
handler: requireConfig(googleReady, "Google (Calendar)", deps.missingDescription, function (args) {
|
|
165
208
|
return (0, calendar_2.calendarGetEvent)(args, deps.calendar);
|
|
166
209
|
})
|
|
@@ -169,6 +212,7 @@ function buildDescriptors(deps) {
|
|
|
169
212
|
name: "servicenow_query_table",
|
|
170
213
|
description: "Read-only GET against the ServiceNow Table API. Required: table (lower-case identifier), sysparm_query (ServiceNow encoded query). Optional: fields[] limits the columns returned, limit caps row count (default 100, max 1000). Tables on the deny list (sys_user_password, sys_credential, etc.) are rejected unless SINC_MCP_SN_TABLE_OVERRIDE=<table> is set.",
|
|
171
214
|
shape: servicenow_1.servicenowQueryTableSchema.shape,
|
|
215
|
+
annotations: READ_ONLY,
|
|
172
216
|
handler: function (args) {
|
|
173
217
|
return (0, servicenow_2.servicenowQueryTable)(args, deps.servicenow);
|
|
174
218
|
}
|
|
@@ -196,7 +240,8 @@ function registerOne(server, desc) {
|
|
|
196
240
|
// when a heterogeneous descriptor list feeds the same call site.
|
|
197
241
|
server.registerTool(desc.name, {
|
|
198
242
|
description: desc.description,
|
|
199
|
-
inputSchema: desc.shape
|
|
243
|
+
inputSchema: desc.shape,
|
|
244
|
+
annotations: desc.annotations
|
|
200
245
|
}, async function (args) {
|
|
201
246
|
try {
|
|
202
247
|
var result = await (0, telemetry_1.withTelemetry)(desc.name, args, function () {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tenonhq/dovetail-mcp",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.10",
|
|
4
4
|
"description": "MCP server exposing read tools for ClickUp / Gmail / Calendar / ServiceNow plus gated ClickUp writes, backed by the Dovetail integration packages.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -17,11 +17,11 @@
|
|
|
17
17
|
"license": "GPL-3.0",
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
20
|
-
"@tenonhq/dovetail-clickup": "
|
|
21
|
-
"@tenonhq/dovetail-gmail": "
|
|
22
|
-
"@tenonhq/dovetail-google-auth": "
|
|
23
|
-
"@tenonhq/dovetail-google-calendar": "
|
|
24
|
-
"@tenonhq/dovetail-servicenow": "
|
|
20
|
+
"@tenonhq/dovetail-clickup": "~0.0.12",
|
|
21
|
+
"@tenonhq/dovetail-gmail": "~0.0.11",
|
|
22
|
+
"@tenonhq/dovetail-google-auth": "~0.0.12",
|
|
23
|
+
"@tenonhq/dovetail-google-calendar": "~0.0.11",
|
|
24
|
+
"@tenonhq/dovetail-servicenow": "~0.0.15",
|
|
25
25
|
"dotenv": "^16.3.1",
|
|
26
26
|
"zod": "^3.23.0"
|
|
27
27
|
},
|