@nestr/mcp 0.1.47 → 0.1.49
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/build/server.d.ts.map +1 -1
- package/build/server.js +49 -9
- package/build/server.js.map +1 -1
- package/build/skills/doing-work.d.ts.map +1 -1
- package/build/skills/doing-work.js +62 -137
- package/build/skills/doing-work.js.map +1 -1
- package/build/skills/tension-processing.d.ts.map +1 -1
- package/build/skills/tension-processing.js +75 -181
- package/build/skills/tension-processing.js.map +1 -1
- package/build/skills/workspace-setup.d.ts.map +1 -1
- package/build/skills/workspace-setup.js +58 -138
- package/build/skills/workspace-setup.js.map +1 -1
- package/build/tools/index.d.ts +50 -60
- package/build/tools/index.d.ts.map +1 -1
- package/build/tools/index.js +144 -43
- package/build/tools/index.js.map +1 -1
- package/package.json +1 -1
package/build/tools/index.js
CHANGED
|
@@ -27,7 +27,7 @@ function completableResponse(data, source, title) {
|
|
|
27
27
|
// Fields to keep for compact list responses (reduces token usage)
|
|
28
28
|
const COMPACT_FIELDS = {
|
|
29
29
|
// Common fields for all nests (includes fields needed by the completable list app)
|
|
30
|
-
base: ["_id", "title", "purpose", "completed", "labels", "path", "parentId", "ancestors", "description", "due"],
|
|
30
|
+
base: ["_id", "title", "purpose", "completed", "labels", "path", "parentId", "ancestors", "description", "due", "hints"],
|
|
31
31
|
// Additional fields for roles
|
|
32
32
|
role: ["accountabilities", "domains"],
|
|
33
33
|
// Additional fields for users
|
|
@@ -65,6 +65,85 @@ function compactResponse(data, type = "nest") {
|
|
|
65
65
|
return compact;
|
|
66
66
|
});
|
|
67
67
|
}
|
|
68
|
+
// URL-to-tool mapping for hint enrichment.
|
|
69
|
+
// The Nestr API returns hints with relative URLs (e.g., "/nests/abc123/children?search=...").
|
|
70
|
+
// This maps those URL patterns to MCP tool calls so models can act on hints directly.
|
|
71
|
+
// Note: patterns are tried in order — more specific patterns must come before catch-alls.
|
|
72
|
+
const HINT_URL_PATTERNS = [
|
|
73
|
+
// /nests/{id}/children?search=... → nestr_search with in:{id} scoped query
|
|
74
|
+
{
|
|
75
|
+
pattern: /^\/nests\/([^/]+)\/children$/,
|
|
76
|
+
tool: "nestr_search",
|
|
77
|
+
params: (m, sp, workspaceId) => {
|
|
78
|
+
const search = sp.get("search") || "";
|
|
79
|
+
const result = { query: `in:${m[1]} ${search}`.trim() };
|
|
80
|
+
if (workspaceId)
|
|
81
|
+
result.workspaceId = workspaceId;
|
|
82
|
+
return result;
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
// /nests/{id}/posts → nestr_get_comments
|
|
86
|
+
{ pattern: /^\/nests\/([^/]+)\/posts$/, tool: "nestr_get_comments", params: (m) => ({ nestId: m[1] }) },
|
|
87
|
+
// /nests/{id}/tensions → nestr_list_tensions
|
|
88
|
+
{ pattern: /^\/nests\/([^/]+)\/tensions$/, tool: "nestr_list_tensions", params: (m) => ({ nestId: m[1] }) },
|
|
89
|
+
// /nests/{id} → nestr_get_nest (must be last — catches all /nests/{id} patterns)
|
|
90
|
+
{ pattern: /^\/nests\/([^/]+)$/, tool: "nestr_get_nest", params: (m) => ({ nestId: m[1] }) },
|
|
91
|
+
];
|
|
92
|
+
// Enrich hints with tool call parameters so models can act on hints directly.
|
|
93
|
+
// Extracts workspaceId from nest ancestors (last element) for search-based hints.
|
|
94
|
+
function enrichHints(data) {
|
|
95
|
+
if (!data || typeof data !== "object")
|
|
96
|
+
return data;
|
|
97
|
+
// Handle arrays (e.g., from getNestChildren)
|
|
98
|
+
if (Array.isArray(data)) {
|
|
99
|
+
return data.map((item) => enrichHints(item));
|
|
100
|
+
}
|
|
101
|
+
// Handle wrapped responses { data: [...] }
|
|
102
|
+
if ("data" in data && Array.isArray(data.data)) {
|
|
103
|
+
return { ...data, data: enrichHints(data.data) };
|
|
104
|
+
}
|
|
105
|
+
// Enrich hints on this nest
|
|
106
|
+
const record = data;
|
|
107
|
+
if (Array.isArray(record.hints)) {
|
|
108
|
+
// Extract workspaceId from ancestors (last element is always the workspace)
|
|
109
|
+
const ancestors = record.ancestors;
|
|
110
|
+
const workspaceId = ancestors?.length ? ancestors[ancestors.length - 1] : undefined;
|
|
111
|
+
record.hints = record.hints.map((hint) => {
|
|
112
|
+
if (!hint.url)
|
|
113
|
+
return hint;
|
|
114
|
+
// Parse URL and query params — strip absolute URL prefix if present
|
|
115
|
+
let rawUrl = hint.url;
|
|
116
|
+
const apiPrefixMatch = rawUrl.match(/^https?:\/\/[^/]+\/api(\/.*)/);
|
|
117
|
+
if (apiPrefixMatch)
|
|
118
|
+
rawUrl = apiPrefixMatch[1];
|
|
119
|
+
const [path, queryString] = rawUrl.split("?");
|
|
120
|
+
const searchParams = new URLSearchParams(queryString || "");
|
|
121
|
+
for (const { pattern, tool, params } of HINT_URL_PATTERNS) {
|
|
122
|
+
const match = path.match(pattern);
|
|
123
|
+
if (match) {
|
|
124
|
+
return { ...hint, toolCall: { tool, params: params(match, searchParams, workspaceId) } };
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
// Log unrecognized hint URLs so we can add mappings when the API adds new patterns
|
|
128
|
+
console.error(`[nestr-mcp] Unrecognized hint URL pattern: "${hint.url}" (hint type: ${hint.type})`);
|
|
129
|
+
return hint;
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
return data;
|
|
133
|
+
}
|
|
134
|
+
// Coerce JSON-stringified arrays/objects before Zod validation.
|
|
135
|
+
// Some MCP clients send array/object params as JSON strings (e.g., "[\"project\"]" instead of ["project"]).
|
|
136
|
+
const coerceFromJson = (schema) => z.preprocess((val) => {
|
|
137
|
+
if (typeof val === 'string') {
|
|
138
|
+
try {
|
|
139
|
+
return JSON.parse(val);
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
return val;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return val;
|
|
146
|
+
}, schema);
|
|
68
147
|
// Tool input schemas using Zod
|
|
69
148
|
export const schemas = {
|
|
70
149
|
listWorkspaces: z.object({
|
|
@@ -81,7 +160,7 @@ export const schemas = {
|
|
|
81
160
|
type: z.enum(['personal', 'collaborative']).optional().describe("'personal' for individual use (free forever), 'collaborative' for team use (free trial, then paid). Defaults to 'collaborative'."),
|
|
82
161
|
governance: z.enum(['holacracy', 'sociocracy', 'roles_circles']).optional().describe("Self-organization model. Defaults to 'roles_circles' (generic role-based)."),
|
|
83
162
|
plan: z.enum(['starter', 'pro']).optional().describe("Subscription plan for collaborative workspaces. Defaults to 'pro' (17-day trial)."),
|
|
84
|
-
apps: z.array(z.enum(['okr', 'feedback', 'insights'])).optional().describe("Apps to enable (e.g., ['okr', 'feedback']). 'insights' requires pro plan."),
|
|
163
|
+
apps: coerceFromJson(z.array(z.enum(['okr', 'feedback', 'insights']))).optional().describe("Apps to enable (e.g., ['okr', 'feedback']). 'insights' requires pro plan."),
|
|
85
164
|
layout: z.enum(['board', 'list']).optional().describe("Layout style for personal workspaces. 'board' creates kanban columns (Todo, Doing, Done)."),
|
|
86
165
|
}),
|
|
87
166
|
search: z.object({
|
|
@@ -108,10 +187,10 @@ export const schemas = {
|
|
|
108
187
|
title: z.string().describe("Title of the new nest (plain text, HTML stripped)"),
|
|
109
188
|
purpose: z.string().optional().describe("Purpose — the aspirational future state this nest is working towards. Most important for workspaces, circles, and roles where it defines the north star and context boundary. For other nests, prefer description or fields for detailed information — but purpose can be set if meaningful. Supports HTML."),
|
|
110
189
|
description: z.string().optional().describe("Detailed description — the primary field for storing information about a nest. Use for project details, task context, acceptance criteria, Definition of Done, etc. Supports HTML: <b>, <i>, <code>, <ul>, <li>, <a>."),
|
|
111
|
-
labels: z.array(z.string()).optional().describe("Label IDs to apply"),
|
|
112
|
-
users: z.array(z.string()).optional().describe("User IDs to assign (required for tasks/projects to associate with a person)"),
|
|
113
|
-
accountabilities: z.array(z.string()).optional().describe("Accountability titles for roles/circles. Only used when labels include 'role' or 'circle'. Each string becomes an accountability child nest."),
|
|
114
|
-
domains: z.array(z.string()).optional().describe("Domain titles for roles/circles. Only used when labels include 'role' or 'circle'. Each string becomes a domain child nest."),
|
|
190
|
+
labels: coerceFromJson(z.array(z.string())).optional().describe("Label IDs to apply"),
|
|
191
|
+
users: coerceFromJson(z.array(z.string())).optional().describe("User IDs to assign (required for tasks/projects to associate with a person)"),
|
|
192
|
+
accountabilities: coerceFromJson(z.array(z.string())).optional().describe("Accountability titles for roles/circles. Only used when labels include 'role' or 'circle'. Each string becomes an accountability child nest."),
|
|
193
|
+
domains: coerceFromJson(z.array(z.string())).optional().describe("Domain titles for roles/circles. Only used when labels include 'role' or 'circle'. Each string becomes a domain child nest."),
|
|
115
194
|
workspaceId: z.string().optional().describe("Workspace ID. Required when creating roles/circles with accountabilities or domains (used to route to the self-organization API)."),
|
|
116
195
|
}),
|
|
117
196
|
updateNest: z.object({
|
|
@@ -120,14 +199,14 @@ export const schemas = {
|
|
|
120
199
|
purpose: z.string().optional().describe("New purpose — the aspirational future state. Most important for workspaces, circles, and roles. For other nests, prefer description or fields — but purpose can be set if meaningful. Supports HTML."),
|
|
121
200
|
description: z.string().optional().describe("New description — the primary field for detailed information. Use for project details, task context, acceptance criteria, etc. Supports HTML."),
|
|
122
201
|
parentId: z.string().optional().describe("New parent ID (move nest to different location, e.g., move inbox item to a role or project)"),
|
|
123
|
-
labels: z.array(z.string()).optional().describe("Label IDs to set (e.g., ['project'] to convert an item into a project)"),
|
|
124
|
-
fields: z.record(z.unknown()).optional().describe("Field updates (e.g., { 'project.status': 'Current' })"),
|
|
125
|
-
users: z.array(z.string()).optional().describe("User IDs to assign"),
|
|
126
|
-
data: z.record(z.unknown()).optional().describe("Key-value data store shared with Nestr internals — never overwrite existing keys. Namespace your own data under 'mcp.' (e.g., { 'mcp.lastSync': '...' }). For AI knowledge persistence, use skills instead."),
|
|
202
|
+
labels: coerceFromJson(z.array(z.string())).optional().describe("Label IDs to set (e.g., ['project'] to convert an item into a project)"),
|
|
203
|
+
fields: coerceFromJson(z.record(z.unknown())).optional().describe("Field updates (e.g., { 'project.status': 'Current' })"),
|
|
204
|
+
users: coerceFromJson(z.array(z.string())).optional().describe("User IDs to assign"),
|
|
205
|
+
data: coerceFromJson(z.record(z.unknown())).optional().describe("Key-value data store shared with Nestr internals — never overwrite existing keys. Namespace your own data under 'mcp.' (e.g., { 'mcp.lastSync': '...' }). For AI knowledge persistence, use skills instead."),
|
|
127
206
|
due: z.string().optional().describe("Due date (ISO format). For projects/tasks: deadline. For roles: re-election date. For meetings: start time."),
|
|
128
207
|
completed: z.boolean().optional().describe("Mark task as completed (root-level field, not in fields). Note: Projects use fields['project.status'] = 'Done' instead."),
|
|
129
|
-
accountabilities: z.array(z.string()).optional().describe("Accountability titles for roles/circles (replaces existing). Only used when updating a role or circle. Requires workspaceId."),
|
|
130
|
-
domains: z.array(z.string()).optional().describe("Domain titles for roles/circles (replaces existing). Only used when updating a role or circle. Requires workspaceId."),
|
|
208
|
+
accountabilities: coerceFromJson(z.array(z.string())).optional().describe("Accountability titles for roles/circles (replaces existing). Only used when updating a role or circle. Requires workspaceId."),
|
|
209
|
+
domains: coerceFromJson(z.array(z.string())).optional().describe("Domain titles for roles/circles (replaces existing). Only used when updating a role or circle. Requires workspaceId."),
|
|
131
210
|
workspaceId: z.string().optional().describe("Workspace ID. Required when updating accountabilities or domains on roles/circles."),
|
|
132
211
|
}),
|
|
133
212
|
deleteNest: z.object({
|
|
@@ -135,11 +214,11 @@ export const schemas = {
|
|
|
135
214
|
}),
|
|
136
215
|
addComment: z.object({
|
|
137
216
|
nestId: z.string().describe("Nest ID to comment on"),
|
|
138
|
-
body: z.string().describe("Comment text (supports HTML and @mentions)"),
|
|
217
|
+
body: z.string().describe("Comment text (supports HTML and @mentions: @{userId}, @{email}, @{circle})"),
|
|
139
218
|
}),
|
|
140
219
|
updateComment: z.object({
|
|
141
220
|
commentId: z.string().describe("Comment ID to update"),
|
|
142
|
-
body: z.string().describe("Updated comment text (supports HTML and @mentions)"),
|
|
221
|
+
body: z.string().describe("Updated comment text (supports HTML and @mentions: @{userId}, @{email}, @{circle})"),
|
|
143
222
|
}),
|
|
144
223
|
deleteComment: z.object({
|
|
145
224
|
commentId: z.string().describe("Comment ID to delete"),
|
|
@@ -216,7 +295,7 @@ export const schemas = {
|
|
|
216
295
|
}),
|
|
217
296
|
// Inbox tools (require OAuth token)
|
|
218
297
|
listInbox: z.object({
|
|
219
|
-
completedAfter: z.string().optional().describe("Include completed items from this date (ISO format). If omitted, only non-completed items are returned."),
|
|
298
|
+
completedAfter: z.string().optional().describe("Include completed items from this date (ISO format). If omitted, only non-completed items are returned. For reordering, this default is usually sufficient — nestr_reorder_inbox only requires the IDs of items you want to reposition."),
|
|
220
299
|
}),
|
|
221
300
|
createInboxItem: z.object({
|
|
222
301
|
title: z.string().describe("Title of the inbox item (plain text, HTML stripped)"),
|
|
@@ -230,10 +309,10 @@ export const schemas = {
|
|
|
230
309
|
title: z.string().optional().describe("Updated title (plain text, HTML stripped)"),
|
|
231
310
|
description: z.string().optional().describe("Updated description (supports HTML)"),
|
|
232
311
|
completed: z.boolean().optional().describe("Mark as completed (processed)"),
|
|
233
|
-
data: z.record(z.unknown()).optional().describe("Custom data storage"),
|
|
312
|
+
data: coerceFromJson(z.record(z.unknown())).optional().describe("Custom data storage"),
|
|
234
313
|
}),
|
|
235
314
|
reorderInbox: z.object({
|
|
236
|
-
nestIds: z.array(z.string()).describe("Array of inbox item IDs in the desired order"),
|
|
315
|
+
nestIds: coerceFromJson(z.array(z.string())).describe("Array of inbox item IDs in the desired order"),
|
|
237
316
|
}),
|
|
238
317
|
reorderInboxItem: z.object({
|
|
239
318
|
nestId: z.string().describe("ID of the inbox item to reorder"),
|
|
@@ -250,10 +329,10 @@ export const schemas = {
|
|
|
250
329
|
labelId: z.string().describe("Label ID to remove"),
|
|
251
330
|
}),
|
|
252
331
|
addToDailyPlan: z.object({
|
|
253
|
-
nestIds: z.array(z.string()).describe("Array of nest IDs to add to the daily plan"),
|
|
332
|
+
nestIds: coerceFromJson(z.array(z.string())).describe("Array of nest IDs to add to the daily plan"),
|
|
254
333
|
}),
|
|
255
334
|
removeFromDailyPlan: z.object({
|
|
256
|
-
nestIds: z.array(z.string()).describe("Array of nest IDs to remove from the daily plan"),
|
|
335
|
+
nestIds: coerceFromJson(z.array(z.string())).describe("Array of nest IDs to remove from the daily plan"),
|
|
257
336
|
}),
|
|
258
337
|
// Personal labels (require OAuth token)
|
|
259
338
|
listPersonalLabels: z.object({}),
|
|
@@ -271,7 +350,7 @@ export const schemas = {
|
|
|
271
350
|
}),
|
|
272
351
|
bulkReorder: z.object({
|
|
273
352
|
workspaceId: z.string().describe("Workspace ID"),
|
|
274
|
-
nestIds: z.array(z.string()).describe("Array of nest IDs in the desired order"),
|
|
353
|
+
nestIds: coerceFromJson(z.array(z.string())).describe("Array of nest IDs in the desired order"),
|
|
275
354
|
}),
|
|
276
355
|
// Daily plan (requires OAuth token)
|
|
277
356
|
getDailyPlan: z.object({}),
|
|
@@ -334,14 +413,14 @@ export const schemas = {
|
|
|
334
413
|
tensionId: z.string().describe("Tension ID"),
|
|
335
414
|
_id: z.string().optional().describe("ID of an existing governance item to change or remove. Omit to propose a new item."),
|
|
336
415
|
title: z.string().optional().describe("Title for the governance item"),
|
|
337
|
-
labels: z.array(z.string()).optional().describe("Labels defining the item type (e.g., ['role'], ['circle'], ['policy'], ['accountability'], ['domain'])"),
|
|
416
|
+
labels: coerceFromJson(z.array(z.string())).optional().describe("Labels defining the item type (e.g., ['role'], ['circle'], ['policy'], ['accountability'], ['domain'])"),
|
|
338
417
|
purpose: z.string().optional().describe("Purpose — aspirational future state. Most important for roles/circles where it defines the north star. Supports HTML."),
|
|
339
418
|
description: z.string().optional().describe("Description — detailed information about the item. Supports HTML."),
|
|
340
419
|
parentId: z.string().optional().describe("Parent ID — use to move/restructure items (e.g., move role to different circle)"),
|
|
341
|
-
users: z.array(z.string()).optional().describe("User IDs to assign (e.g., for role elections: assign the elected user to the role)"),
|
|
420
|
+
users: coerceFromJson(z.array(z.string())).optional().describe("User IDs to assign (e.g., for role elections: assign the elected user to the role)"),
|
|
342
421
|
due: z.string().optional().describe("Due date / re-election date (ISO format)"),
|
|
343
|
-
accountabilities: z.array(z.string()).optional().describe("Accountability titles to set on a role (replaces all — use children endpoint for individual management)"),
|
|
344
|
-
domains: z.array(z.string()).optional().describe("Domain titles to set on a role (replaces all — use children endpoint for individual management)"),
|
|
422
|
+
accountabilities: coerceFromJson(z.array(z.string())).optional().describe("Accountability titles to set on a role (replaces all — use children endpoint for individual management)"),
|
|
423
|
+
domains: coerceFromJson(z.array(z.string())).optional().describe("Domain titles to set on a role (replaces all — use children endpoint for individual management)"),
|
|
345
424
|
}),
|
|
346
425
|
modifyTensionPart: z.object({
|
|
347
426
|
nestId: z.string().describe("ID of the circle or role the tension belongs to"),
|
|
@@ -350,12 +429,12 @@ export const schemas = {
|
|
|
350
429
|
title: z.string().optional().describe("Updated title"),
|
|
351
430
|
purpose: z.string().optional().describe("Updated purpose — aspirational future state. Most important for roles/circles. Supports HTML."),
|
|
352
431
|
description: z.string().optional().describe("Updated description — detailed information. Supports HTML."),
|
|
353
|
-
labels: z.array(z.string()).optional().describe("Updated labels"),
|
|
432
|
+
labels: coerceFromJson(z.array(z.string())).optional().describe("Updated labels"),
|
|
354
433
|
parentId: z.string().optional().describe("Updated parent ID"),
|
|
355
|
-
users: z.array(z.string()).optional().describe("Updated user assignments"),
|
|
434
|
+
users: coerceFromJson(z.array(z.string())).optional().describe("Updated user assignments"),
|
|
356
435
|
due: z.string().optional().describe("Updated due date (ISO format)"),
|
|
357
|
-
accountabilities: z.array(z.string()).optional().describe("Updated accountabilities (replaces all — use children endpoint for individual management)"),
|
|
358
|
-
domains: z.array(z.string()).optional().describe("Updated domains (replaces all — use children endpoint for individual management)"),
|
|
436
|
+
accountabilities: coerceFromJson(z.array(z.string())).optional().describe("Updated accountabilities (replaces all — use children endpoint for individual management)"),
|
|
437
|
+
domains: coerceFromJson(z.array(z.string())).optional().describe("Updated domains (replaces all — use children endpoint for individual management)"),
|
|
359
438
|
}),
|
|
360
439
|
removeTensionPart: z.object({
|
|
361
440
|
nestId: z.string().describe("ID of the circle or role the tension belongs to"),
|
|
@@ -372,7 +451,7 @@ export const schemas = {
|
|
|
372
451
|
tensionId: z.string().describe("Tension ID"),
|
|
373
452
|
partId: z.string().describe("Part ID"),
|
|
374
453
|
title: z.string().describe("Title for the new accountability or domain"),
|
|
375
|
-
labels: z.array(z.string()).describe("Labels defining the type: ['accountability'] or ['domain']"),
|
|
454
|
+
labels: coerceFromJson(z.array(z.string())).describe("Labels defining the type: ['accountability'] or ['domain']"),
|
|
376
455
|
}),
|
|
377
456
|
updateTensionPartChild: z.object({
|
|
378
457
|
nestId: z.string().describe("ID of the circle or role the tension belongs to"),
|
|
@@ -493,7 +572,27 @@ export const toolDefinitions = [
|
|
|
493
572
|
},
|
|
494
573
|
{
|
|
495
574
|
name: "nestr_search",
|
|
496
|
-
description:
|
|
575
|
+
description: `Search for nests within a workspace. IMPORTANT: Use completed:false when searching for work to exclude old completed items.
|
|
576
|
+
|
|
577
|
+
Operators (combine with spaces for AND; commas within operator for OR; ! prefix for negation):
|
|
578
|
+
- label:role / label:!project — Filter/exclude by label
|
|
579
|
+
- parent-label:circle — Parent has this label
|
|
580
|
+
- assignee:me / assignee:userId / assignee:none / assignee:!userId — Filter by assignee
|
|
581
|
+
- completed:false / completed:true / completed:past_7_days / completed:this_month / completed:YYYY-MM-DD_YYYY-MM-DD
|
|
582
|
+
- in:nestId — Scope to descendants of a specific nest
|
|
583
|
+
- depth:1 / depth:2 — Limit depth (1=direct children, 2=children+grandchildren)
|
|
584
|
+
- mindepth:N — Minimum depth from context
|
|
585
|
+
- has:due / has:pastdue / has:children / has:incompletechildren (supports ! prefix)
|
|
586
|
+
- project->status:Current,Future / project->status:!Done — Field value search (label->field:value)
|
|
587
|
+
- fields.label.property:value — Search any field value (supports partial match)
|
|
588
|
+
- data.property:value — Search data properties
|
|
589
|
+
- updated-date:past_7_days / updated-date:!past_30_days — Filter by update recency
|
|
590
|
+
- sort:title / sort:due / sort:updatedAt / sort:createdAt + sort-order:asc/desc
|
|
591
|
+
- createdby:me / admin:me / type:comment / limit:N / template:id
|
|
592
|
+
|
|
593
|
+
Examples: label:role → all roles | assignee:me completed:false → my active work | in:circleId label:role depth:1 → roles in circle | label:project project->status:Current → active projects | in:roleId label:project → role's projects | has:pastdue completed:false → overdue items | label:accountability customer → accountabilities matching keyword
|
|
594
|
+
|
|
595
|
+
Response includes meta.total showing total matching count. IMPORTANT UI RULE: The completable list app must ONLY be used when results are confirmed to contain completable items (tasks, projects, todos) AND there are results to show. When searching for roles, circles, metrics, policies, or any non-completable type, you MUST omit the _listTitle parameter and respond in plain text instead. Never render empty results in the app.`,
|
|
497
596
|
inputSchema: {
|
|
498
597
|
type: "object",
|
|
499
598
|
properties: {
|
|
@@ -506,7 +605,8 @@ export const toolDefinitions = [
|
|
|
506
605
|
},
|
|
507
606
|
required: ["workspaceId", "query"],
|
|
508
607
|
},
|
|
509
|
-
_meta: completableListUi,
|
|
608
|
+
// No _meta: completableListUi — search returns all types of nests (roles, circles, etc.).
|
|
609
|
+
// The completable list app should only be used when results are confirmed to be completable items.
|
|
510
610
|
...readOnly,
|
|
511
611
|
},
|
|
512
612
|
{
|
|
@@ -539,12 +639,13 @@ export const toolDefinitions = [
|
|
|
539
639
|
},
|
|
540
640
|
required: ["nestId"],
|
|
541
641
|
},
|
|
542
|
-
_meta: completableListUi,
|
|
642
|
+
// No _meta: completableListUi — children can be any type (roles, accountabilities, etc.).
|
|
643
|
+
// The completable list app should only be used when results are confirmed to be completable items.
|
|
543
644
|
...readOnly,
|
|
544
645
|
},
|
|
545
646
|
{
|
|
546
647
|
name: "nestr_create_nest",
|
|
547
|
-
description: "Create a new nest (task, project, role, circle, etc.) under a parent. Set users to assign to people - placing under a role does NOT auto-assign. For roles and circles: include accountabilities and domains arrays to create them inline (requires workspaceId). The API auto-routes to the self-organization endpoint when governance labels are detected with accountabilities/domains.",
|
|
648
|
+
description: "Create a new nest (task, project, role, circle, etc.) under a parent. Set users to assign to people - placing under a role does NOT auto-assign. For roles and circles: include accountabilities and domains arrays to create them inline (requires workspaceId). The API auto-routes to the self-organization endpoint when governance labels are detected with accountabilities/domains. GOVERNANCE RULE: Only create roles, circles, accountabilities, domains, or policies directly during workspace/circle setup mode (new or sparsely populated workspace). In established workspaces with multiple users, governance changes MUST go through the tension/proposal flow (nestr_create_tension + nestr_add_tension_part) so circle members can consent.",
|
|
548
649
|
inputSchema: {
|
|
549
650
|
type: "object",
|
|
550
651
|
properties: {
|
|
@@ -583,7 +684,7 @@ export const toolDefinitions = [
|
|
|
583
684
|
},
|
|
584
685
|
{
|
|
585
686
|
name: "nestr_update_nest",
|
|
586
|
-
description: "Update properties of an existing nest. Use parentId to move a nest (e.g., inbox item to a project). For roles and circles: include accountabilities and domains arrays to update them inline (requires workspaceId). For AI knowledge persistence, create skill-labeled nests under roles/circles instead of using data fields.",
|
|
687
|
+
description: "Update properties of an existing nest. Use parentId to move a nest (e.g., inbox item to a project). For roles and circles: include accountabilities and domains arrays to update them inline (requires workspaceId). For AI knowledge persistence, create skill-labeled nests under roles/circles instead of using data fields. GOVERNANCE RULE: Only modify governance items (roles, circles, accountabilities, domains, policies) directly during setup mode. In established workspaces, governance changes MUST go through tensions (nestr_create_tension + nestr_add_tension_part).",
|
|
587
688
|
inputSchema: {
|
|
588
689
|
type: "object",
|
|
589
690
|
properties: {
|
|
@@ -639,7 +740,7 @@ export const toolDefinitions = [
|
|
|
639
740
|
},
|
|
640
741
|
{
|
|
641
742
|
name: "nestr_delete_nest",
|
|
642
|
-
description: "Delete a nest (use with caution)",
|
|
743
|
+
description: "Delete a nest (use with caution). GOVERNANCE RULE: To remove governance items (roles, accountabilities, domains, policies) in established workspaces, use nestr_remove_tension_part to propose deletion through the consent process instead.",
|
|
643
744
|
inputSchema: {
|
|
644
745
|
type: "object",
|
|
645
746
|
properties: {
|
|
@@ -651,12 +752,12 @@ export const toolDefinitions = [
|
|
|
651
752
|
},
|
|
652
753
|
{
|
|
653
754
|
name: "nestr_add_comment",
|
|
654
|
-
description: "Add a comment to a nest.
|
|
755
|
+
description: "Add a comment to a nest. Supports @mentions using the format @{userId|email|circle|everyone}: @{userId} mentions by user ID, @{email} mentions by any email the user is registered with in Nestr, @{circle} notifies all role fillers in the nearest ancestor circle, @{everyone} is available in the UI but not yet via the API.",
|
|
655
756
|
inputSchema: {
|
|
656
757
|
type: "object",
|
|
657
758
|
properties: {
|
|
658
759
|
nestId: { type: "string", description: "Nest ID to comment on" },
|
|
659
|
-
body: { type: "string", description: "Comment text (supports HTML and @mentions)" },
|
|
760
|
+
body: { type: "string", description: "Comment text (supports HTML and @mentions: @{userId}, @{email}, @{circle})" },
|
|
660
761
|
},
|
|
661
762
|
required: ["nestId", "body"],
|
|
662
763
|
},
|
|
@@ -664,12 +765,12 @@ export const toolDefinitions = [
|
|
|
664
765
|
},
|
|
665
766
|
{
|
|
666
767
|
name: "nestr_update_comment",
|
|
667
|
-
description: "Update an existing comment's text.",
|
|
768
|
+
description: "Update an existing comment's text. Supports @mentions using the format @{userId|email|circle|everyone}: @{userId} mentions by user ID, @{email} mentions by any email the user is registered with in Nestr, @{circle} notifies all role fillers in the nearest ancestor circle, @{everyone} is available in the UI but not yet via the API.",
|
|
668
769
|
inputSchema: {
|
|
669
770
|
type: "object",
|
|
670
771
|
properties: {
|
|
671
772
|
commentId: { type: "string", description: "Comment ID to update" },
|
|
672
|
-
body: { type: "string", description: "Updated comment text (supports HTML and @mentions)" },
|
|
773
|
+
body: { type: "string", description: "Updated comment text (supports HTML and @mentions: @{userId}, @{email}, @{circle})" },
|
|
673
774
|
},
|
|
674
775
|
required: ["commentId", "body"],
|
|
675
776
|
},
|
|
@@ -901,7 +1002,7 @@ export const toolDefinitions = [
|
|
|
901
1002
|
inputSchema: {
|
|
902
1003
|
type: "object",
|
|
903
1004
|
properties: {
|
|
904
|
-
completedAfter: { type: "string", description: "Include completed items from this date (ISO format). If omitted, only non-completed items are returned." },
|
|
1005
|
+
completedAfter: { type: "string", description: "Include completed items from this date (ISO format). If omitted, only non-completed items are returned. For reordering, this default is usually sufficient — nestr_reorder_inbox only requires the IDs of items you want to reposition." },
|
|
905
1006
|
stripDescription: { type: "boolean", description: "Set true to strip description fields from response, significantly reducing size." },
|
|
906
1007
|
},
|
|
907
1008
|
},
|
|
@@ -952,7 +1053,7 @@ export const toolDefinitions = [
|
|
|
952
1053
|
},
|
|
953
1054
|
{
|
|
954
1055
|
name: "nestr_reorder_inbox",
|
|
955
|
-
description: "Reorder inbox items by providing an array of item IDs in the desired order. You can provide a subset of items
|
|
1056
|
+
description: "Reorder inbox items by providing an array of item IDs in the desired order. You can provide a subset of items — they will be placed at the top in the given order, while all other items retain their existing order below them. To move non-completed items to the top, simply pass their IDs — there is no need to fetch completed items. Requires OAuth token.",
|
|
956
1057
|
inputSchema: {
|
|
957
1058
|
type: "object",
|
|
958
1059
|
properties: {
|
|
@@ -1548,7 +1649,7 @@ async function _handleToolCall(client, name, args) {
|
|
|
1548
1649
|
fieldsMetaData: parsed.fieldsMetaData,
|
|
1549
1650
|
hints: parsed.hints !== false,
|
|
1550
1651
|
});
|
|
1551
|
-
return formatResult(nest);
|
|
1652
|
+
return formatResult(enrichHints(nest));
|
|
1552
1653
|
}
|
|
1553
1654
|
case "nestr_get_nest_children": {
|
|
1554
1655
|
const parsed = schemas.getNestChildren.parse(args);
|
|
@@ -1558,7 +1659,7 @@ async function _handleToolCall(client, name, args) {
|
|
|
1558
1659
|
cleanText: true,
|
|
1559
1660
|
hints: parsed.hints !== false,
|
|
1560
1661
|
});
|
|
1561
|
-
return formatResult(completableResponse(compactResponse(children), "children", parsed._listTitle || "Sub-items"));
|
|
1662
|
+
return formatResult(completableResponse(compactResponse(enrichHints(children)), "children", parsed._listTitle || "Sub-items"));
|
|
1562
1663
|
}
|
|
1563
1664
|
case "nestr_create_nest": {
|
|
1564
1665
|
const parsed = schemas.createNest.parse(args);
|