@nestr/mcp 0.1.8

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.
@@ -0,0 +1,383 @@
1
+ /**
2
+ * Nestr MCP Server
3
+ * Core server setup and configuration
4
+ */
5
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
6
+ import { ListToolsRequestSchema, CallToolRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
7
+ import { createClientFromEnv } from "./api/client.js";
8
+ import { toolDefinitions, handleToolCall } from "./tools/index.js";
9
+ // Server instructions provide context to AI assistants about what Nestr is and how to use it
10
+ const SERVER_INSTRUCTIONS = `
11
+ Nestr is a work management platform for teams practicing self-organization, Holacracy, Sociocracy, and Teal methodologies.
12
+
13
+ ## Workspace Types
14
+
15
+ Most workspaces are organizational, representing a self-organized team. Check the workspace's labels to determine the type:
16
+
17
+ - **Organizational Workspace** (most common): Has the "anchor-circle" label. The workspace IS the anchor circle of a self-organized team using Holacracy/Sociocracy/Teal governance. Contains sub-circles, roles with accountabilities and domains, and collaborative projects.
18
+ - **Personal Workspace**: No "anchor-circle" label. A personal space where an individual tracks their own work and projects.
19
+
20
+ The specific self-organization methodology is stored in \`workspace.data['self_organisation_type']\` (e.g., "holacracy", "sociocracy", "custom").
21
+
22
+ ## Core Concepts
23
+
24
+ - **Workspace**: Top-level container - either an organization's anchor circle or a personal workspace
25
+ - **Nest**: The universal building block - can be a task, project, role, circle, meeting, or any work item
26
+ - **Circle**: A self-governing team with defined purpose, roles, and accountabilities (Holacracy/Sociocracy concept)
27
+ - **Role**: A set of responsibilities (accountabilities) and decision rights (domains) that a person energizes
28
+ - **Label**: Tags that define what type of nest something is (e.g., "project", "todo", "meeting", "anchor-circle")
29
+
30
+ ## Nest Model Architecture
31
+
32
+ Every nest has these **standard fields**:
33
+ - \`_id\` - Unique identifier
34
+ - \`title\` - Display name
35
+ - \`purpose\` - Why this nest exists (especially important for roles/circles)
36
+ - \`description\` - Detailed description (may contain rich text)
37
+ - \`parentId\` - ID of parent nest
38
+ - \`ancestors\` - Array of ancestor IDs (for hierarchy traversal)
39
+ - \`labels\` - Array of label IDs that define what type this nest is
40
+ - \`fields\` - Label-specific custom fields (see below)
41
+ - \`data\` - Miscellaneous data storage for non-field data (e.g., third-party IDs, integration metadata, custom tracking data)
42
+ - \`due\` - Context-dependent date field:
43
+ - **Project/Task**: Due date
44
+ - **Role**: Re-election date (when the role assignment should be reviewed)
45
+ - **Meeting**: Start date/time
46
+ - \`completed\` - Whether this item is completed (for tasks/projects/meetings etc.)
47
+ - \`users\` - Array of user IDs assigned to this nest
48
+ - \`createdAt\`, \`updatedAt\` - Timestamps
49
+
50
+ ### User Assignment
51
+
52
+ **Important:** When creating tasks or projects, you must explicitly set the \`users\` array to associate work with a person. Placing a nest under a role does NOT automatically assign it to the role filler.
53
+
54
+ \`\`\`json
55
+ {
56
+ "parentId": "roleId",
57
+ "title": "Complete quarterly report",
58
+ "users": ["userId123"]
59
+ }
60
+ \`\`\`
61
+
62
+ - \`users: ["userId"]\` - Assign to specific user(s)
63
+ - \`users: []\` or omitted - Unassigned (valid for work under unfilled roles or shared tasks)
64
+
65
+ **Note:** Accountabilities, domains, and policies never have users assigned - they belong to roles, not people.
66
+
67
+ ### The \`fields\` Property
68
+
69
+ The \`fields\` object holds custom data defined by labels. Fields are **namespaced by the label that defines them**:
70
+
71
+ \`\`\`json
72
+ {
73
+ "fields": {
74
+ "project.status": "Current",
75
+ "role.electable-role": true,
76
+ "metric.frequency": "Weekly",
77
+ "circle.strategy": "Focus on enterprise clients"
78
+ }
79
+ }
80
+ \`\`\`
81
+
82
+ **Key project statuses** (in \`fields['project.status']\`):
83
+ - \`Future\` - Planned but not started
84
+ - \`Current\` - Actively being worked on
85
+ - \`Waiting\` - Blocked or on hold
86
+ - \`Done\` - Completed
87
+
88
+ **Circle strategy** (in \`fields['circle.strategy']\`):
89
+ A strategy that all roles within the circle must follow. Sub-circle strategies must align with and support the super-circle's strategy.
90
+
91
+ **Important:** Label field schemas can be customized at the workspace or circle level. This means the available fields and their options may vary between different parts of the organization hierarchy. Always check what fields are actually present on a nest rather than assuming a fixed schema.
92
+
93
+ ### Hierarchical Purpose
94
+
95
+ The \`purpose\` field follows a strict hierarchy:
96
+ - The **anchor circle's purpose** is the purpose of the entire organization
97
+ - Each **sub-circle's purpose** must contribute to its parent circle's purpose
98
+ - Each **role's purpose** must contribute to its circle's purpose
99
+
100
+ This cascades through the entire hierarchy, which may be many layers deep. When creating or updating purposes, ensure they align with and serve the parent's purpose.
101
+
102
+ ## Best Practices
103
+
104
+ 1. **Start by listing workspaces** to get the workspace ID and check if it has the "anchor-circle" label
105
+ 2. **Use search** to find specific items rather than browsing through hierarchies
106
+ 3. **Check labels** to understand what type of nest you're working with
107
+ 4. **Use @mentions** in comments to notify team members
108
+ 5. **Respect the hierarchy**: nests live under parents (workspace → circle → role/project → task)
109
+ 6. **Check circle strategy and purpose** before creating work or governance:
110
+ - Fetch the parent circle to review its \`purpose\` and \`fields['circle.strategy']\`
111
+ - Ensure new projects and tasks align with and serve the circle's strategy
112
+ - Use strategy and purpose to prioritize work and define clear outcomes
113
+ - When proposing governance changes, consider how they support the circle's purpose
114
+ 7. **Use \`data.botContext\` to maintain AI memory** across sessions:
115
+ - Any nest can store AI context in \`data.botContext\` (markdown format) to persist learned information
116
+ - Update via \`nestr_update_nest\` with \`{ data: { botContext: "# Context\\n\\n..." } }\`
117
+ - Check for existing \`data.botContext\` when working on a nest to pick up prior context
118
+ - **Especially valuable for roles and circles**: Store information relevant to the *role*, not the person filling it (e.g., key contacts, recurring processes, domain knowledge). This context transfers automatically when the role is assigned to a different user.
119
+ - Enables future agentic work: AI agents can autonomously energize roles, maintaining continuity as they learn preferences, make decisions, and accumulate role-specific knowledge over time
120
+
121
+ ## Important Labels
122
+
123
+ Labels define what type a nest is. The API strips the "circleplus-" prefix, so use labels without it.
124
+
125
+ **Governance Structure:**
126
+ - \`anchor-circle\` - The workspace itself when it's an organization
127
+ - \`circle\` - A sub-circle/team within the organization
128
+ - \`role\` - A role with accountabilities and domains
129
+ - \`accountability\` - An ongoing activity the role is responsible for performing (Holacracy: "an ongoing activity that the Role will enact")
130
+ - \`domain\` - An area the role has exclusive control over; others must get permission to impact it (Holacracy: "something the Role may exclusively control on behalf of the Organization")
131
+ - \`policy\` - A grant or restriction of authority affecting how others interact with a domain or process. Can live on a domain, role, or circle directly.
132
+
133
+ **Note:** Accountabilities, domains, and policies are child-nests of roles/circles. Use \`nestr_get_circle_roles\` or \`nestr_get_nest_children\` to retrieve them. The generic \`nestr_search\` won't return them by default.
134
+
135
+ **Meetings & Operations:**
136
+ - \`metric\` - A metric tracked by a role/circle
137
+ - \`checklist\` - A recurring checklist item
138
+ - \`governance\` - A governance meeting
139
+ - \`tactical\` - A tactical/operational meeting
140
+
141
+ **OKRs & Goals:**
142
+ - \`goal\` - An Objective (the O in OKR)
143
+ - \`result\` - A Key Result (the KR in OKR)
144
+
145
+ **Work Tracking:**
146
+ - \`project\` - An outcome requiring multiple steps to complete. Define in past tense as what "done" looks like (e.g., "Website redesign launched", "Q1 report published"). Has status: Future/Current/Waiting/Done.
147
+ - *(no label)* - A nest without labels is a todo/action: a single, concrete action that can be done in one sitting (e.g., "Call supplier about pricing", "Draft intro paragraph"). The next physical step to move something forward.
148
+ - \`note\` - A simple note
149
+ - \`meeting\` - A calendar meeting
150
+ - \`prepared-tension\` - A tension (gap between current and desired state). Used for meeting agenda items, async governance proposals, and general tension processing. Central to Holacracy practice.
151
+
152
+ ## Search Query Syntax
153
+
154
+ The \`nestr_search\` tool supports powerful query operators. Combine multiple operators with spaces (AND logic) or use commas within an operator (OR logic).
155
+
156
+ ### Common Search Operators
157
+
158
+ | Operator | Example | Description |
159
+ |----------|---------|-------------|
160
+ | \`label:\` | \`label:role\` | Filter by label type |
161
+ | \`label:!\` | \`label:!project\` | Exclude label |
162
+ | \`assignee:\` | \`assignee:me\` | Filter by assignee (use \`me\` for current user) |
163
+ | \`completed:\` | \`completed:false\` | Filter by completion status |
164
+ | \`has:\` | \`has:due\` | Items with a property (due, children, etc.) |
165
+ | \`depth:\` | \`depth:1\` | Limit search depth (1 = direct children only) |
166
+ | \`createdby:\` | \`createdby:me\` | Filter by creator |
167
+
168
+ ### Field Value Search
169
+
170
+ Search by label-specific field values using \`label->field:value\`:
171
+ - \`project->status:Current\` - Projects with status "Current"
172
+ - \`project->status:Current,Future\` - Status is Current OR Future
173
+ - \`project->status:!Done\` - Status is NOT Done
174
+
175
+ ### Search Examples
176
+
177
+ \`\`\`
178
+ label:role
179
+ -> Find all roles
180
+
181
+ label:project assignee:me completed:false
182
+ -> My incomplete projects
183
+
184
+ label:project project->status:Current
185
+ -> Projects with status "Current"
186
+
187
+ label:circle depth:1
188
+ -> Direct sub-circles only
189
+
190
+ has:due completed:false
191
+ -> Incomplete items with due dates
192
+
193
+ label:meeting has:!completed
194
+ -> Meetings not yet completed
195
+
196
+ label:policy spending
197
+ -> Policies mentioning spending
198
+
199
+ label:policy budget cost expense
200
+ -> Policies about budgets, costs, or expenses
201
+
202
+ label:accountability customer
203
+ -> Accountabilities related to customers
204
+ \`\`\`
205
+
206
+ ### Additional Operators
207
+
208
+ - \`parent-label:circle\` - Items under a circle
209
+ - \`in:nestId\` - Search within specific nest
210
+ - \`updated-date:past_7_days\` - Recently updated (also: past_30_days, this_month, etc.)
211
+ - \`type:comment\` - Search comments/posts
212
+ - \`deleted:true\` - Include deleted items
213
+
214
+ ## Linking to the Web App
215
+
216
+ When sharing results with users, provide clickable links to the Nestr web app.
217
+
218
+ **Base URL:** \`https://app.nestr.io\`
219
+
220
+ ### Link Formats
221
+
222
+ | Format | Example | Use Case |
223
+ |--------|---------|----------|
224
+ | \`/n/{nestId}\` | \`/n/abc123\` | Direct link to any nest |
225
+ | \`/n/{nestId}/{childId}\` | \`/n/circleId/roleId\` | Show child in context (opens detail pane on desktop) |
226
+ | \`/n/{workspaceId}?s=1#hash\` | \`/n/wsId?s=1#users\` | Workspace admin settings |
227
+
228
+ ### Context Links (Detail Pane)
229
+
230
+ Use the two-ID format to show items in context:
231
+ - \`/n/{circleId}/{roleId}\` - Role within its circle
232
+ - \`/n/{roleId}/{projectId}\` - Project owned by a role
233
+ - \`/n/{projectId}/{taskId}\` - Task within its project
234
+
235
+ ### Cross-Workspace Views
236
+
237
+ These pages show items across all workspaces for the current user:
238
+ - \`/roles\` - All roles this user fills
239
+ - \`/projects\` - All projects assigned to this user
240
+
241
+ ### User Profile
242
+
243
+ View a user's roles within a specific workspace:
244
+ - \`/profile/{userId}?cId={workspaceId}\` - User's roles in that workspace
245
+
246
+ This works for viewing colleagues too - replace userId to see what roles they fill in the same workspace.
247
+
248
+ ### Admin Settings Hashes
249
+
250
+ For workspace admins, link to settings with \`/n/{workspaceId}?s=1\` plus:
251
+ - \`#users\` - Team members
252
+ - \`#labels\` - Label configuration
253
+ - \`#workspace-apps\` - Enabled apps/features
254
+ - \`#plan\` - Subscription plan
255
+
256
+ ## Inbox (Quick Capture)
257
+
258
+ The inbox is a collection point for capturing "stuff" that still needs processing. Use it for:
259
+ - Quick capture of thoughts, ideas, or tasks without deciding where they belong
260
+ - Collecting items that need clarification before becoming projects or actions
261
+ - Temporary holding area before organizing into the proper location
262
+
263
+ **Note:** Inbox tools require OAuth authentication (user-scoped token). They won't work with workspace API keys.
264
+
265
+ ### Inbox Workflow
266
+
267
+ 1. **Capture**: Use \`nestr_create_inbox_item\` to quickly add items without organizing
268
+ 2. **Review**: Use \`nestr_list_inbox\` to see items needing processing
269
+ 3. **Process**: For each item, decide:
270
+ - **Delete**: If not needed, mark \`completed: true\`
271
+ - **Do it**: If quick (<2 min), do it now and mark complete
272
+ - **Organize**: Move to appropriate location with \`nestr_update_nest\` by setting \`parentId\`
273
+
274
+ ### Moving Items Out of Inbox
275
+
276
+ To clarify/organize an inbox item, use \`nestr_update_nest\` to change its \`parentId\`:
277
+
278
+ \`\`\`json
279
+ {
280
+ "nestId": "inboxItemId",
281
+ "parentId": "projectOrRoleId"
282
+ }
283
+ \`\`\`
284
+
285
+ This moves the item from inbox to the specified project, role, or other location. A nest without any labels is the basic building block - it's completable and acts as a todo/action item.
286
+
287
+ ## Common Workflows
288
+
289
+ - **Task Management**: Create nests (no label needed for basic todos), update completed status, add comments for updates
290
+ - **Project Tracking**: List projects, get children to see tasks, check insights for metrics
291
+ - **Team Structure**: List circles to see teams, get roles to understand accountabilities and domains
292
+ - **Finding Accountabilities/Domains**: Use \`nestr_get_circle_roles\` for a circle's roles with their accountabilities, or \`nestr_get_nest_children\` on a specific role
293
+ - **Search & Discovery**: Use search with operators like \`label:role\` or \`assignee:me completed:false\`
294
+ - **Quick Capture**: Use inbox tools to capture thoughts without organizing, then process later
295
+ `.trim();
296
+ export function createServer(config = {}) {
297
+ const client = config.client || createClientFromEnv();
298
+ const server = new Server({
299
+ name: "nestr-mcp",
300
+ version: "0.1.0",
301
+ description: "Manage tasks, projects, roles, and circles for self-organizing teams. Built for Holacracy, Sociocracy, and Teal organizations practicing role-based governance and distributed authority. AI-native tool for the future of work - automate workflows and run your autonomous team with AI assistants.",
302
+ }, {
303
+ capabilities: {
304
+ tools: {},
305
+ resources: {},
306
+ },
307
+ instructions: SERVER_INSTRUCTIONS,
308
+ });
309
+ // Register tool list handler
310
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
311
+ return { tools: toolDefinitions };
312
+ });
313
+ // Register tool call handler
314
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
315
+ const { name, arguments: args } = request.params;
316
+ return handleToolCall(client, name, args || {});
317
+ });
318
+ // Register resource list handler
319
+ server.setRequestHandler(ListResourcesRequestSchema, async () => {
320
+ return {
321
+ resources: [
322
+ {
323
+ uri: "nestr://workspaces",
324
+ name: "My Workspaces",
325
+ description: "List of Nestr workspaces you have access to",
326
+ mimeType: "application/json",
327
+ },
328
+ ],
329
+ };
330
+ });
331
+ // Register resource read handler
332
+ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
333
+ const { uri } = request.params;
334
+ if (uri === "nestr://workspaces") {
335
+ const workspaces = await client.listWorkspaces({ cleanText: true });
336
+ return {
337
+ contents: [
338
+ {
339
+ uri,
340
+ mimeType: "application/json",
341
+ text: JSON.stringify(workspaces, null, 2),
342
+ },
343
+ ],
344
+ };
345
+ }
346
+ // Handle dynamic workspace resources
347
+ const workspaceMatch = uri.match(/^nestr:\/\/workspace\/([^/]+)\/(.+)$/);
348
+ if (workspaceMatch) {
349
+ const [, workspaceId, resource] = workspaceMatch;
350
+ switch (resource) {
351
+ case "structure": {
352
+ const circles = await client.listCircles(workspaceId, { cleanText: true });
353
+ return {
354
+ contents: [
355
+ {
356
+ uri,
357
+ mimeType: "application/json",
358
+ text: JSON.stringify(circles, null, 2),
359
+ },
360
+ ],
361
+ };
362
+ }
363
+ case "projects": {
364
+ const projects = await client.getWorkspaceProjects(workspaceId, {
365
+ cleanText: true,
366
+ });
367
+ return {
368
+ contents: [
369
+ {
370
+ uri,
371
+ mimeType: "application/json",
372
+ text: JSON.stringify(projects, null, 2),
373
+ },
374
+ ],
375
+ };
376
+ }
377
+ }
378
+ }
379
+ throw new Error(`Unknown resource: ${uri}`);
380
+ });
381
+ return server;
382
+ }
383
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}