@nestr/mcp 0.1.104 → 0.1.106
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/SECURITY.md +97 -0
- package/build/api/client.d.ts +82 -29
- package/build/api/client.d.ts.map +1 -1
- package/build/api/client.js +90 -34
- package/build/api/client.js.map +1 -1
- package/build/api/readonly-bearer.d.ts +13 -0
- package/build/api/readonly-bearer.d.ts.map +1 -0
- package/build/api/readonly-bearer.js +18 -0
- package/build/api/readonly-bearer.js.map +1 -0
- package/build/help/articles.d.ts.map +1 -1
- package/build/help/articles.js +88 -4
- package/build/help/articles.js.map +1 -1
- package/build/help/cross-links.d.ts.map +1 -1
- package/build/help/cross-links.js +4 -1
- package/build/help/cross-links.js.map +1 -1
- package/build/help/topics.d.ts.map +1 -1
- package/build/help/topics.js +356 -22
- package/build/help/topics.js.map +1 -1
- package/build/http.d.ts.map +1 -1
- package/build/http.js +6 -1
- package/build/http.js.map +1 -1
- package/build/server.d.ts.map +1 -1
- package/build/server.js +2 -1
- package/build/server.js.map +1 -1
- package/build/skills/tension-processing.d.ts.map +1 -1
- package/build/skills/tension-processing.js +3 -2
- package/build/skills/tension-processing.js.map +1 -1
- package/build/tools/index.d.ts +1110 -13
- package/build/tools/index.d.ts.map +1 -1
- package/build/tools/index.js +277 -24
- package/build/tools/index.js.map +1 -1
- package/package.json +4 -3
package/build/help/topics.js
CHANGED
|
@@ -15,14 +15,15 @@ The internal topics below are curated MCP-flavoured guidance — tool call patte
|
|
|
15
15
|
- topics: This list
|
|
16
16
|
- operating-modes: Detailed assistant/role-filler/workspace mode behaviors
|
|
17
17
|
- matching-work-to-roles: How to determine which role owns work
|
|
18
|
-
- linking:
|
|
18
|
+
- linking: Linking to an item vs. linking to a view (tab hashes); read before handing anyone a link
|
|
19
19
|
- workspace-types: Holacracy, Sociocracy, and Custom workspace configurations
|
|
20
20
|
- core-concepts: Workspace, nest, circle, role, label basics and content format
|
|
21
21
|
- nest-model: Nest fields, hierarchy, hints, and fieldsMetaData
|
|
22
22
|
- labels: Important labels, label architecture, and field schema customization
|
|
23
|
-
- search: Full search query syntax with all operators and examples
|
|
23
|
+
- search: Full search query syntax with all operators and examples, including strict:true
|
|
24
|
+
- api-surface: nestr_api_spec, for checking whether an endpoint exists at all
|
|
24
25
|
- fields: Adding custom fields to labels when Nestr has no field for something yet
|
|
25
|
-
- web-app-links: URL
|
|
26
|
+
- web-app-links: Every web-app URL shape, the content tab hashes, and the two-pane hash form
|
|
26
27
|
- workspace-settings: Where workspace settings are, every tab, and the link to hand over
|
|
27
28
|
- inbox: Inbox quick capture, processing workflow, and reordering
|
|
28
29
|
- daily-plan: Daily plan usage, scope, and planning workflows
|
|
@@ -35,8 +36,31 @@ The internal topics below are curated MCP-flavoured guidance — tool call patte
|
|
|
35
36
|
- doing-work: How work flows from purpose through strategy to execution
|
|
36
37
|
- tension-processing: Listening for, creating, and processing tensions
|
|
37
38
|
- workspace-setup: Guided setup wizard for new workspaces and circles
|
|
39
|
+
- meetings: Creating and scheduling circle, governance and role meetings, and where a meeting nest belongs
|
|
38
40
|
- scrum: Scrum/Agile workspace app — user stories, sprints, epics, burndown
|
|
39
|
-
- okr: Objectives & Key Results workspace app — goals, key results, contributions
|
|
41
|
+
- okr: Objectives & Key Results workspace app — goals, key results, contributions
|
|
42
|
+
- pricing: What Nestr costs, and the one rule about answering that from memory`,
|
|
43
|
+
"pricing": `### Pricing
|
|
44
|
+
|
|
45
|
+
**Never state a Nestr price, plan limit or included allowance from memory. Not once, not hedged, not "roughly".**
|
|
46
|
+
|
|
47
|
+
This is the single topic where a wrong answer is immediately expensive, immediately checkable, and lands on someone deciding whether to buy. It has already happened: a prospect asked what Nestr costs and was told "starter: environ 99 €/mois, jusqu'à 10 utilisateurs" and "pro: environ 199 €/mois, utilisateurs illimités". Nestr does not sell flat monthly tiers and has no such user caps; the real figures are per seat and roughly a seventh of that. Nothing in the answer was retrieved. Nothing corrected it.
|
|
48
|
+
|
|
49
|
+
**Where the answer actually lives, in order:**
|
|
50
|
+
|
|
51
|
+
1. \`nestr_help({ search: "pricing" })\` → the **pricing-plans-what-you-pay-for** article, which carries the current plan names and per-seat figures. Quote it, and link it.
|
|
52
|
+
2. **https://nestr.io/pricing** — the live price table. Give this link to the person in every pricing answer, whatever else you say. Prices change and the page is the thing that changes with them.
|
|
53
|
+
|
|
54
|
+
**What is stable enough to say in your own words** (the numbers never are):
|
|
55
|
+
|
|
56
|
+
- Billing is **per user, per month**, with a cheaper annual rate. It is not a flat fee per company, and there is no seat cap that forces a plan change.
|
|
57
|
+
- There is a **free tier for a single user**, and paid tiers above it.
|
|
58
|
+
- **A user is someone with login access.** Agents are not seats: putting an agent in five roles adds no cost, because it is one login. See the managing-users article for who counts.
|
|
59
|
+
- **AI work is metered separately as AI credit**, and that is not the same thing as the subscription. Product support questions — how does Nestr work, where is this setting — are free. Work inside a workspace draws credit. A workspace admin manages the balance, auto top-up and the monthly spend limit under Workspace settings → Workspace plan & billing.
|
|
60
|
+
|
|
61
|
+
**If the search returns nothing**, say you would rather not guess at prices, give the https://nestr.io/pricing link, and offer to bring in a human. An unanswered pricing question costs one link. An invented one costs the deal and the trust.
|
|
62
|
+
|
|
63
|
+
**Not a pricing question**: "how do I add a colleague", "extend our plan by one person", "we need another seat". Those are user management — see the managing-users-invitations-permissions article — and answering them with a price list sends someone to buy something they already have.`,
|
|
40
64
|
"operating-modes": `### Mode-Specific Behavior Summary
|
|
41
65
|
|
|
42
66
|
**Role-filler agents should:**
|
|
@@ -109,26 +133,94 @@ When determining work assignments, consider:
|
|
|
109
133
|
3. Are there multiple roles whose accountabilities overlap? Surface this for clarification.`,
|
|
110
134
|
"linking": `## Linking to Nests
|
|
111
135
|
|
|
112
|
-
**
|
|
136
|
+
**Two different questions, two different answers.** Sending someone to an ITEM is not the
|
|
137
|
+
same as sending them to a VIEW, and the link that answers one does not answer the other.
|
|
138
|
+
|
|
139
|
+
| You want them to see | Use |
|
|
140
|
+
|---|---|
|
|
141
|
+
| One item (a project, a role, a todo) | the nest's own \`url\` field |
|
|
142
|
+
| A list or board (the circle's projects, its roles, its meetings) | the CONTAINER's \`tabs\` hint |
|
|
143
|
+
|
|
144
|
+
### Linking to an item
|
|
145
|
+
|
|
146
|
+
Every nest returned by this MCP server carries a precomputed \`url\`. **Use that field. Do not
|
|
147
|
+
assemble one from ids.** It already applies the parent context and the tab hash (below), and
|
|
148
|
+
assembling one by analogy is how a link ends up pointing at the workspace root instead of the
|
|
149
|
+
circle the item actually lives in.
|
|
150
|
+
|
|
151
|
+
The canonical path is \`/n/{nestId}\` — never \`/nests/{nestId}\`, \`/nest/\`, or anything else.
|
|
152
|
+
|
|
153
|
+
### Linking to a view
|
|
154
|
+
|
|
155
|
+
A view is a TAB on a container, and a tab is a hash. Call \`nestr_get_nest\` on the circle or
|
|
156
|
+
role and take the URL from its \`tabs\` hint:
|
|
157
|
+
|
|
158
|
+
\`\`\`json
|
|
159
|
+
{ "type": "tabs", "tabs": [
|
|
160
|
+
{ "id": "projects", "title": "Projects", "parentTab": "Work",
|
|
161
|
+
"url": "https://app.nestr.io/n/circleId#projects" } ] }
|
|
162
|
+
\`\`\`
|
|
163
|
+
|
|
164
|
+
Say "Work > Projects" to the person (\`parentTab\` is the on-screen grouping) and give them
|
|
165
|
+
\`/n/{circleId}#projects\`. The hash is always the leaf \`id\`; \`parentTab\` is never in the URL.
|
|
166
|
+
|
|
167
|
+
Do not read the tab list off \`nestr_help\` or off memory. A workspace can rename Projects,
|
|
168
|
+
hide a tab, or not have one, and only the hint on that nest knows.
|
|
169
|
+
|
|
170
|
+
**Circle board or role board.** Both a circle and a role have a Projects tab, and they answer
|
|
171
|
+
different questions. Pick from what the person actually asked for rather than defaulting to
|
|
172
|
+
the circle.
|
|
173
|
+
|
|
174
|
+
| They asked | Give them |
|
|
175
|
+
|---|---|
|
|
176
|
+
| "what is the team working on", an overview, a status view | \`/n/{circleId}#projects\` — everything in flight across the circle |
|
|
177
|
+
| "my work", "what should I do next", focused work | \`/n/{roleId}#projects\` — only the work that role holds |
|
|
178
|
+
|
|
179
|
+
The circle board is the shared picture and the one to open in a tactical meeting. The role
|
|
180
|
+
board is where a person works: it is already filtered to their accountabilities, so nothing
|
|
181
|
+
on it belongs to someone else. Someone who fills several roles has several boards, one per
|
|
182
|
+
role, which is the point rather than a limitation. Find their roles with
|
|
183
|
+
\`assignee:me label:role\`.
|
|
113
184
|
|
|
114
|
-
|
|
185
|
+
### Why the hash is not optional
|
|
115
186
|
|
|
116
|
-
|
|
187
|
+
**A nest URL with no \`#\` is not a stable link.** The web app remembers the last tab each
|
|
188
|
+
person opened on each nest, in that person's own browser, and a link with no hash lands on
|
|
189
|
+
whatever that happens to be. A first-time visitor gets the container's first tab, which on a
|
|
190
|
+
circle is Structure > About. This is a real failure, not a theoretical one: a new user was
|
|
191
|
+
sent three links to their new project, landed on the anchor circle's governance view every
|
|
192
|
+
time, and concluded Nestr had no boards.
|
|
117
193
|
|
|
118
|
-
|
|
119
|
-
\`https://app.nestr.io/n/{parentId}/{nestId}\`
|
|
120
|
-
2. If the nest has no parent, or its parent is \`inbox\`, fall back to the bare form:
|
|
121
|
-
\`https://app.nestr.io/n/{nestId}\`
|
|
194
|
+
### Two panes, two hashes
|
|
122
195
|
|
|
123
|
-
|
|
196
|
+
On a wide screen \`/n/{leftId}/{rightId}\` shows the left nest with the right one open beside
|
|
197
|
+
it. The hash follows the same shape: \`#{leftTab}/{rightTab}\`.
|
|
124
198
|
|
|
125
|
-
|
|
199
|
+
- \`/n/{circleId}/{projectId}#projects\` — circle on its Projects tab, project open beside it.
|
|
200
|
+
This is what the \`url\` field gives you, and it is almost always what you want.
|
|
201
|
+
- \`/n/{circleId}/{roleId}#roles/projects\` — circle on Roles, and the role beside it opened
|
|
202
|
+
on ITS Projects tab. Rarely needed, but this is how you steer both panes at once.
|
|
203
|
+
- \`/n/{circleId}/{roleId}#_/projects\` — \`_\` in the left slot leaves the left pane's tab
|
|
204
|
+
alone and sets only the right pane's.
|
|
126
205
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
206
|
+
A hash naming a tab the nest does not have is ignored and the nest opens on its default tab,
|
|
207
|
+
so a stale or wrong hash degrades to the hashless behaviour rather than erroring.
|
|
208
|
+
|
|
209
|
+
### Other shapes
|
|
210
|
+
|
|
211
|
+
| Format | Example | Use case |
|
|
212
|
+
|---|---|---|
|
|
213
|
+
| \`/n/{nestId}\` | \`/n/abc123\` | Any nest, no context (always works) |
|
|
214
|
+
| \`/n/{parentId}/{nestId}\` | \`/n/circleId/roleId\` | Item in context (detail pane on desktop) |
|
|
215
|
+
| \`/n/{nestId}#{tab}\` | \`/n/circleId#projects\` | A view on that nest |
|
|
216
|
+
| \`/n/{nestId}?s=1#{tab}\` | \`/n/wsId?s=1#users\` | Settings (see the \`settings\` hint) |
|
|
217
|
+
|
|
218
|
+
Cross-workspace pages for the current user: \`/roles\`, \`/projects\`.
|
|
219
|
+
A user's roles in one workspace: \`/profile/{userId}?cId={workspaceId}\`.
|
|
220
|
+
|
|
221
|
+
Inbox items have no parent context — link them bare as \`/n/{itemId}\`.
|
|
222
|
+
|
|
223
|
+
See \`nestr_help({ topic: "web-app-links" })\` for the full tab and settings hash tables.`,
|
|
132
224
|
"workspace-types": `## Workspace Types
|
|
133
225
|
|
|
134
226
|
Most workspaces are organizational, representing a self-organized team. Check the workspace's labels to determine the type:
|
|
@@ -321,6 +413,7 @@ Each hint object has:
|
|
|
321
413
|
- \`toolCall\` — pre-mapped tool call to drill into the hint: \`{ tool: "nestr_search", params: { workspaceId: "...", query: "..." } }\`. Call the specified tool with the given params to investigate.
|
|
322
414
|
- \`lastPost\` — (comments hints only) ISO timestamp of the most recent comment
|
|
323
415
|
- \`readAt\` — (comments hints only, user-scoped auth only) ISO timestamp of when the user last read comments. Compare \`lastPost > readAt\` to detect unread comments.
|
|
416
|
+
- \`tabs\` — (on the \`tabs\` and \`settings\` hints only) the pages a PERSON can be sent to, each with \`id\`, \`title\` and a ready-made absolute \`url\`. These carry no \`toolCall\` and should not: the answer is a link to hand over, not a call to make.
|
|
324
417
|
|
|
325
418
|
**Available hint types:**
|
|
326
419
|
|
|
@@ -351,6 +444,8 @@ Each hint object has:
|
|
|
351
444
|
| \`project_overdue\` | warning | project | Past due date |
|
|
352
445
|
| \`no_proposed_output\` | suggestion | tension | Tension has no proposed output yet |
|
|
353
446
|
| \`inline_images\` | info | all | Count of images pasted into the text (see below) |
|
|
447
|
+
| \`tabs\` | info | all (single-nest reads) | The nest's own content tabs, each with the URL that opens it |
|
|
448
|
+
| \`settings\` | info | all (single-nest reads) | The settings tabs THIS viewer may open, each with its URL |
|
|
354
449
|
|
|
355
450
|
Example response with hints:
|
|
356
451
|
\`\`\`json
|
|
@@ -374,6 +469,37 @@ Example response with hints:
|
|
|
374
469
|
}
|
|
375
470
|
\`\`\`
|
|
376
471
|
|
|
472
|
+
**Hints have a level, and a listing should not ask for the expensive one.** \`hints\` takes
|
|
473
|
+
\`'full'\`, \`'summary'\` or \`false\`. \`summary\` keeps everything that varies per nest — type,
|
|
474
|
+
severity, count, url, and the \`query\` below — and drops the two things identical for every
|
|
475
|
+
nest of that type: \`detail\`, the teaching paragraph, and \`endpoints\`. A page of fifty nests
|
|
476
|
+
at \`full\` carries fifty copies of one paragraph; \`nestr_help\` carries it once, and
|
|
477
|
+
\`helpTopic\` on the hint names which topic. Single reads default to \`full\`, listings to
|
|
478
|
+
\`summary\`. Narrow further with \`hintTypes\` and \`minSeverity\`.
|
|
479
|
+
|
|
480
|
+
**A hint can tell you how to find every other nest with the same problem.** Where the
|
|
481
|
+
predicate is expressible as a search, the hint carries a \`query\`:
|
|
482
|
+
|
|
483
|
+
\`\`\`json
|
|
484
|
+
{ "type": "project_waiting_no_reason", "severity": "warning",
|
|
485
|
+
"query": {
|
|
486
|
+
"search": "label:project fields.project.status:Waiting fields.project.waiting_reason:!exists",
|
|
487
|
+
"scope": "<circleId>", "url": "https://.../api/nests/<circleId>/search?search=...",
|
|
488
|
+
"exact": false,
|
|
489
|
+
"caveat": "A reason saved as empty markup triggers the hint but is not matched by !exists." } }
|
|
490
|
+
\`\`\`
|
|
491
|
+
|
|
492
|
+
Read \`exact\` before you trust a count from it. \`true\` means the search reproduces the hint's
|
|
493
|
+
predicate; \`false\` means it gets close and \`caveat\` says which way it is wrong. Where the
|
|
494
|
+
predicate cannot be expressed at all there is no \`query\` — deliberately, because a search
|
|
495
|
+
that quietly disagrees with its hint is worse than none.
|
|
496
|
+
|
|
497
|
+
**To count rather than to list, use \`nestr_hints_rollup\`.** It returns how many nests under
|
|
498
|
+
a circle or workspace carry each hint type, with a small sample, in one call instead of one
|
|
499
|
+
per nest. Read its \`notComputed\`: it names the types it cannot count in a single query, so a
|
|
500
|
+
type listed there is unknown, not zero. Do not sum counts across types — one nest can carry
|
|
501
|
+
several.
|
|
502
|
+
|
|
377
503
|
**Inline images.** An image pasted into a nest's text is stored as a file and left in the
|
|
378
504
|
content as a markdown reference: \`\`. These are
|
|
379
505
|
deliberately absent from \`nestr_get_nest_files\`, because they belong to the text rather than
|
|
@@ -383,6 +509,24 @@ the reference in the content and call \`nestr_read_file({ nestId, fileId })\`
|
|
|
383
509
|
inline images even though they are not listed. The hint carries no \`toolCall\`: the id belongs
|
|
384
510
|
to a specific reference, so there is no single call to pre-map.
|
|
385
511
|
|
|
512
|
+
**The \`tabs\` hint is the only correct source for an in-app view link.** It is computed per
|
|
513
|
+
nest and per viewer: a workspace can rename Projects to "Stories Board", hide a tab, or not
|
|
514
|
+
have one at all, and the hint already reflects that. It is returned on single-nest reads
|
|
515
|
+
(\`nestr_get_nest\`), not on listings, so when you need to send someone to a VIEW, read the
|
|
516
|
+
CONTAINER — the circle or the role — and take the URL from its \`tabs\` hint.
|
|
517
|
+
|
|
518
|
+
\`\`\`json
|
|
519
|
+
{ "type": "tabs", "count": 15, "tabs": [
|
|
520
|
+
{ "id": "projects", "title": "Projects", "parentTab": "Work",
|
|
521
|
+
"url": "https://app.nestr.io/n/circleId#projects" },
|
|
522
|
+
{ "id": "roles", "title": "Roles", "parentTab": "Structure",
|
|
523
|
+
"url": "https://app.nestr.io/n/circleId#roles" } ] }
|
|
524
|
+
\`\`\`
|
|
525
|
+
|
|
526
|
+
\`parentTab\` ("Work", "Structure", "Communicate") is how the tab bar groups tabs on screen, so
|
|
527
|
+
say "Work > Projects" to a person. It is never part of the URL: the hash is always the leaf
|
|
528
|
+
\`id\`. See \`nestr_help({ topic: "linking" })\`.
|
|
529
|
+
|
|
386
530
|
Use hints to proactively surface issues to the user — for example, when reviewing a circle's roles, hints can reveal which roles need attention without separate queries. Use the \`toolCall\` to drill into any hint directly.`,
|
|
387
531
|
"labels": `## Important Labels
|
|
388
532
|
|
|
@@ -404,7 +548,7 @@ Labels define what type a nest is. The API strips the "circleplus-" prefix, so u
|
|
|
404
548
|
- \`governance\` - Combined with \`meeting\` label to create a governance meeting (processes governance tensions/proposals)
|
|
405
549
|
- \`circle-meeting\` - Combined with \`meeting\` label to create a circle/tactical meeting (processes operational tensions — projects, todos, inter-role requests)
|
|
406
550
|
|
|
407
|
-
**Creating meetings:** A meeting is a nest with \`labels: ["meeting", "governance"]\` or \`labels: ["meeting", "circle-meeting"]\`. Set \`due\` to the meeting start time. Assign all role fillers in the circle to the meeting's \`users\` array — this includes people/agents energizing roles in the circle, plus rep-link and circle-lead roles from sub-circles. Use graph tools (\`nestr_add_graph_link\` with relation \`meeting\`) to link tensions as agenda items. Agenda items that don't originate from a specific role can be created as child nests of the meeting directly.
|
|
551
|
+
**Creating meetings:** See \`nestr_help({ topic: "meetings" })\` for where a meeting nest belongs (an existing circle — the anchor circle counts) and why you must not create a circle to hold one. A meeting is a nest with \`labels: ["meeting", "governance"]\` or \`labels: ["meeting", "circle-meeting"]\`. Set \`due\` to the meeting start time. Assign all role fillers in the circle to the meeting's \`users\` array — this includes people/agents energizing roles in the circle, plus rep-link and circle-lead roles from sub-circles. Use graph tools (\`nestr_add_graph_link\` with relation \`meeting\`) to link tensions as agenda items. Agenda items that don't originate from a specific role can be created as child nests of the meeting directly.
|
|
408
552
|
|
|
409
553
|
**OKRs & Goals:**
|
|
410
554
|
- \`goal\` - An Objective (the O in OKR)
|
|
@@ -580,6 +724,26 @@ The \`nestr_search\` tool supports powerful query operators. Combine multiple op
|
|
|
580
724
|
| \`groupby:\` | \`groupby:parent\` | Group results into sections (app + tabs; inert here) |
|
|
581
725
|
| \`groupbycol:\` | \`groupbycol:project->status\` | Same grouping as board columns — see "Grouping and Layout" below |
|
|
582
726
|
|
|
727
|
+
### Knowing your filter applied: \`strict:true\`
|
|
728
|
+
|
|
729
|
+
An operator this parser does not recognise, an unknown label, or a field filter naming a
|
|
730
|
+
field that does not exist are all **dropped silently**. The search still runs, returns a
|
|
731
|
+
broader result, and nothing says a filter went missing. Fine when browsing, wrong when
|
|
732
|
+
counting: a mistyped filter reads as a real, larger answer.
|
|
733
|
+
|
|
734
|
+
Add \`strict:true\` and any dropped term becomes an error instead:
|
|
735
|
+
|
|
736
|
+
\`\`\`
|
|
737
|
+
label:project fields.project.stauts:Waiting strict:true
|
|
738
|
+
-> rejected, naming the unknown field, instead of returning every project
|
|
739
|
+
\`\`\`
|
|
740
|
+
|
|
741
|
+
\`nestr_search\` also takes \`strict: true\` as a parameter, which appends the operator for you.
|
|
742
|
+
Use it whenever the number matters: counting, reporting, or deciding something on the result.
|
|
743
|
+
|
|
744
|
+
For "does this deployment have X at all", \`strict:true\` answers it for the search language
|
|
745
|
+
and \`nestr_api_spec\` answers it for the HTTP surface.
|
|
746
|
+
|
|
583
747
|
### The \`has:\` Operator
|
|
584
748
|
|
|
585
749
|
The \`has:\` operator checks for property existence. Supports \`!\` prefix for negation (e.g., \`has:!due\`).
|
|
@@ -595,6 +759,10 @@ The \`has:\` operator checks for property existence. Supports \`!\` prefix for n
|
|
|
595
759
|
- \`has:tabs\` - Items with tabs configured
|
|
596
760
|
- \`has:header\` - Items with a header
|
|
597
761
|
|
|
762
|
+
An unlisted value is not an error: \`has:<x>\` falls through to the nest property \`_p.<x>\`,
|
|
763
|
+
so \`has:completable\` (items that can be completed) and \`has:eventable\` both work. The names
|
|
764
|
+
above are the ones with special handling, not the whole set.
|
|
765
|
+
|
|
598
766
|
### Field Value Search
|
|
599
767
|
|
|
600
768
|
Search by label-specific field values using \`label->field:value\`:
|
|
@@ -682,9 +850,20 @@ Both support \`!\` prefix for negation.
|
|
|
682
850
|
|
|
683
851
|
### Additional Operators
|
|
684
852
|
|
|
685
|
-
- \`deleted:true\` - Include deleted items (hidden by default)
|
|
686
853
|
- \`linkeditems:true\` - Items linked to the current context
|
|
687
854
|
|
|
855
|
+
### Deleted Items
|
|
856
|
+
|
|
857
|
+
Deleted items are soft-deleted and stay in the workspace, so they remain findable. Searches exclude them unless one of these operators is present.
|
|
858
|
+
|
|
859
|
+
- \`deleted:true\` - ONLY deleted items, no date limit. This does not mean "live plus deleted"
|
|
860
|
+
- \`deleted:today\`, \`deleted:past_7_days\`, \`deleted:past_30_days\`, \`deleted:past_12_months\` - ONLY items deleted in that window
|
|
861
|
+
- \`include-deleted:<range>\` - live items AND those deleted in the range, in one result set. Same ranges as above, plus \`include-deleted:all\` for no date limit
|
|
862
|
+
|
|
863
|
+
Use \`include-deleted:\` when the user asks "did this ever exist" or "find X, including anything deleted". Use \`deleted:\` when they want the deleted set on its own, for example "what was removed last week".
|
|
864
|
+
|
|
865
|
+
Deleted results come back read-only. **There is no restore tool: restoring is done in the Nestr app.** Point the user at the item's page, where a banner carries a Restore link, and note that restoring requires the delete right on that item. An item deleted as part of its parent is restored by restoring that parent, which brings back everything removed in the same deletion event. See the \`recovering-deleted-items-undo-activity-stream\` help article.
|
|
866
|
+
|
|
688
867
|
### Sorting Results
|
|
689
868
|
|
|
690
869
|
Use \`sort:\` to specify the sort field and \`sort-order:\` to set direction.
|
|
@@ -788,6 +967,24 @@ in:roleId label:project project->status:Current
|
|
|
788
967
|
- All work in a circle: \`in:circleId completed:false\` (includes all nested items)
|
|
789
968
|
- Direct tasks under a project: \`in:projectId depth:1 completed:false\`
|
|
790
969
|
|
|
970
|
+
**Two ways to scope, and when to use which.** \`in:nestId\` scopes a workspace-wide
|
|
971
|
+
\`nestr_search\`. \`nestr_get_nest_children\` takes a \`search\` that is already scoped to that
|
|
972
|
+
nest, so the same question is shorter:
|
|
973
|
+
|
|
974
|
+
\`\`\`
|
|
975
|
+
nestr_search({ query: "in:circleId label:role depth:1" })
|
|
976
|
+
nestr_get_nest_children({ nestId: circleId, search: "label:role" })
|
|
977
|
+
-> the same roles
|
|
978
|
+
\`\`\`
|
|
979
|
+
|
|
980
|
+
The children route applies \`depth:1\` when your query sets no depth, and says so in
|
|
981
|
+
\`appliedDefaults\` on the response, which also names how to widen it. Reach for it when you
|
|
982
|
+
already hold the nest id; reach for \`in:\` when you are composing one query across several
|
|
983
|
+
scopes.
|
|
984
|
+
|
|
985
|
+
Either way, ask for the subset you want rather than fetching a mixed set and sorting it
|
|
986
|
+
out afterwards. "What type is this nest" is a question you can avoid asking.
|
|
987
|
+
|
|
791
988
|
### Filtering by Completion Status
|
|
792
989
|
|
|
793
990
|
**Important:** When fetching work items (tasks, projects), always use \`completed:false\` unless you specifically need completed items. This avoids cluttering results with old completed work.
|
|
@@ -860,8 +1057,59 @@ When sharing results with users, provide clickable links to the Nestr web app.
|
|
|
860
1057
|
|--------|---------|----------|
|
|
861
1058
|
| \`/n/{nestId}\` | \`/n/abc123\` | Direct link to any nest |
|
|
862
1059
|
| \`/n/{nestId}/{childId}\` | \`/n/circleId/roleId\` | Show child in context (opens detail pane on desktop) |
|
|
1060
|
+
| \`/n/{nestId}#{tab}\` | \`/n/circleId#projects\` | A specific tab (view) on that nest |
|
|
1061
|
+
| \`/n/{leftId}/{rightId}#{left}/{right}\` | \`/n/circleId/roleId#roles/projects\` | A tab on each pane |
|
|
863
1062
|
| \`/n/{workspaceId}?s=1#hash\` | \`/n/wsId?s=1#users\` | Workspace admin settings |
|
|
864
1063
|
|
|
1064
|
+
### Content Tab Hashes
|
|
1065
|
+
|
|
1066
|
+
A tab on a nest is a hash on that nest's own URL. **Read the real list off the nest's
|
|
1067
|
+
\`tabs\` hint (\`nestr_get_nest\`), because a workspace can rename or hide any of them.** These
|
|
1068
|
+
are the defaults a circle or role ships with:
|
|
1069
|
+
|
|
1070
|
+
| Hash | Tab | Group |
|
|
1071
|
+
|---|---|---|
|
|
1072
|
+
| \`#about\` | About | Structure |
|
|
1073
|
+
| \`#roles\` | Roles | Structure |
|
|
1074
|
+
| \`#policies\` | Domains & Policies | Structure |
|
|
1075
|
+
| \`#skills\` | Skills | Structure |
|
|
1076
|
+
| \`#goals\` | Goals | Work |
|
|
1077
|
+
| \`#projects\` | Projects | Work |
|
|
1078
|
+
| \`#tasks\` | Todos | Work |
|
|
1079
|
+
| \`#meetings\` | Meetings | Work |
|
|
1080
|
+
| \`#metrics\` | Metrics | Work |
|
|
1081
|
+
| \`#checklists\` | Checklist | Work |
|
|
1082
|
+
| \`#feedback\` | Feedback | Work |
|
|
1083
|
+
| \`#feed\` | Feed | Communicate |
|
|
1084
|
+
| \`#notes\` | Notes | Communicate |
|
|
1085
|
+
|
|
1086
|
+
The Group column is the \`parentTab\` the tab bar shows on screen, so say "Work > Projects"
|
|
1087
|
+
to a person. It is NEVER part of the hash: the hash is always the leaf id.
|
|
1088
|
+
|
|
1089
|
+
**\`#projects\` already IS the board.** The Projects tab defaults to columns grouped by project
|
|
1090
|
+
status (Future / Current / In Review / Waiting / Done) on desktop, and to a flat list on
|
|
1091
|
+
mobile. So a person asking for "a board" needs the link and nothing else — do not send them
|
|
1092
|
+
hunting for a board icon or a view toggle. (Elsewhere in Nestr a list does start flat and
|
|
1093
|
+
Group by is a per-browser setting; the Projects tab is the exception because its tab
|
|
1094
|
+
definition sets the column view as its default.)
|
|
1095
|
+
|
|
1096
|
+
**Circles and roles both have one, and they are for different things.**
|
|
1097
|
+
\`/n/{circleId}#projects\` is the team's board: everything in flight across the circle, the
|
|
1098
|
+
shared picture, the one to open in a tactical meeting. \`/n/{roleId}#projects\` is one role's
|
|
1099
|
+
board: already filtered to that role's accountabilities, which makes it the place to do
|
|
1100
|
+
focused work. Someone who fills three roles has three boards. Neither is the "real" one —
|
|
1101
|
+
choose by what was asked for.
|
|
1102
|
+
|
|
1103
|
+
### Two Panes, Two Hashes
|
|
1104
|
+
|
|
1105
|
+
\`/n/{leftId}/{rightId}\` shows the left nest with the right one open beside it, and the hash
|
|
1106
|
+
splits the same way: \`#{leftTab}/{rightTab}\`. Most links only need the left half, because
|
|
1107
|
+
the right pane is usually an item rather than a view. \`_\` in the left slot (\`#_/projects\`)
|
|
1108
|
+
means "leave the left pane's tab alone and set only the right".
|
|
1109
|
+
|
|
1110
|
+
A hash naming a tab the nest does not have is ignored, and the nest opens on its default
|
|
1111
|
+
tab — the same thing a hashless link does.
|
|
1112
|
+
|
|
865
1113
|
### Context Links (Detail Pane)
|
|
866
1114
|
|
|
867
1115
|
Use the two-ID format to show items in context:
|
|
@@ -896,7 +1144,8 @@ For workspace admins, link to settings with \`/n/{workspaceId}?s=1\` plus:
|
|
|
896
1144
|
|
|
897
1145
|
The same \`?s=1#labels\` opens a CIRCLE's own settings when the id is a circle rather
|
|
898
1146
|
than the workspace. See \`nestr_help({ topic: "workspace-settings" })\` before answering
|
|
899
|
-
any "where do I find..." question about settings
|
|
1147
|
+
any "where do I find..." question about settings, and \`nestr_help({ topic: "linking" })\`
|
|
1148
|
+
for when to link an item and when to link a view.`,
|
|
900
1149
|
"workspace-settings": `## Where workspace settings are
|
|
901
1150
|
|
|
902
1151
|
**Answer this with a link, never with a menu path.** Do not describe a gear icon, a
|
|
@@ -1149,6 +1398,47 @@ When a user asks about trends or patterns (e.g., "Are we getting better at gover
|
|
|
1149
1398
|
- **All plans**: Workspace-level insights (aggregated across the whole organization)
|
|
1150
1399
|
- **Pro plan only**: Circle-level insights (\`nestId\` parameter) and user-level insights (\`userId\` parameter). If the workspace is not on a Pro plan, these filters will return a 402 error.
|
|
1151
1400
|
- \`userId\` and \`nestId\` cannot be combined — user metrics are always workspace-level.`,
|
|
1401
|
+
"api-surface": `## Checking whether the API has something
|
|
1402
|
+
|
|
1403
|
+
There is a difference between "I did not find it" and "it is not there", and only the
|
|
1404
|
+
second is safe to act on. \`nestr_api_spec\` is how you get the second.
|
|
1405
|
+
|
|
1406
|
+
It returns this deployment's own OpenAPI document, reduced to something readable:
|
|
1407
|
+
|
|
1408
|
+
- **No arguments**: the operation index. Method, path, and a one-line summary for every
|
|
1409
|
+
operation the deployment serves, plus \`totalOperations\`.
|
|
1410
|
+
- **\`search\`**: filter that index by keyword against path and summary. The counts are the
|
|
1411
|
+
answer as much as the rows are. \`matchedOperations: 0\` out of \`totalOperations: 94\` for
|
|
1412
|
+
"duration" means this API has nothing about duration, full stop.
|
|
1413
|
+
- **\`path\`**: the full schema for one operation, including every parameter it accepts and
|
|
1414
|
+
what each does. Use this before assuming a parameter exists.
|
|
1415
|
+
|
|
1416
|
+
### When to reach for it
|
|
1417
|
+
|
|
1418
|
+
- A user asks whether Nestr can do X through the API and you are about to say "I do not
|
|
1419
|
+
think so". Check first, then say it with certainty.
|
|
1420
|
+
- You are about to pass a parameter you have not seen documented. \`path\` tells you whether
|
|
1421
|
+
it exists rather than having it silently ignored.
|
|
1422
|
+
- Something is missing and you need to say whether it is unsupported or merely not exposed
|
|
1423
|
+
through a tool. Those are different answers with different next steps.
|
|
1424
|
+
|
|
1425
|
+
### The neighbouring questions
|
|
1426
|
+
|
|
1427
|
+
\`nestr_api_spec\` answers "does the HTTP surface have this". Two others answer nearby
|
|
1428
|
+
questions, and mixing them up wastes a call:
|
|
1429
|
+
|
|
1430
|
+
- **Does my search filter apply?** \`strict: true\` on \`nestr_search\`. An unrecognised
|
|
1431
|
+
operator or field is otherwise dropped silently and the search returns a broader result.
|
|
1432
|
+
See \`nestr_help('search')\`.
|
|
1433
|
+
- **Does this label have this field?** \`nestr_get_label\` returns a label's fields with their
|
|
1434
|
+
codes, types and options. That is the data model rather than the HTTP surface, so a field
|
|
1435
|
+
can exist here and have no dedicated endpoint.
|
|
1436
|
+
|
|
1437
|
+
### What it does not tell you
|
|
1438
|
+
|
|
1439
|
+
The spec describes routes, not permissions. An operation being listed does not mean this
|
|
1440
|
+
caller may call it: read-only keys, key profiles and per-nest rights all still apply. For
|
|
1441
|
+
"why can I not do this", use \`nestr_explain_nest\`; for auth failures, \`nestr_diagnose\`.`,
|
|
1152
1442
|
"mcp-apps": `## MCP Apps (Interactive UI)
|
|
1153
1443
|
|
|
1154
1444
|
Nestr provides interactive UI components that can be embedded in MCP clients that support the \`ui://\` resource protocol.
|
|
@@ -1409,7 +1699,7 @@ The Scrum app adds four label types (\`userstory\`, \`sprint\`, \`epic\`, \`mile
|
|
|
1409
1699
|
|
|
1410
1700
|
### Detect whether Scrum is enabled
|
|
1411
1701
|
|
|
1412
|
-
Call \`nestr_get_workspace_apps({ workspaceId })\` and look for \`
|
|
1702
|
+
Call \`nestr_get_workspace_apps({ workspaceId })\` and look for \`_id: 'scrum'\` with \`enabled: true\`. The field is \`_id\`, not \`id\`, and the endpoint returns every app rather than only the enabled ones, so a disabled Scrum app comes back as \`enabled: false\` rather than going missing — testing for absence reports it as on. When it is disabled the labels won't exist in the workspace and any \`label:userstory\` / \`label:sprint\` / \`label:epic\` / \`label:milestone\` queries will return empty.
|
|
1413
1703
|
|
|
1414
1704
|
Sprints, epics, and milestones can each be individually disabled at the workspace level even when the app is on — check \`workspace.data['appfield-scrum-sprints-enabled']\` (and the \`-epics-\` / \`-milestones-\` variants). When a sub-feature is set to \`false\`, the corresponding graph-field on user stories is hidden in the UI; the labels still resolve, so search and graph-link tools keep working.
|
|
1415
1705
|
|
|
@@ -1663,6 +1953,50 @@ The parent Objective's \`goal_complete\` updates from the average of its KRs.
|
|
|
1663
1953
|
**Role-filler mode** — If your role owns an Objective or contributes \`resultwork\` to a Key Result, update your contribution as part of your operational rhythm. Reading the parent goal's \`term\` tells you the window you're working in. Capture repeatable measurement patterns as skill nests.
|
|
1664
1954
|
|
|
1665
1955
|
**Workspace mode** — Use for cross-circle reporting and period-level rollups. Pair \`goal.term=this_quarter\` queries with insights tools to build dashboards.`,
|
|
1956
|
+
"meetings": `## Meetings (creating and scheduling)
|
|
1957
|
+
|
|
1958
|
+
For how a meeting is *run* once it exists — the facilitator flow, check-in, processing agenda items into outcomes, closing, the emailed report and the PDF minutes — read the end-user article: \`nestr_help({ topic: "running-meetings-in-nestr" })\`. This topic covers the part that article does not: where a meeting nest belongs in the tree, and how to create one with tool calls.
|
|
1959
|
+
|
|
1960
|
+
### A meeting attaches to a circle that already exists
|
|
1961
|
+
|
|
1962
|
+
A meeting nest's parent must be one of:
|
|
1963
|
+
|
|
1964
|
+
| Parent label | What it is |
|
|
1965
|
+
|---|---|
|
|
1966
|
+
| \`anchor-circle\` | The workspace itself |
|
|
1967
|
+
| \`circle\` | A sub-circle |
|
|
1968
|
+
| \`role\` | A role (role meetings — off by default; a workspace admin enables them under Workspace settings > Applications) |
|
|
1969
|
+
|
|
1970
|
+
**The anchor circle is a circle.** Every workspace has one from the moment it is created, it already holds the Circle lead, Facilitator and Secretary roles, and it hosts tactical and governance meetings exactly like any sub-circle does. In a young workspace that has no sub-circles yet, the anchor circle is the right parent — not a missing prerequisite.
|
|
1971
|
+
|
|
1972
|
+
This matters because the end-user article opens with "navigate to the circle where you want to hold the meeting", which reads like a blocker when \`nestr_list_circles\` returns only the anchor circle. It is not one. The web app's own **+ Create > Meeting** dialog offers the anchor circle in its circle picker.
|
|
1973
|
+
|
|
1974
|
+
### Never create a circle in order to hold a meeting
|
|
1975
|
+
|
|
1976
|
+
A circle is a durable domain of work with a purpose, roles and accountabilities: "Marketing", "Customer Support", "Platform". A meeting is an event that a circle holds. "Weekly team sync", "Monday standup" and "Quarterly review" are meeting names, never circle names.
|
|
1977
|
+
|
|
1978
|
+
Creating a circle to house a meeting is close to pure cost. It arrives with no purpose, nobody filling its roles, and four core roles (Circle lead, Rep link, Facilitator, Secretary) generated alongside it, and it widens the governance tree permanently in exchange for a calendar entry. It also splits the workspace's work across a boundary the organisation never asked for.
|
|
1979
|
+
|
|
1980
|
+
If a meeting genuinely seems to want a circle of its own, that is a claim about the **organisation**, not about the meeting: a distinct domain of work needs a home. That is a governance decision, so ask the user before creating anything, and name the circle after the domain rather than the meeting.
|
|
1981
|
+
|
|
1982
|
+
### Creating one
|
|
1983
|
+
|
|
1984
|
+
\`nestr_create_nest\`:
|
|
1985
|
+
|
|
1986
|
+
- \`parentId\` — the circle (or role) that holds it, per the table above
|
|
1987
|
+
- \`labels\` — \`["meeting", "circle-meeting"]\` for a tactical meeting, \`["meeting", "governance"]\` for a governance meeting
|
|
1988
|
+
- \`title\` — what the meeting is, e.g. "Weekly team sync"
|
|
1989
|
+
- \`due\` — the meeting start time as an ISO datetime. A future \`due\` schedules it (the web app shows a countdown and a "Start meeting now" link); omit it to mean now.
|
|
1990
|
+
- \`users\` — everyone who should attend: the people and agents energizing roles in the circle, plus circle-lead and rep-link fillers from its sub-circles.
|
|
1991
|
+
|
|
1992
|
+
Agenda items are tensions. Link them with \`nestr_add_graph_link\` (relation \`meeting\`); an agenda item with no originating role can be a plain child nest of the meeting instead.
|
|
1993
|
+
|
|
1994
|
+
### Before you create one
|
|
1995
|
+
|
|
1996
|
+
1. Call \`nestr_list_circles({ workspaceId })\`. A workspace always has at least the anchor circle, so an answer of "there are no circles" is always wrong.
|
|
1997
|
+
2. If more than one circle exists and the user has not said which, **ask**. Putting a meeting in the wrong circle puts it in front of the wrong people.
|
|
1998
|
+
3. Check the app is on with \`nestr_get_workspace_apps({ workspaceId })\` and look for \`_id: "meetings"\` (titled "Circle Meetings") with \`enabled: true\`. Note the field is \`_id\`, not \`id\`, and the app id is the short \`meetings\` — not the \`circleplus-meetings\` data key the workspace stores internally. Whether tactical and governance meetings are individually enabled is NOT exposed by this endpoint; if you need that distinction, ask the admin. If meetings are off, say so and point the admin at Workspace settings > Applications rather than creating a nest nobody can open.
|
|
1999
|
+
4. Report back with a link to the meeting. See \`nestr_help({ topic: "web-app-links" })\` — meetings live on the \`#meetings\` tab of their circle.`,
|
|
1666
2000
|
"doing-work": DOING_WORK_INSTRUCTIONS,
|
|
1667
2001
|
"tension-processing": TENSION_PROCESSING_INSTRUCTIONS,
|
|
1668
2002
|
"workspace-setup": WORKSPACE_SETUP_INSTRUCTIONS,
|
package/build/help/topics.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"topics.js","sourceRoot":"","sources":["../../src/help/topics.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAAE,+BAA+B,EAAE,MAAM,iCAAiC,CAAC;AAClF,OAAO,EAAE,4BAA4B,EAAE,MAAM,8BAA8B,CAAC;AAE5E,MAAM,CAAC,MAAM,WAAW,GAA2B;IACjD,QAAQ,EAAE
|
|
1
|
+
{"version":3,"file":"topics.js","sourceRoot":"","sources":["../../src/help/topics.ts"],"names":[],"mappings":"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"}
|
package/build/http.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAgB,EAAE,OAAO,EAAE,QAAQ,EAAgB,MAAM,SAAS,CAAC;AAEnE,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAgB,EAAE,OAAO,EAAE,QAAQ,EAAgB,MAAM,SAAS,CAAC;AAEnE,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AAgCnG,OAAO,EAAa,KAAK,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxE,OAAO,oBAAoB,CAAC;AAmB5B,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,EAAE,IAAI,MAAM,CAEjE;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAS/C;AAED,eAAO,MAAM,GAAG,6CAAY,CAAC;AA86B7B,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,6BAA6B,CAAC;IACzC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,aAAa,EAAE,OAAO,CAAC;IACvB,qBAAqB,EAAE,OAAO,CAAC;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,QAAQ,CAAC;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC;IACnC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,yEAAyE;IACzE,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,6EAA6E;IAC7E,kBAAkB,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACtE,6FAA6F;IAC7F,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAMD,eAAO,MAAM,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAM,CAAC;AAKxD,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAM,CAAC;AAS9D,eAAO,MAAM,wBAAwB,QAAiB,CAAC;AAcvD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAgBtF;AA0YD;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAcxD"}
|
package/build/http.js
CHANGED
|
@@ -34,6 +34,7 @@ import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
|
|
|
34
34
|
import { createServer } from "./server.js";
|
|
35
35
|
import { toolDefinitions, PUBLIC_TOOL_NAMES, READONLY_TOOL_NAMES } from "./tools/index.js";
|
|
36
36
|
import { NestrClient, NestrApiError, tokenFingerprint } from "./api/client.js";
|
|
37
|
+
import { bearerIsReadOnly } from "./api/readonly-bearer.js";
|
|
37
38
|
import { getProtectedResourceMetadata, getAuthorizationServerMetadata, getOAuthConfig, } from "./oauth/config.js";
|
|
38
39
|
import { createAuthorizationRequest, getPendingAuth, exchangeCodeForTokens, storeOAuthSession, verifyPKCE, getOAuthSession, } from "./oauth/flow.js";
|
|
39
40
|
import { initStore, getStore, } from "./oauth/store.js";
|
|
@@ -1771,10 +1772,14 @@ async function handleMcpPost(req, res, routeOpts = {}) {
|
|
|
1771
1772
|
// Check if we have a stored session for this token (browser flow).
|
|
1772
1773
|
// Standard MCP OAuth clients manage tokens client-side and won't have a stored session.
|
|
1773
1774
|
const hasStoredSession = !isApiKey && !!(await getStore().getSession(authToken));
|
|
1775
|
+
// A read-only key gets the read-only tool surface on the ordinary /mcp
|
|
1776
|
+
// endpoint, so a consumer does not have to point at a second URL.
|
|
1777
|
+
// Skipped on /mcp/readonly, where the route already decided.
|
|
1778
|
+
const readOnlyBearer = !isReadOnly && await bearerIsReadOnly(new NestrClient({ apiKey: authToken, baseUrl: process.env.NESTR_API_BASE }));
|
|
1774
1779
|
const session = buildMcpSession({
|
|
1775
1780
|
authToken,
|
|
1776
1781
|
isApiKey,
|
|
1777
|
-
isReadOnly,
|
|
1782
|
+
isReadOnly: isReadOnly || readOnlyBearer,
|
|
1778
1783
|
mcpClient: mcpClientName,
|
|
1779
1784
|
mcpClientVersion,
|
|
1780
1785
|
userId,
|