@nestr/mcp 0.1.98 → 0.1.100
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 +1 -1
- package/build/api/client.d.ts +15 -179
- package/build/api/client.d.ts.map +1 -1
- package/build/api/client.js +1 -124
- package/build/api/client.js.map +1 -1
- package/build/help/topics.js +1 -1
- package/build/http.d.ts +1 -0
- package/build/http.d.ts.map +1 -1
- package/build/http.js +83 -9
- package/build/http.js.map +1 -1
- package/build/oauth/store.d.ts +2 -0
- package/build/oauth/store.d.ts.map +1 -1
- package/build/oauth/store.js.map +1 -1
- package/build/server.d.ts +8 -0
- package/build/server.d.ts.map +1 -1
- package/build/server.js +5 -2
- package/build/server.js.map +1 -1
- package/build/skills/doing-work.d.ts.map +1 -1
- package/build/skills/doing-work.js +0 -25
- 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 +0 -2
- package/build/skills/tension-processing.js.map +1 -1
- package/build/tools/index.d.ts +102 -1693
- package/build/tools/index.d.ts.map +1 -1
- package/build/tools/index.js +128 -456
- package/build/tools/index.js.map +1 -1
- package/package.json +1 -1
package/build/tools/index.js
CHANGED
|
@@ -169,46 +169,6 @@ const HINT_URL_PATTERNS = [
|
|
|
169
169
|
{ pattern: /^\/nests\/([^/]+)$/, tool: "nestr_get_nest", params: (m) => ({ nestId: m[1] }) },
|
|
170
170
|
];
|
|
171
171
|
const HINT_TYPE_TOOL_CALLS = {
|
|
172
|
-
// Raised by POST /connectors when a hand-written url points at a vendor the
|
|
173
|
-
// deployment ships a template for. The follow-up is to look at the template,
|
|
174
|
-
// not to re-register blindly: the connector just created may be exactly what
|
|
175
|
-
// the caller wanted, and only they can say. The hint carries its own ids, so
|
|
176
|
-
// this works on a catalog entry rather than needing nest ancestors.
|
|
177
|
-
connector_template_available(record) {
|
|
178
|
-
const hints = record.hints || [];
|
|
179
|
-
const hint = hints.find((h) => { return h.type === "connector_template_available"; });
|
|
180
|
-
const workspaceId = (hint && hint.workspaceId) || record.workspaceId;
|
|
181
|
-
if (!workspaceId)
|
|
182
|
-
return null;
|
|
183
|
-
return {
|
|
184
|
-
tool: "nestr_list_connector_templates",
|
|
185
|
-
params: { workspaceId },
|
|
186
|
-
};
|
|
187
|
-
},
|
|
188
|
-
// Raised by GET /connector-templates on the response that LISTS them, because
|
|
189
|
-
// listing turned out not to be enough. A caller that could see the Xero
|
|
190
|
-
// template still hand-built a connector from what the template told it, and a
|
|
191
|
-
// hand-built copy carries no settingsKey, no OAuth client and whatever
|
|
192
|
-
// endpoint the model believed, so it authorises and then fails at first use.
|
|
193
|
-
// The follow-up here is the register call itself, pre-filled with the template
|
|
194
|
-
// id, so using the template is one call rather than a thing to remember.
|
|
195
|
-
connector_template_create(record) {
|
|
196
|
-
const hints = record.hints || [];
|
|
197
|
-
const hint = hints.find((h) => { return h.type === "connector_template_create"; });
|
|
198
|
-
if (!hint)
|
|
199
|
-
return null;
|
|
200
|
-
const workspaceId = hint.workspaceId || record.workspaceId;
|
|
201
|
-
if (!workspaceId)
|
|
202
|
-
return null;
|
|
203
|
-
const ids = hint.templateIds || [];
|
|
204
|
-
return {
|
|
205
|
-
tool: "nestr_register_connector",
|
|
206
|
-
params: {
|
|
207
|
-
workspaceId,
|
|
208
|
-
templateId: ids.length === 1 ? ids[0] : "<id of the template you want, from this list>",
|
|
209
|
-
},
|
|
210
|
-
};
|
|
211
|
-
},
|
|
212
172
|
no_strategy(nest) {
|
|
213
173
|
const nestId = nest._id;
|
|
214
174
|
if (!nestId)
|
|
@@ -645,7 +605,12 @@ const coerceIntArray = (schema) => z.preprocess((val) => {
|
|
|
645
605
|
// Shared description for the sort parameter on list/fetch tools. All of these
|
|
646
606
|
// endpoints honor a `sort` query param server-side (field name, '-' prefix for
|
|
647
607
|
// descending) — the same fields the search `sort:` operator uses.
|
|
648
|
-
const
|
|
608
|
+
const MENTION_DESC = "Supports HTML and @mentions. Mentions MUST use literal curly braces: `@{userId:roleId}`, NOT `@userId`. Without braces nothing is linked and nobody is notified. Forms: `@{userId:roleId}` (preferred, names the role), `@{userId}`, `@{email}`, `@{circle}` (all fillers in the nearest ancestor circle).";
|
|
609
|
+
const PRIME_LABEL_RULE = "At most ONE prime label per nest (project, tension, role, circle, anchor-circle, meeting, metric, goal, result, checklist, feedback, userstory, sprint, epic, milestone): they are the nest's identity and cannot coexist. Only exception: userstory may pair with project.";
|
|
610
|
+
const PURPOSE_DESC = "Only for workspaces, circles and roles: a short aspirational statement. Details belong in description, not here. Supports HTML.";
|
|
611
|
+
const CONTENT_DESC = "The primary content field: details, context, acceptance criteria. Structured data goes in fields, progress in comments. Supports Markdown and HTML.";
|
|
612
|
+
const STRIP_DESCRIPTION = "Strip description fields to shrink the response. Use for bulk or index reads.";
|
|
613
|
+
const SORT_DESCRIPTION = "Sort field: title, createdAt, updatedAt, due, activityAt, order. Prefix '-' to reverse. For 'recently active' use '-activityAt' (includes children), not '-updatedAt' (own edits only).";
|
|
649
614
|
// Tool input schemas using Zod
|
|
650
615
|
export const schemas = {
|
|
651
616
|
listWorkspaces: z.object({
|
|
@@ -722,7 +687,7 @@ export const schemas = {
|
|
|
722
687
|
hints: z.boolean().optional().describe("Include contextual hints on each nest (default: true). Hints surface actionable signals like unassigned roles, stale projects, or unread comments. Set to false for bulk lookups where you only need structural data, not contextual guidance."),
|
|
723
688
|
provenance: z.boolean().optional().describe("Single-nest only. Include field/property provenance: which label (and circle context) defines each field and property."),
|
|
724
689
|
rights: z.boolean().optional().describe("Single-nest only. Include the caller's composed rights on the nest plus a deny trace naming the profiles that block each op."),
|
|
725
|
-
forUser: z.string().optional().describe("Single
|
|
690
|
+
forUser: z.string().optional().describe("Single nest, with rights=true. Rights for this user id instead of the caller. Caller must be a nest admin."),
|
|
726
691
|
whoCan: z.string().optional().describe("Single-nest only. Comma-separated ops (read,update,delete,create) to list who can perform each on this nest."),
|
|
727
692
|
}),
|
|
728
693
|
explainNest: z.object({
|
|
@@ -742,7 +707,7 @@ export const schemas = {
|
|
|
742
707
|
parentId: z.string().describe("Parent nest ID (workspace, circle, or project)"),
|
|
743
708
|
title: z.string().describe("Title of the new nest (plain text, HTML stripped)"),
|
|
744
709
|
description: z.string().optional().describe("The primary content field — use for project details, task context, acceptance criteria, Definition of Done, and any detailed information. Supports Markdown and HTML."),
|
|
745
|
-
purpose: z.string().optional().describe(
|
|
710
|
+
purpose: z.string().optional().describe(PURPOSE_DESC),
|
|
746
711
|
labels: coerceFromJson(z.array(z.string())).optional().describe("Label IDs to apply"),
|
|
747
712
|
fields: coerceFromJson(z.record(z.unknown())).optional().describe("Structured field values to set on creation (e.g., { 'project.status': 'Current' }, { 'skill.type': 'process' }). Same shape as nestr_update_nest fields — saves a follow-up update call."),
|
|
748
713
|
users: coerceFromJson(z.array(z.string())).optional().describe("User IDs to assign (required for tasks/projects to associate with a person)"),
|
|
@@ -754,7 +719,7 @@ export const schemas = {
|
|
|
754
719
|
nestId: z.string().describe("Nest ID to update"),
|
|
755
720
|
title: z.string().optional().describe("New title (plain text, HTML stripped)"),
|
|
756
721
|
description: z.string().optional().describe("The primary content field — use for project details, task context, acceptance criteria, and any detailed information. Supports Markdown and HTML."),
|
|
757
|
-
purpose: z.string().optional().describe(
|
|
722
|
+
purpose: z.string().optional().describe(PURPOSE_DESC),
|
|
758
723
|
parentId: z.string().optional().describe("New parent ID (move nest to different location, e.g., move inbox item to a role or project)"),
|
|
759
724
|
labels: coerceFromJson(z.array(z.string())).optional().describe("Label IDs to set (e.g., ['project'] to convert an item into a project)"),
|
|
760
725
|
fields: coerceFromJson(z.record(z.unknown())).optional().describe("Field updates (e.g., { 'project.status': 'Current' })"),
|
|
@@ -771,12 +736,12 @@ export const schemas = {
|
|
|
771
736
|
}),
|
|
772
737
|
addComment: z.object({
|
|
773
738
|
nestId: z.string().describe("Nest ID to comment on"),
|
|
774
|
-
body: z.string().describe(
|
|
775
|
-
labels: z.array(z.string()).optional().describe("Optional label IDs to attach
|
|
739
|
+
body: z.string().describe(`Comment text. ${MENTION_DESC}`),
|
|
740
|
+
labels: z.array(z.string()).optional().describe("Optional label IDs to attach at creation (e.g. 'decision', 'question', or a custom ID). Personal labels are auto-scoped to the caller. Discover IDs via nestr_list_labels / nestr_list_personal_labels."),
|
|
776
741
|
}),
|
|
777
742
|
updateComment: z.object({
|
|
778
743
|
commentId: z.string().describe("Comment ID to update"),
|
|
779
|
-
body: z.string().describe(
|
|
744
|
+
body: z.string().describe(`Updated comment text. ${MENTION_DESC}`),
|
|
780
745
|
labels: z.array(z.string()).optional().describe("Optional full set of label IDs for the comment. When provided, this REPLACES the comment's existing labels. To incrementally add or remove a single label without replacing the rest, use nestr_add_label / nestr_remove_label with the commentId as the nestId."),
|
|
781
746
|
}),
|
|
782
747
|
deleteComment: z.object({
|
|
@@ -1000,14 +965,14 @@ export const schemas = {
|
|
|
1000
965
|
tensionId: z.string().describe("Tension ID"),
|
|
1001
966
|
_id: z.string().optional().describe("ID of an existing governance item to change or remove. Omit to propose a new item."),
|
|
1002
967
|
title: z.string().optional().describe("Title for the governance item"),
|
|
1003
|
-
labels: coerceFromJson(z.array(z.string())).optional().describe("
|
|
968
|
+
labels: coerceFromJson(z.array(z.string())).optional().describe("Item type, e.g. ['role'], ['circle'], ['policy'], ['accountability'], ['domain']"),
|
|
1004
969
|
description: z.string().optional().describe("The primary content field — detailed information about the item. Supports Markdown and HTML."),
|
|
1005
|
-
purpose: z.string().optional().describe(
|
|
970
|
+
purpose: z.string().optional().describe(PURPOSE_DESC),
|
|
1006
971
|
parentId: z.string().optional().describe("Parent ID — use to move/restructure items (e.g., move role to different circle)"),
|
|
1007
|
-
users: coerceFromJson(z.array(z.string())).optional().describe("User IDs to assign. For an election (with roleId), the
|
|
1008
|
-
due: z.string().optional().describe("Due
|
|
1009
|
-
accountabilities: coerceFromJson(z.array(z.string())).optional().describe("Accountability titles
|
|
1010
|
-
domains: coerceFromJson(z.array(z.string())).optional().describe("Domain titles
|
|
972
|
+
users: coerceFromJson(z.array(z.string())).optional().describe("User IDs to assign. For an election (with roleId), the one user being elected."),
|
|
973
|
+
due: z.string().optional().describe("Due or re-election date, ISO. For an election, the term end; omit for no term."),
|
|
974
|
+
accountabilities: coerceFromJson(z.array(z.string())).optional().describe("Accountability titles on a role (replaces all; children tools for individual edits)"),
|
|
975
|
+
domains: coerceFromJson(z.array(z.string())).optional().describe("Domain titles on a role (replaces all; children tools for individual edits)"),
|
|
1011
976
|
roleId: z.string().optional().describe("Hold an ELECTION: the electable role to fill (Facilitator/Secretary/Rep Link or any electable role). Assigns or reconfirms the role's filler for a term WITHOUT changing its accountabilities/domains — provide users:[userId] (one person) and optional due (term). Do not combine with _id."),
|
|
1012
977
|
removeNest: z.boolean().optional().describe("Set true with _id to propose deletion of the referenced governance item (when the proposal is accepted, the item is removed). Distinct from nestr_remove_tension_part, which undoes a proposal part you already added. Requires _id; other body fields are ignored."),
|
|
1013
978
|
}).refine((data) => !data.removeNest || !!data._id, { message: "removeNest:true requires _id to identify which item to propose for deletion" }).refine((data) => !data.roleId || (Array.isArray(data.users) && data.users.length === 1), { message: "An election (roleId) requires exactly one user to elect — users: [userId]." }).refine((data) => !(data.roleId && data._id), { message: "Provide either roleId (to hold an election) or _id (to change/delete an existing item), not both." }),
|
|
@@ -1017,13 +982,13 @@ export const schemas = {
|
|
|
1017
982
|
partId: z.string().describe("Part ID to modify"),
|
|
1018
983
|
title: z.string().optional().describe("Updated title"),
|
|
1019
984
|
description: z.string().optional().describe("Updated description — the primary content field. Supports Markdown and HTML."),
|
|
1020
|
-
purpose: z.string().optional().describe(
|
|
985
|
+
purpose: z.string().optional().describe(PURPOSE_DESC),
|
|
1021
986
|
labels: coerceFromJson(z.array(z.string())).optional().describe("Updated labels"),
|
|
1022
987
|
parentId: z.string().optional().describe("Updated parent ID"),
|
|
1023
988
|
users: coerceFromJson(z.array(z.string())).optional().describe("Updated user assignments"),
|
|
1024
989
|
due: z.string().optional().describe("Updated due date (ISO format)"),
|
|
1025
|
-
accountabilities: coerceFromJson(z.array(z.string())).optional().describe("Updated accountabilities (replaces all
|
|
1026
|
-
domains: coerceFromJson(z.array(z.string())).optional().describe("Updated domains (replaces all
|
|
990
|
+
accountabilities: coerceFromJson(z.array(z.string())).optional().describe("Updated accountabilities (replaces all; children tools for individual edits)"),
|
|
991
|
+
domains: coerceFromJson(z.array(z.string())).optional().describe("Updated domains (replaces all; children tools for individual edits)"),
|
|
1027
992
|
}),
|
|
1028
993
|
removeTensionPart: z.object({
|
|
1029
994
|
nestId: z.string().describe("ID of the circle or role the tension belongs to"),
|
|
@@ -1099,70 +1064,20 @@ export const schemas = {
|
|
|
1099
1064
|
listConnectors: z.object({
|
|
1100
1065
|
workspaceId: z.string().describe("Workspace ID whose connector catalog to list"),
|
|
1101
1066
|
}),
|
|
1102
|
-
listConnectorTemplates: z.object({
|
|
1103
|
-
workspaceId: z.string().describe("Workspace ID whose available connector templates to list"),
|
|
1104
|
-
}),
|
|
1105
1067
|
registerConnector: z.object({
|
|
1106
|
-
templateId: z.string().optional().describe("Id of a template from nestr_list_connector_templates. Given this, everything else is filled in from the template and you should omit type/config/capabilities/exposure/authStrategy. ALWAYS prefer this over hand-registering a vendor the deployment already knows."),
|
|
1107
1068
|
workspaceId: z.string().describe("Workspace ID to register the connector in"),
|
|
1108
|
-
type: z.enum(["mcp", "cli", "api"]).
|
|
1109
|
-
name: z.string().
|
|
1110
|
-
config: coerceFromJson(z.record(z.unknown())).optional().describe("
|
|
1111
|
-
capabilities: coerceFromJson(z.record(z.unknown())).optional().describe("
|
|
1112
|
-
exposure: coerceFromJson(z.record(z.unknown())).optional().describe("
|
|
1113
|
-
authStrategy: z.enum(["secret", "oauth2"]).optional().describe("How a principal connects: 'secret' (
|
|
1114
|
-
}),
|
|
1115
|
-
createAgent: z.object({
|
|
1116
|
-
workspaceId: z.string().describe("Workspace ID to create the agent in"),
|
|
1117
|
-
name: z.string().describe("The AGENT's own name, as its identity calls it (e.g. 'Collab'). Not the name of the work: that belongs to the role this agent will fill."),
|
|
1118
|
-
agentConfig: coerceFromJson(z.record(z.unknown())).optional().describe("Runtime wiring, not persona: { runtimeCallbackUrl (https, or http to a *.svc.cluster.local service), tokenTtlSeconds (30-1800) }. Omit for an agent that runs on Nestr's own runtime."),
|
|
1069
|
+
type: z.enum(["mcp", "cli", "api"]).describe("Transport: 'mcp' (MCP server over a url), 'api' (REST endpoint over a url), or 'cli' (a command)"),
|
|
1070
|
+
name: z.string().describe("Unique connector name within the workspace catalog"),
|
|
1071
|
+
config: coerceFromJson(z.record(z.unknown())).optional().describe("Transport config, no secret. mcp/api need a url, cli a command. Optional non-secret headers go under headers."),
|
|
1072
|
+
capabilities: coerceFromJson(z.record(z.unknown())).optional().describe("{ discover: boolean, tools: [{ name, description, inputSchema }] }. discover:true lets the connector self-describe its tools at runtime."),
|
|
1073
|
+
exposure: coerceFromJson(z.record(z.unknown())).optional().describe("Which owners may bind: { userAgent: boolean, domainGated: boolean }. domainGated:true allows binding to a role's domain."),
|
|
1074
|
+
authStrategy: z.enum(["secret", "oauth2"]).optional().describe("How a principal connects: 'secret' (one-time, via the Connect button) or 'oauth2'. The agent never sees it."),
|
|
1119
1075
|
}),
|
|
1120
1076
|
bindConnector: z.object({
|
|
1121
1077
|
workspaceId: z.string().describe("Workspace ID the connector and owner belong to"),
|
|
1122
1078
|
connectorId: z.string().describe("ID of an enabled connector from nestr_list_connectors"),
|
|
1123
|
-
ownerType: z.enum(["
|
|
1124
|
-
ownerId: z.string().describe("Owner ID.
|
|
1125
|
-
}),
|
|
1126
|
-
updateConnector: z.object({
|
|
1127
|
-
workspaceId: z.string().describe("Workspace ID the connector belongs to"),
|
|
1128
|
-
connectorId: z.string().describe("ID of the connector to update"),
|
|
1129
|
-
type: z.enum(["mcp", "cli", "api"]).optional().describe("Transport"),
|
|
1130
|
-
name: z.string().optional().describe("Unique connector name within the workspace catalog"),
|
|
1131
|
-
config: coerceFromJson(z.record(z.unknown())).optional().describe("Per-type transport config, no secret"),
|
|
1132
|
-
capabilities: coerceFromJson(z.record(z.unknown())).optional().describe("Capability descriptor"),
|
|
1133
|
-
exposure: coerceFromJson(z.record(z.unknown())).optional().describe("Exposure policy: { userAgent, domainGated }"),
|
|
1134
|
-
authStrategy: z.enum(["secret", "oauth2"]).optional().describe("How a principal connects"),
|
|
1135
|
-
enabled: z.boolean().optional().describe("Switch the connector on or off in this workspace"),
|
|
1136
|
-
}),
|
|
1137
|
-
removeConnector: z.object({
|
|
1138
|
-
workspaceId: z.string().describe("Workspace ID the connector belongs to"),
|
|
1139
|
-
connectorId: z.string().describe("ID of the connector to remove from the catalog"),
|
|
1140
|
-
}),
|
|
1141
|
-
listConnections: z.object({
|
|
1142
|
-
workspaceId: z.string().describe("Workspace ID whose bindings to list"),
|
|
1143
|
-
includeDisabled: z.boolean().optional().describe("Include removed (disabled) bindings. Default false."),
|
|
1144
|
-
}),
|
|
1145
|
-
removeConnection: z.object({
|
|
1146
|
-
workspaceId: z.string().describe("Workspace ID the binding belongs to"),
|
|
1147
|
-
connectionId: z.string().describe("ID of the binding to remove, from nestr_list_connections"),
|
|
1148
|
-
}),
|
|
1149
|
-
getConnectLink: z.object({
|
|
1150
|
-
workspaceId: z.string().describe("Workspace ID the binding belongs to"),
|
|
1151
|
-
connectionId: z.string().describe("ID of the binding to connect, from nestr_list_connections"),
|
|
1152
|
-
}),
|
|
1153
|
-
revokeConnectionCredential: z.object({
|
|
1154
|
-
workspaceId: z.string().describe("Workspace ID the binding belongs to"),
|
|
1155
|
-
connectionId: z.string().describe("ID of the binding whose credential to revoke"),
|
|
1156
|
-
}),
|
|
1157
|
-
getAgentConnectorReach: z.object({
|
|
1158
|
-
workspaceId: z.string().describe("Workspace ID the agent belongs to"),
|
|
1159
|
-
agentUserId: z.string().describe("The agent's bot user ID"),
|
|
1160
|
-
}),
|
|
1161
|
-
runAgent: z.object({
|
|
1162
|
-
workspaceId: z.string().describe("Workspace ID the agent belongs to"),
|
|
1163
|
-
agentUserId: z.string().describe("The agent's bot user ID"),
|
|
1164
|
-
nestId: z.string().describe("The nest the run is pinned to: a role, a project, a task"),
|
|
1165
|
-
message: z.string().optional().describe("What this run is for. Omit for a plain 'advance this item' run."),
|
|
1079
|
+
ownerType: z.enum(["user", "agent", "workspace", "role-domain"]).describe("Owner type. 'role-domain' binds to a role's domain, materialising a credentials field there."),
|
|
1080
|
+
ownerId: z.string().describe("Owner ID. user/agent: the user ID. workspace: the workspace ID. role-domain: the domain nest ID."),
|
|
1166
1081
|
}),
|
|
1167
1082
|
// File attachments (a comment id works as the nestId — files are keyed by nestId)
|
|
1168
1083
|
getNestFiles: z.object({
|
|
@@ -1194,15 +1109,15 @@ const destructive = { annotations: { readOnlyHint: false, destructiveHint: true
|
|
|
1194
1109
|
export const toolDefinitions = [
|
|
1195
1110
|
{
|
|
1196
1111
|
name: "nestr_help",
|
|
1197
|
-
description: "
|
|
1112
|
+
description: "Nestr documentation, three modes. (1) Internal topic: `topic` with a curated key (search, labels, nest-model, inbox, daily-plan, notifications, insights, tension-processing, skills, mcp-apps, authentication, scrum, okr, ...); 'topics' lists them all. (2) Help article: `topic` with a slug from nestr.io/help/articles/<slug>; returns markdown plus a numbered list of its images. Images are never attached by default: includeImages:true takes the first maxImages screenshots, imageIndexes:[..] takes chosen ones. Attach when the user wants to see how something looks. (3) Search: `search` with free text; returns ranked matches, each a title and one-line summary, tolerant of typos and synonyms (kanban/sprint to scrum). A topic is tried internally first, then as an article. Every response opens with 'Resolved as:' naming the mode, and topics and articles cross-link. Call before unfamiliar operations. No auth.",
|
|
1198
1113
|
inputSchema: {
|
|
1199
1114
|
type: "object",
|
|
1200
1115
|
properties: {
|
|
1201
1116
|
topic: { type: "string", description: "Internal topic key or help-article slug. Use 'topics' for the full list of internal topics." },
|
|
1202
1117
|
search: { type: "string", description: "Free-text query against the public help articles. Returns slugs to fetch via `topic`." },
|
|
1203
|
-
includeImages: { type: "boolean", description: "
|
|
1204
|
-
imageIndexes: { type: "array", items: { type: "integer", minimum: 0 }, description: "
|
|
1205
|
-
maxImages: { type: "integer", minimum: 1, description: "
|
|
1118
|
+
includeImages: { type: "boolean", description: "Article mode. Default false (markdown plus a numbered image-URL list). True attaches the first maxImages content screenshots inline, downscaled. Header, thumbnail and uncaptioned images are never auto-attached; reach those with imageIndexes." },
|
|
1119
|
+
imageIndexes: { type: "array", items: { type: "integer", minimum: 0 }, description: "Article mode. Attach screenshots by [index] from the numbered list in a prior response, e.g. [4,5,6]. Overrides the default selection and the maxImages cap, attaching exactly these in order." },
|
|
1120
|
+
maxImages: { type: "integer", minimum: 1, description: "Article mode. Caps the default selection (first N content images). Default 3, max 6. Ignored when imageIndexes is set." },
|
|
1206
1121
|
},
|
|
1207
1122
|
},
|
|
1208
1123
|
...readOnly,
|
|
@@ -1226,7 +1141,7 @@ export const toolDefinitions = [
|
|
|
1226
1141
|
sort: { type: "string", description: SORT_DESCRIPTION },
|
|
1227
1142
|
limit: { type: "number", description: "Omit on first call to see meta.total count" },
|
|
1228
1143
|
page: { type: "number", description: "Page number (1-indexed) for pagination" },
|
|
1229
|
-
stripDescription: { type: "boolean", description:
|
|
1144
|
+
stripDescription: { type: "boolean", description: STRIP_DESCRIPTION },
|
|
1230
1145
|
},
|
|
1231
1146
|
},
|
|
1232
1147
|
...readOnly,
|
|
@@ -1238,7 +1153,7 @@ export const toolDefinitions = [
|
|
|
1238
1153
|
type: "object",
|
|
1239
1154
|
properties: {
|
|
1240
1155
|
workspaceId: { type: "string", description: "Workspace ID" },
|
|
1241
|
-
stripDescription: { type: "boolean", description:
|
|
1156
|
+
stripDescription: { type: "boolean", description: STRIP_DESCRIPTION },
|
|
1242
1157
|
},
|
|
1243
1158
|
required: ["workspaceId"],
|
|
1244
1159
|
},
|
|
@@ -1291,10 +1206,10 @@ export const toolDefinitions = [
|
|
|
1291
1206
|
workspaceId: { type: "string", description: "Workspace ID to search in" },
|
|
1292
1207
|
query: { type: "string", description: "Search query with optional operators (e.g., 'label:role', 'assignee:me completed:false')" },
|
|
1293
1208
|
sort: { type: "string", description: `${SORT_DESCRIPTION} Takes precedence over sort:/sort-order: operators in the query.` },
|
|
1294
|
-
limit: { type: "number", description: "Max results per page.
|
|
1209
|
+
limit: { type: "number", description: "Max results per page. Omit on the first call so meta.total shows the match count." },
|
|
1295
1210
|
page: { type: "number", description: "Page number (1-indexed) for fetching additional pages" },
|
|
1296
|
-
stripDescription: { type: "boolean", description:
|
|
1297
|
-
_listTitle: { type: "string", description: "Short
|
|
1211
|
+
stripDescription: { type: "boolean", description: STRIP_DESCRIPTION },
|
|
1212
|
+
_listTitle: { type: "string", description: "Short title for the list UI header, 2-4 words, e.g. Marketing projects. Say what is shown, not the query." },
|
|
1298
1213
|
},
|
|
1299
1214
|
required: ["workspaceId", "query"],
|
|
1300
1215
|
},
|
|
@@ -1308,14 +1223,14 @@ export const toolDefinitions = [
|
|
|
1308
1223
|
inputSchema: {
|
|
1309
1224
|
type: "object",
|
|
1310
1225
|
properties: {
|
|
1311
|
-
nestId: { type: "string", description: "Nest ID, or comma-separated IDs
|
|
1226
|
+
nestId: { type: "string", description: "Nest ID, or comma-separated IDs for a batch (e.g. 'id1,id2'). Keep the URL under 2000 chars." },
|
|
1312
1227
|
fieldsMetaData: { type: "boolean", description: "Set to true to include field schema metadata (available options, field types)" },
|
|
1313
|
-
hints: { type: "boolean", description: "
|
|
1314
|
-
stripDescription: { type: "boolean", description:
|
|
1315
|
-
provenance: { type: "boolean", description: "Single
|
|
1316
|
-
rights: { type: "boolean", description: "Single
|
|
1317
|
-
forUser: { type: "string", description: "Single
|
|
1318
|
-
whoCan: { type: "string", description: "Single
|
|
1228
|
+
hints: { type: "boolean", description: "Contextual hints, default true. False for bulk lookups needing only structure." },
|
|
1229
|
+
stripDescription: { type: "boolean", description: STRIP_DESCRIPTION },
|
|
1230
|
+
provenance: { type: "boolean", description: "Single nest. Which label and circle context defines each field and property, e.g. why a role has a given icon." },
|
|
1231
|
+
rights: { type: "boolean", description: "Single nest. The caller's composed rights (self read/update/delete) plus a deny trace naming what blocks each op, and why." },
|
|
1232
|
+
forUser: { type: "string", description: "Single nest, with rights=true. Rights for this user id instead of the caller. Caller must be a nest admin." },
|
|
1233
|
+
whoCan: { type: "string", description: "Single nest. Comma-separated ops (read,update,delete,create): who can perform each (admins + role-holders), with contact for admin callers." },
|
|
1319
1234
|
},
|
|
1320
1235
|
required: ["nestId"],
|
|
1321
1236
|
},
|
|
@@ -1346,7 +1261,7 @@ export const toolDefinitions = [
|
|
|
1346
1261
|
limit: { type: "number", description: "Omit on first call to see meta.total count" },
|
|
1347
1262
|
page: { type: "number", description: "Page number (1-indexed)" },
|
|
1348
1263
|
hints: { type: "boolean", description: "Include contextual hints (default: true). Set to false for large result sets or bulk operations." },
|
|
1349
|
-
stripDescription: { type: "boolean", description:
|
|
1264
|
+
stripDescription: { type: "boolean", description: STRIP_DESCRIPTION },
|
|
1350
1265
|
_listTitle: { type: "string", description: "Short descriptive title for the list UI header (e.g., \"Tasks for Website Redesign\", \"API project sub-tasks\"). Include the parent name for context." },
|
|
1351
1266
|
},
|
|
1352
1267
|
required: ["nestId"],
|
|
@@ -1357,14 +1272,14 @@ export const toolDefinitions = [
|
|
|
1357
1272
|
},
|
|
1358
1273
|
{
|
|
1359
1274
|
name: "nestr_create_nest",
|
|
1360
|
-
description:
|
|
1275
|
+
description: `Create a nest under a parent. Labels define the type, e.g. ['project'], ['role']. ${PRIME_LABEL_RULE} Sprint/epic/milestone never pair; stories link to those via graph relations. In established workspaces prefer the tension flow for governance. See nestr_help('labels').`,
|
|
1361
1276
|
inputSchema: {
|
|
1362
1277
|
type: "object",
|
|
1363
1278
|
properties: {
|
|
1364
1279
|
parentId: { type: "string", description: "Parent nest ID (workspace, circle, or project)" },
|
|
1365
1280
|
title: { type: "string", description: "Title of the new nest (plain text, HTML tags stripped)" },
|
|
1366
|
-
description: { type: "string", description:
|
|
1367
|
-
purpose: { type: "string", description:
|
|
1281
|
+
description: { type: "string", description: CONTENT_DESC },
|
|
1282
|
+
purpose: { type: "string", description: PURPOSE_DESC },
|
|
1368
1283
|
labels: {
|
|
1369
1284
|
type: "array",
|
|
1370
1285
|
items: { type: "string" },
|
|
@@ -1400,14 +1315,14 @@ export const toolDefinitions = [
|
|
|
1400
1315
|
},
|
|
1401
1316
|
{
|
|
1402
1317
|
name: "nestr_update_nest",
|
|
1403
|
-
description:
|
|
1318
|
+
description: `Update nest properties. Set parentId to move. Send only what changes. When replacing labels: ${PRIME_LABEL_RULE} Prefer tensions for governance. See nestr_help('nest-model') for fields and data namespacing.`,
|
|
1404
1319
|
inputSchema: {
|
|
1405
1320
|
type: "object",
|
|
1406
1321
|
properties: {
|
|
1407
1322
|
nestId: { type: "string", description: "Nest ID to update" },
|
|
1408
1323
|
title: { type: "string", description: "New title (plain text, HTML tags stripped)" },
|
|
1409
|
-
description: { type: "string", description:
|
|
1410
|
-
purpose: { type: "string", description:
|
|
1324
|
+
description: { type: "string", description: CONTENT_DESC },
|
|
1325
|
+
purpose: { type: "string", description: PURPOSE_DESC },
|
|
1411
1326
|
parentId: { type: "string", description: "New parent ID (move nest to different location)" },
|
|
1412
1327
|
labels: {
|
|
1413
1328
|
type: "array",
|
|
@@ -1468,16 +1383,16 @@ export const toolDefinitions = [
|
|
|
1468
1383
|
},
|
|
1469
1384
|
{
|
|
1470
1385
|
name: "nestr_add_comment",
|
|
1471
|
-
description: "Add a comment to a nest
|
|
1386
|
+
description: "Add a comment to a nest, for progress updates and discussion. Mentions need literal curly braces, see `body`. Optionally attach labels at creation.",
|
|
1472
1387
|
inputSchema: {
|
|
1473
1388
|
type: "object",
|
|
1474
1389
|
properties: {
|
|
1475
|
-
nestId: { type: "string", description: "
|
|
1476
|
-
body: { type: "string", description:
|
|
1390
|
+
nestId: { type: "string", description: "Nest or conversation the comment belongs to. A comment ID instead replies inside that thread. Direct-message conversations are flat, so a message ID there lands on the conversation and the response says where." },
|
|
1391
|
+
body: { type: "string", description: `Comment text. ${MENTION_DESC}` },
|
|
1477
1392
|
labels: {
|
|
1478
1393
|
type: "array",
|
|
1479
1394
|
items: { type: "string" },
|
|
1480
|
-
description: "Optional label IDs to attach
|
|
1395
|
+
description: "Optional label IDs to attach at creation (e.g. 'decision', 'question', or a custom ID). Personal labels are auto-scoped to the caller. Discover IDs via nestr_list_labels / nestr_list_personal_labels.",
|
|
1481
1396
|
},
|
|
1482
1397
|
},
|
|
1483
1398
|
required: ["nestId", "body"],
|
|
@@ -1491,7 +1406,7 @@ export const toolDefinitions = [
|
|
|
1491
1406
|
type: "object",
|
|
1492
1407
|
properties: {
|
|
1493
1408
|
commentId: { type: "string", description: "Comment ID to update" },
|
|
1494
|
-
body: { type: "string", description:
|
|
1409
|
+
body: { type: "string", description: `Updated comment text. ${MENTION_DESC}` },
|
|
1495
1410
|
labels: {
|
|
1496
1411
|
type: "array",
|
|
1497
1412
|
items: { type: "string" },
|
|
@@ -1524,7 +1439,7 @@ export const toolDefinitions = [
|
|
|
1524
1439
|
sort: { type: "string", description: SORT_DESCRIPTION },
|
|
1525
1440
|
limit: { type: "number", description: "Omit on first call to see meta.total count" },
|
|
1526
1441
|
page: { type: "number", description: "Page number (1-indexed)" },
|
|
1527
|
-
stripDescription: { type: "boolean", description:
|
|
1442
|
+
stripDescription: { type: "boolean", description: STRIP_DESCRIPTION },
|
|
1528
1443
|
},
|
|
1529
1444
|
required: ["workspaceId"],
|
|
1530
1445
|
},
|
|
@@ -1541,7 +1456,7 @@ export const toolDefinitions = [
|
|
|
1541
1456
|
sort: { type: "string", description: SORT_DESCRIPTION },
|
|
1542
1457
|
limit: { type: "number", description: "Omit on first call to see meta.total count" },
|
|
1543
1458
|
page: { type: "number", description: "Page number (1-indexed)" },
|
|
1544
|
-
stripDescription: { type: "boolean", description:
|
|
1459
|
+
stripDescription: { type: "boolean", description: STRIP_DESCRIPTION },
|
|
1545
1460
|
},
|
|
1546
1461
|
required: ["workspaceId", "circleId"],
|
|
1547
1462
|
},
|
|
@@ -1569,7 +1484,7 @@ export const toolDefinitions = [
|
|
|
1569
1484
|
sort: { type: "string", description: SORT_DESCRIPTION },
|
|
1570
1485
|
limit: { type: "number", description: "Omit on first call to see meta.total count" },
|
|
1571
1486
|
page: { type: "number", description: "Page number (1-indexed)" },
|
|
1572
|
-
stripDescription: { type: "boolean", description:
|
|
1487
|
+
stripDescription: { type: "boolean", description: STRIP_DESCRIPTION },
|
|
1573
1488
|
},
|
|
1574
1489
|
required: ["workspaceId"],
|
|
1575
1490
|
},
|
|
@@ -1778,7 +1693,7 @@ export const toolDefinitions = [
|
|
|
1778
1693
|
sort: { type: "string", description: SORT_DESCRIPTION },
|
|
1779
1694
|
limit: { type: "number", description: "Omit on first call to see meta.total count" },
|
|
1780
1695
|
page: { type: "number", description: "Page number (1-indexed)" },
|
|
1781
|
-
stripDescription: { type: "boolean", description:
|
|
1696
|
+
stripDescription: { type: "boolean", description: STRIP_DESCRIPTION },
|
|
1782
1697
|
_listTitle: { type: "string", description: "Short descriptive title for the list UI header (e.g., \"Engineering projects\", \"All projects\"). Omit for default." },
|
|
1783
1698
|
},
|
|
1784
1699
|
required: ["workspaceId"],
|
|
@@ -1810,7 +1725,7 @@ export const toolDefinitions = [
|
|
|
1810
1725
|
properties: {
|
|
1811
1726
|
workspaceId: { type: "string", description: "Workspace ID" },
|
|
1812
1727
|
circleId: { type: "string", description: "Circle ID" },
|
|
1813
|
-
stripDescription: { type: "boolean", description:
|
|
1728
|
+
stripDescription: { type: "boolean", description: STRIP_DESCRIPTION },
|
|
1814
1729
|
},
|
|
1815
1730
|
required: ["workspaceId", "circleId"],
|
|
1816
1731
|
},
|
|
@@ -1831,10 +1746,16 @@ export const toolDefinitions = [
|
|
|
1831
1746
|
},
|
|
1832
1747
|
{
|
|
1833
1748
|
name: "nestr_add_workspace_user",
|
|
1834
|
-
description: "
|
|
1835
|
-
+ "
|
|
1836
|
-
+ "
|
|
1837
|
-
+ "
|
|
1749
|
+
description: "Adds a user to a workspace by email, creating the account if needed. It SENDS A REAL INVITE "
|
|
1750
|
+
+ "EMAIL, so confirm with the requester first. Covers seats, membership, plan headcount, adding "
|
|
1751
|
+
+ "a colleague. It provisions only domains the workspace added and Nestr verified: personal "
|
|
1752
|
+
+ "providers (gmail.com) are always refused, and an added domain stays refused until verified. "
|
|
1753
|
+
+ "That limits this tool, not the workspace. The in-app invite (Workspace settings, Users, "
|
|
1754
|
+
+ "\"Invite users\") accepts any address with no domain check, so offer it first on a refusal. "
|
|
1755
|
+
+ "NEVER suggest clearing the workspace domain list: it keeps the requirement, kills the only "
|
|
1756
|
+
+ "way to meet it, and breaks auto-join. Suggest adding a domain only when they control that "
|
|
1757
|
+
+ "company domain. Nestr review "
|
|
1758
|
+
+ "takes up to 24 hours.",
|
|
1838
1759
|
inputSchema: {
|
|
1839
1760
|
type: "object",
|
|
1840
1761
|
properties: {
|
|
@@ -1896,7 +1817,7 @@ export const toolDefinitions = [
|
|
|
1896
1817
|
type: "object",
|
|
1897
1818
|
properties: {
|
|
1898
1819
|
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." },
|
|
1899
|
-
stripDescription: { type: "boolean", description:
|
|
1820
|
+
stripDescription: { type: "boolean", description: STRIP_DESCRIPTION },
|
|
1900
1821
|
},
|
|
1901
1822
|
},
|
|
1902
1823
|
_meta: completableListUi,
|
|
@@ -1922,7 +1843,7 @@ export const toolDefinitions = [
|
|
|
1922
1843
|
type: "object",
|
|
1923
1844
|
properties: {
|
|
1924
1845
|
nestId: { type: "string", description: "Inbox item ID" },
|
|
1925
|
-
stripDescription: { type: "boolean", description:
|
|
1846
|
+
stripDescription: { type: "boolean", description: STRIP_DESCRIPTION },
|
|
1926
1847
|
},
|
|
1927
1848
|
required: ["nestId"],
|
|
1928
1849
|
},
|
|
@@ -2038,7 +1959,7 @@ export const toolDefinitions = [
|
|
|
2038
1959
|
inputSchema: {
|
|
2039
1960
|
type: "object",
|
|
2040
1961
|
properties: {
|
|
2041
|
-
stripDescription: { type: "boolean", description:
|
|
1962
|
+
stripDescription: { type: "boolean", description: STRIP_DESCRIPTION },
|
|
2042
1963
|
},
|
|
2043
1964
|
},
|
|
2044
1965
|
_meta: completableListUi,
|
|
@@ -2284,7 +2205,7 @@ export const toolDefinitions = [
|
|
|
2284
2205
|
},
|
|
2285
2206
|
{
|
|
2286
2207
|
name: "nestr_add_tension_part",
|
|
2287
|
-
description: "Add a governance proposal part to a tension.
|
|
2208
|
+
description: "Add a governance proposal part to a tension. Modes: new item (omit _id, give title/labels); change one (_id plus the changed fields; editing a role copies its accountabilities/domains in, so it reads as a full role edit); delete one (_id plus removeNest:true); election (roleId plus users:[userId], optional due, which assigns or reconfirms the filler and leaves accountabilities/domains untouched). See nestr_help('tension-processing').",
|
|
2288
2209
|
inputSchema: {
|
|
2289
2210
|
type: "object",
|
|
2290
2211
|
properties: {
|
|
@@ -2292,16 +2213,16 @@ export const toolDefinitions = [
|
|
|
2292
2213
|
tensionId: { type: "string", description: "Tension ID" },
|
|
2293
2214
|
_id: { type: "string", description: "ID of an existing governance item to change or remove. Omit to propose a new item." },
|
|
2294
2215
|
title: { type: "string", description: "Title for the governance item" },
|
|
2295
|
-
labels: { type: "array", items: { type: "string" }, description: "
|
|
2216
|
+
labels: { type: "array", items: { type: "string" }, description: "Item type, e.g. ['role'], ['circle'], ['policy'], ['accountability'], ['domain']" },
|
|
2296
2217
|
description: { type: "string", description: "The primary content field — detailed information about the item. Supports Markdown and HTML." },
|
|
2297
|
-
purpose: { type: "string", description:
|
|
2218
|
+
purpose: { type: "string", description: PURPOSE_DESC },
|
|
2298
2219
|
parentId: { type: "string", description: "Parent ID — use to move/restructure items (e.g., move role to different circle)" },
|
|
2299
|
-
users: { type: "array", items: { type: "string" }, description: "User IDs to assign. For an election (with roleId), the
|
|
2300
|
-
due: { type: "string", description: "Due
|
|
2301
|
-
accountabilities: { type: "array", items: { type: "string" }, description: "Accountability titles
|
|
2302
|
-
domains: { type: "array", items: { type: "string" }, description: "Domain titles
|
|
2303
|
-
roleId: { type: "string", description: "
|
|
2304
|
-
removeNest: { type: "boolean", description: "
|
|
2220
|
+
users: { type: "array", items: { type: "string" }, description: "User IDs to assign. For an election (with roleId), the one user being elected." },
|
|
2221
|
+
due: { type: "string", description: "Due or re-election date, ISO. For an election, the term end; omit for no term." },
|
|
2222
|
+
accountabilities: { type: "array", items: { type: "string" }, description: "Accountability titles on a role (replaces all; children tools for individual edits)" },
|
|
2223
|
+
domains: { type: "array", items: { type: "string" }, description: "Domain titles on a role (replaces all; children tools for individual edits)" },
|
|
2224
|
+
roleId: { type: "string", description: "ELECTION mode: the electable role to fill (Facilitator, Secretary, Rep Link or any electable role). Pair with users:[oneUserId] and optional due. Never with _id." },
|
|
2225
|
+
removeNest: { type: "boolean", description: "With _id, propose deleting that governance item; it goes when the proposal is accepted. Not nestr_remove_tension_part, which undoes a part you already added." },
|
|
2305
2226
|
},
|
|
2306
2227
|
required: ["nestId", "tensionId"],
|
|
2307
2228
|
},
|
|
@@ -2318,13 +2239,13 @@ export const toolDefinitions = [
|
|
|
2318
2239
|
partId: { type: "string", description: "Part ID to modify" },
|
|
2319
2240
|
title: { type: "string", description: "Updated title" },
|
|
2320
2241
|
description: { type: "string", description: "Updated description — the primary content field. Supports Markdown and HTML." },
|
|
2321
|
-
purpose: { type: "string", description:
|
|
2242
|
+
purpose: { type: "string", description: PURPOSE_DESC },
|
|
2322
2243
|
labels: { type: "array", items: { type: "string" }, description: "Updated labels" },
|
|
2323
2244
|
parentId: { type: "string", description: "Updated parent ID" },
|
|
2324
2245
|
users: { type: "array", items: { type: "string" }, description: "Updated user assignments" },
|
|
2325
2246
|
due: { type: "string", description: "Updated due date (ISO format)" },
|
|
2326
|
-
accountabilities: { type: "array", items: { type: "string" }, description: "Updated accountabilities (replaces all
|
|
2327
|
-
domains: { type: "array", items: { type: "string" }, description: "Updated domains (replaces all
|
|
2247
|
+
accountabilities: { type: "array", items: { type: "string" }, description: "Updated accountabilities (replaces all; children tools for individual edits)" },
|
|
2248
|
+
domains: { type: "array", items: { type: "string" }, description: "Updated domains (replaces all; children tools for individual edits)" },
|
|
2328
2249
|
},
|
|
2329
2250
|
required: ["nestId", "tensionId", "partId"],
|
|
2330
2251
|
},
|
|
@@ -2356,6 +2277,7 @@ export const toolDefinitions = [
|
|
|
2356
2277
|
},
|
|
2357
2278
|
required: ["nestId", "tensionId", "partId"],
|
|
2358
2279
|
},
|
|
2280
|
+
...readOnly,
|
|
2359
2281
|
},
|
|
2360
2282
|
{
|
|
2361
2283
|
name: "nestr_create_tension_part_child",
|
|
@@ -2499,86 +2421,44 @@ export const toolDefinitions = [
|
|
|
2499
2421
|
},
|
|
2500
2422
|
...readOnly,
|
|
2501
2423
|
},
|
|
2502
|
-
{
|
|
2503
|
-
name: "nestr_list_connector_templates",
|
|
2504
|
-
description: "The connector templates this deployment can add in one click, filtered to the ones it can actually offer. Each carries the vendor's real endpoint, transport, auth strategy and the deployment's OAuth client.\n\nCALL THIS FIRST, before nestr_register_connector, whenever the tool is a known vendor (Xero, HubSpot, Slack, Stripe, GitHub, Notion, Linear and so on). Hand-registering means guessing an endpoint, and a wrong guess authorises cleanly and then fails every call: a Xero connector registered against api.xero.com instead of the template's mcp.xero.com looked healthy in every record and returned 403 forever. Pass the id you find here as templateId to nestr_register_connector. Workspace-admin only.",
|
|
2505
|
-
inputSchema: {
|
|
2506
|
-
type: "object",
|
|
2507
|
-
properties: {
|
|
2508
|
-
workspaceId: { type: "string", description: "Workspace ID whose available connector templates to list" },
|
|
2509
|
-
},
|
|
2510
|
-
required: ["workspaceId"],
|
|
2511
|
-
},
|
|
2512
|
-
...readOnly,
|
|
2513
|
-
},
|
|
2514
2424
|
{
|
|
2515
2425
|
name: "nestr_register_connector",
|
|
2516
|
-
description: "Register a connector in the workspace catalog
|
|
2426
|
+
description: "Register a connector in the workspace catalog: a reusable mcp/cli/api template holding no secret. Workspace-admin only; a non-admin gets AUTH_SCOPE_INSUFFICIENT (call nestr_diagnose on any auth error). Give type ('mcp' and 'api' need a url in config, 'cli' a command) and a unique name; optionally capabilities, exposure, authStrategy. This creates the template only. Flow: register, bind to a role's domain with nestr_bind_connector, then a human or agent connects the account via the credentials field's Connect button. The secret is captured out-of-band, never by the agent.",
|
|
2517
2427
|
inputSchema: {
|
|
2518
2428
|
type: "object",
|
|
2519
2429
|
properties: {
|
|
2520
|
-
templateId: {
|
|
2521
|
-
type: "string",
|
|
2522
|
-
description: "Id of a template from nestr_list_connector_templates. Given this, everything else is filled in from the template and you should omit type/config/capabilities/exposure/authStrategy. ALWAYS prefer this over hand-registering a vendor the deployment already knows.",
|
|
2523
|
-
},
|
|
2524
2430
|
workspaceId: { type: "string", description: "Workspace ID to register the connector in" },
|
|
2525
2431
|
type: {
|
|
2526
2432
|
type: "string",
|
|
2527
2433
|
enum: ["mcp", "cli", "api"],
|
|
2528
|
-
description: "Transport: 'mcp' (MCP server over a url), 'api' (REST endpoint over a url), or 'cli' (a command)
|
|
2529
|
-
},
|
|
2530
|
-
name: {
|
|
2531
|
-
type: "string",
|
|
2532
|
-
description: "Unique connector name within the workspace catalog. Required unless templateId is given, where it defaults to the template's own name.",
|
|
2434
|
+
description: "Transport: 'mcp' (MCP server over a url), 'api' (REST endpoint over a url), or 'cli' (a command)",
|
|
2533
2435
|
},
|
|
2436
|
+
name: { type: "string", description: "Unique connector name within the workspace catalog" },
|
|
2534
2437
|
config: {
|
|
2535
2438
|
type: "object",
|
|
2536
|
-
description: "
|
|
2439
|
+
description: "Transport config, no secret. mcp/api need a url, cli a command. Optional non-secret headers go under headers.",
|
|
2537
2440
|
},
|
|
2538
2441
|
capabilities: {
|
|
2539
2442
|
type: "object",
|
|
2540
|
-
description: "
|
|
2443
|
+
description: "{ discover: boolean, tools: [{ name, description, inputSchema }] }. discover:true lets the connector self-describe its tools at runtime.",
|
|
2541
2444
|
},
|
|
2542
2445
|
exposure: {
|
|
2543
2446
|
type: "object",
|
|
2544
|
-
description: "
|
|
2447
|
+
description: "Which owners may bind: { userAgent: boolean, domainGated: boolean }. domainGated:true allows binding to a role's domain.",
|
|
2545
2448
|
},
|
|
2546
2449
|
authStrategy: {
|
|
2547
2450
|
type: "string",
|
|
2548
2451
|
enum: ["secret", "oauth2"],
|
|
2549
|
-
description: "How a principal connects: 'secret' (
|
|
2452
|
+
description: "How a principal connects: 'secret' (one-time, via the Connect button) or 'oauth2'. The agent never sees it.",
|
|
2550
2453
|
},
|
|
2551
2454
|
},
|
|
2552
|
-
|
|
2553
|
-
// and demanding type and name here is what made the whole template path
|
|
2554
|
-
// unreachable from a client that reads the schema.
|
|
2555
|
-
required: ["workspaceId"],
|
|
2556
|
-
},
|
|
2557
|
-
...mutating,
|
|
2558
|
-
},
|
|
2559
|
-
{
|
|
2560
|
-
name: "nestr_create_agent",
|
|
2561
|
-
description: "Create an agent user in the workspace. Workspace-admin only; a non-admin caller gets AUTH_SCOPE_INSUFFICIENT (call nestr_diagnose on any auth error). The agent is added to the workspace and can then fill roles like a person does.\n\nAn agent and the role it fills are two different things, named differently: the agent carries its own name (Collab), the role is named for the WORK (Marketing). Do not name the role after the agent. Create the agent here, create or find the role with nestr_create_nest, then assign the agent to the role with nestr_update_nest users. Keeping them apart is what lets the agent be replaced without the role losing its purpose, accountabilities and history, and lets one agent fill several roles.\n\nThe agent's instructions do not go here: they belong in a skill nest under the role, which loads whenever the role acts. agentConfig is runtime wiring only.",
|
|
2562
|
-
inputSchema: {
|
|
2563
|
-
type: "object",
|
|
2564
|
-
properties: {
|
|
2565
|
-
workspaceId: { type: "string", description: "Workspace ID to create the agent in" },
|
|
2566
|
-
name: {
|
|
2567
|
-
type: "string",
|
|
2568
|
-
description: "The AGENT's own name, as its identity calls it (e.g. 'Collab'). Not the name of the work: that belongs to the role this agent will fill.",
|
|
2569
|
-
},
|
|
2570
|
-
agentConfig: {
|
|
2571
|
-
type: "object",
|
|
2572
|
-
description: "Runtime wiring, not persona: { runtimeCallbackUrl (https, or http to a *.svc.cluster.local service), tokenTtlSeconds (30-1800) }. Omit for an agent that runs on Nestr's own runtime.",
|
|
2573
|
-
},
|
|
2574
|
-
},
|
|
2575
|
-
required: ["workspaceId", "name"],
|
|
2455
|
+
required: ["workspaceId", "type", "name"],
|
|
2576
2456
|
},
|
|
2577
2457
|
...mutating,
|
|
2578
2458
|
},
|
|
2579
2459
|
{
|
|
2580
2460
|
name: "nestr_bind_connector",
|
|
2581
|
-
description: "Bind a registered connector to an owner so that owner can use it. Owner types: '
|
|
2461
|
+
description: "Bind a registered connector to an owner so that owner can use it. Owner types: 'user' or 'agent' (ownerId is the user ID), 'workspace' (the workspace ID), 'role-domain' (the domain nest ID). A role-domain binding also materialises a credentials field on the domain nest, so the Connect button renders there and the response carries credentialsField { domainId, fieldId, fieldCode }. A human or agent then connects the account through that button; the secret is captured out-of-band, never seen by the agent. Workspace-admin only, and the connector must already be registered and enabled.",
|
|
2582
2462
|
inputSchema: {
|
|
2583
2463
|
type: "object",
|
|
2584
2464
|
properties: {
|
|
@@ -2586,129 +2466,18 @@ export const toolDefinitions = [
|
|
|
2586
2466
|
connectorId: { type: "string", description: "ID of an enabled connector from nestr_list_connectors" },
|
|
2587
2467
|
ownerType: {
|
|
2588
2468
|
type: "string",
|
|
2589
|
-
enum: ["
|
|
2590
|
-
description: "Owner type. 'role'
|
|
2469
|
+
enum: ["user", "agent", "workspace", "role-domain"],
|
|
2470
|
+
description: "Owner type. 'role-domain' binds to a role's domain, materialising a credentials field there.",
|
|
2591
2471
|
},
|
|
2592
2472
|
ownerId: {
|
|
2593
2473
|
type: "string",
|
|
2594
|
-
description: "Owner ID.
|
|
2474
|
+
description: "Owner ID. user/agent: the user ID. workspace: the workspace ID. role-domain: the domain nest ID.",
|
|
2595
2475
|
},
|
|
2596
2476
|
},
|
|
2597
2477
|
required: ["workspaceId", "connectorId", "ownerType", "ownerId"],
|
|
2598
2478
|
},
|
|
2599
2479
|
...mutating,
|
|
2600
2480
|
},
|
|
2601
|
-
{
|
|
2602
|
-
name: "nestr_update_connector",
|
|
2603
|
-
description: "Update a connector in the workspace catalog, or switch it on and off with `enabled`. Workspace-admin only. Switching it off, or narrowing its exposure, takes effect immediately everywhere it is used: the policy is re-read every time a credential is handed out, not only when access was given.",
|
|
2604
|
-
inputSchema: {
|
|
2605
|
-
type: "object",
|
|
2606
|
-
properties: {
|
|
2607
|
-
workspaceId: { type: "string", description: "Workspace ID the connector belongs to" },
|
|
2608
|
-
connectorId: { type: "string", description: "ID of the connector to update" },
|
|
2609
|
-
type: { type: "string", enum: ["mcp", "cli", "api"], description: "Transport" },
|
|
2610
|
-
name: { type: "string", description: "Unique connector name within the workspace catalog" },
|
|
2611
|
-
config: { type: "object", description: "Per-type transport config, no secret" },
|
|
2612
|
-
capabilities: { type: "object", description: "Capability descriptor" },
|
|
2613
|
-
exposure: { type: "object", description: "Exposure policy: { userAgent, domainGated }" },
|
|
2614
|
-
authStrategy: { type: "string", enum: ["secret", "oauth2"], description: "How a principal connects" },
|
|
2615
|
-
enabled: { type: "boolean", description: "Switch the connector on or off in this workspace" },
|
|
2616
|
-
},
|
|
2617
|
-
required: ["workspaceId", "connectorId"],
|
|
2618
|
-
},
|
|
2619
|
-
...mutating,
|
|
2620
|
-
},
|
|
2621
|
-
{
|
|
2622
|
-
name: "nestr_remove_connector",
|
|
2623
|
-
description: "Remove a connector from the workspace catalog. Workspace-admin only. Bindings that named it stop resolving, so prefer nestr_update_connector with enabled:false when you only want to pause it.",
|
|
2624
|
-
inputSchema: {
|
|
2625
|
-
type: "object",
|
|
2626
|
-
properties: {
|
|
2627
|
-
workspaceId: { type: "string", description: "Workspace ID the connector belongs to" },
|
|
2628
|
-
connectorId: { type: "string", description: "ID of the connector to remove" },
|
|
2629
|
-
},
|
|
2630
|
-
required: ["workspaceId", "connectorId"],
|
|
2631
|
-
},
|
|
2632
|
-
...destructive,
|
|
2633
|
-
},
|
|
2634
|
-
{
|
|
2635
|
-
name: "nestr_list_connections",
|
|
2636
|
-
description: "List who has access to what in this workspace: each binding's connector, its owner (a role's domain, a person, an agent, or the whole workspace), and who holds a credential on it. Shows when an agent is using a person's account, and never returns a secret. Use it to check whether access already exists before giving more, and to find the connectionId for nestr_get_connect_link.",
|
|
2637
|
-
inputSchema: {
|
|
2638
|
-
type: "object",
|
|
2639
|
-
properties: {
|
|
2640
|
-
workspaceId: { type: "string", description: "Workspace ID whose bindings to list" },
|
|
2641
|
-
includeDisabled: { type: "boolean", description: "Include removed bindings. Default false." },
|
|
2642
|
-
},
|
|
2643
|
-
required: ["workspaceId"],
|
|
2644
|
-
},
|
|
2645
|
-
},
|
|
2646
|
-
{
|
|
2647
|
-
name: "nestr_remove_connection",
|
|
2648
|
-
description: "Take a connector off an owner: the binding is removed and every credential on it revoked. Workspace-admin only. A domain left holding nothing goes back to being an ordinary descriptive domain.",
|
|
2649
|
-
inputSchema: {
|
|
2650
|
-
type: "object",
|
|
2651
|
-
properties: {
|
|
2652
|
-
workspaceId: { type: "string", description: "Workspace ID the binding belongs to" },
|
|
2653
|
-
connectionId: { type: "string", description: "Binding ID from nestr_list_connections" },
|
|
2654
|
-
},
|
|
2655
|
-
required: ["workspaceId", "connectionId"],
|
|
2656
|
-
},
|
|
2657
|
-
...destructive,
|
|
2658
|
-
},
|
|
2659
|
-
{
|
|
2660
|
-
name: "nestr_get_connect_link",
|
|
2661
|
-
description: "Get a link a PERSON opens to connect an account for a binding. This is how you finish setting up access: you can register a connector and give a role access, but you must never handle a raw token, so the sign-in or key entry happens behind this link. The link carries no authority — whoever opens it is checked then. Give it to the user in your reply.",
|
|
2662
|
-
inputSchema: {
|
|
2663
|
-
type: "object",
|
|
2664
|
-
properties: {
|
|
2665
|
-
workspaceId: { type: "string", description: "Workspace ID the binding belongs to" },
|
|
2666
|
-
connectionId: { type: "string", description: "Binding ID from nestr_list_connections" },
|
|
2667
|
-
},
|
|
2668
|
-
required: ["workspaceId", "connectionId"],
|
|
2669
|
-
},
|
|
2670
|
-
...mutating,
|
|
2671
|
-
},
|
|
2672
|
-
{
|
|
2673
|
-
name: "nestr_revoke_connection_credential",
|
|
2674
|
-
description: "Revoke the calling user's credential on a binding. The binding stays, so access can be restored by connecting again. Use nestr_remove_connection to remove the access entirely.",
|
|
2675
|
-
inputSchema: {
|
|
2676
|
-
type: "object",
|
|
2677
|
-
properties: {
|
|
2678
|
-
workspaceId: { type: "string", description: "Workspace ID the binding belongs to" },
|
|
2679
|
-
connectionId: { type: "string", description: "Binding ID from nestr_list_connections" },
|
|
2680
|
-
},
|
|
2681
|
-
required: ["workspaceId", "connectionId"],
|
|
2682
|
-
},
|
|
2683
|
-
...destructive,
|
|
2684
|
-
},
|
|
2685
|
-
{
|
|
2686
|
-
name: "nestr_get_agent_connectors",
|
|
2687
|
-
description: "What an agent can and cannot use, and why. Groups each connector by where the grant comes from (its own binding, the workspace, or a role it fills) and, when unavailable, names the reason: no credential yet, the connector is disabled, it has no usable tools, or it no longer allows this kind of access. Reach for this when an agent seems to be missing something it should have.",
|
|
2688
|
-
inputSchema: {
|
|
2689
|
-
type: "object",
|
|
2690
|
-
properties: {
|
|
2691
|
-
workspaceId: { type: "string", description: "Workspace ID the agent belongs to" },
|
|
2692
|
-
agentUserId: { type: "string", description: "The agent's bot user ID" },
|
|
2693
|
-
},
|
|
2694
|
-
required: ["workspaceId", "agentUserId"],
|
|
2695
|
-
},
|
|
2696
|
-
},
|
|
2697
|
-
{
|
|
2698
|
-
name: "nestr_run_agent",
|
|
2699
|
-
description: "Run an agent now on a nest, optionally saying what the run is for. This is how one agent asks another to do something. The run is pinned to the nest you name and reports back there. You need assign rights on that nest and the agent must fill or be assigned to it, so this cannot run an agent anywhere in the workspace.",
|
|
2700
|
-
inputSchema: {
|
|
2701
|
-
type: "object",
|
|
2702
|
-
properties: {
|
|
2703
|
-
workspaceId: { type: "string", description: "Workspace ID the agent belongs to" },
|
|
2704
|
-
agentUserId: { type: "string", description: "The agent's bot user ID" },
|
|
2705
|
-
nestId: { type: "string", description: "The nest the run is pinned to: a role, a project, a task" },
|
|
2706
|
-
message: { type: "string", description: "What this run is for. Omit for a plain 'advance this item' run." },
|
|
2707
|
-
},
|
|
2708
|
-
required: ["workspaceId", "agentUserId", "nestId"],
|
|
2709
|
-
},
|
|
2710
|
-
...mutating,
|
|
2711
|
-
},
|
|
2712
2481
|
{
|
|
2713
2482
|
name: "nestr_get_nest_files",
|
|
2714
2483
|
description: "List a nest's file attachments. Images pasted into the nest's text are deliberately excluded — they belong to the text that references them; the inline_images hint counts those and their ids come from the references in the content. A comment ID works too — files are keyed by nestId, so pass a comment ID to see files attached to that comment. Returns each file's id, name, contentType and size. Use nestr_read_file with a returned id to read one (images come back as viewable image content). Auth: any valid token with access to the nest.",
|
|
@@ -2764,6 +2533,13 @@ export const toolDefinitions = [
|
|
|
2764
2533
|
...destructive,
|
|
2765
2534
|
},
|
|
2766
2535
|
];
|
|
2536
|
+
// The read-only surface (/mcp/readonly) serves an authenticated bearer but only
|
|
2537
|
+
// tools that cannot change anything. Derived from the annotations rather than a
|
|
2538
|
+
// hand-kept list, so a new tool is denied until someone marks it readOnly: the
|
|
2539
|
+
// failure mode of forgetting is a missing capability, not a silent write.
|
|
2540
|
+
export const READONLY_TOOL_NAMES = new Set(toolDefinitions
|
|
2541
|
+
.filter((t) => t.annotations?.readOnlyHint === true)
|
|
2542
|
+
.map((t) => t.name));
|
|
2767
2543
|
// Strip description fields from nest objects in response data
|
|
2768
2544
|
export function stripDescriptionFields(data) {
|
|
2769
2545
|
if (Array.isArray(data)) {
|
|
@@ -2889,6 +2665,18 @@ async function _handleToolCall(client, name, args, context) {
|
|
|
2889
2665
|
return formatResult(PUBLIC_GUEST_ME);
|
|
2890
2666
|
}
|
|
2891
2667
|
}
|
|
2668
|
+
// READ-ONLY surface gate (defense in depth — the readonly route also filters
|
|
2669
|
+
// the advertised tool list). Anything not marked readOnlyHint is refused,
|
|
2670
|
+
// including tools carrying no annotation at all.
|
|
2671
|
+
if (context?.isReadOnly && !READONLY_TOOL_NAMES.has(name)) {
|
|
2672
|
+
return formatError({
|
|
2673
|
+
error: true,
|
|
2674
|
+
code: "AUTH_SCOPE_INSUFFICIENT",
|
|
2675
|
+
message: `Tool '${name}' is not available on the read-only Nestr MCP. This workspace is out of AI credit, so the agent can read but not change anything.`,
|
|
2676
|
+
retryable: false,
|
|
2677
|
+
hint: "Add AI credit, or raise the monthly ceiling if that is what was reached, to restore the full toolset.",
|
|
2678
|
+
});
|
|
2679
|
+
}
|
|
2892
2680
|
switch (name) {
|
|
2893
2681
|
case "nestr_help": {
|
|
2894
2682
|
const parsed = schemas.help.parse(args);
|
|
@@ -3839,46 +3627,9 @@ async function _handleToolCall(client, name, args, context) {
|
|
|
3839
3627
|
const connectors = await client.listConnectors(parsed.workspaceId);
|
|
3840
3628
|
return formatResult(connectors);
|
|
3841
3629
|
}
|
|
3842
|
-
case "nestr_list_connector_templates": {
|
|
3843
|
-
const parsed = schemas.listConnectorTemplates.parse(args);
|
|
3844
|
-
const listed = await client.listConnectorTemplates(parsed.workspaceId);
|
|
3845
|
-
const templates = listed.templates;
|
|
3846
|
-
if (!Array.isArray(templates) || templates.length === 0) {
|
|
3847
|
-
return { content: [{ type: "text", text: "This deployment offers no connector templates." }] };
|
|
3848
|
-
}
|
|
3849
|
-
// Enriched so the hint arrives as the register call itself, pre-filled
|
|
3850
|
-
// with the template id, the same treatment every other hint gets.
|
|
3851
|
-
const enrichedTemplates = enrichHints({
|
|
3852
|
-
workspaceId: parsed.workspaceId,
|
|
3853
|
-
...(listed.hints ? { hints: listed.hints } : {}),
|
|
3854
|
-
});
|
|
3855
|
-
return formatResult({
|
|
3856
|
-
message: "Pass the id of the one you want as templateId to nestr_register_connector. It carries the vendor's endpoint, transport and auth strategy, so nothing has to be guessed. Do not rebuild one of these by hand: a hand-built copy has no OAuth client and fails at first use.",
|
|
3857
|
-
templates,
|
|
3858
|
-
...(enrichedTemplates.hints ? { hints: enrichedTemplates.hints } : {}),
|
|
3859
|
-
});
|
|
3860
|
-
}
|
|
3861
3630
|
case "nestr_register_connector": {
|
|
3862
3631
|
const parsed = schemas.registerConnector.parse(args);
|
|
3863
|
-
|
|
3864
|
-
const fromTemplate = await client.registerConnector(parsed.workspaceId, {
|
|
3865
|
-
templateId: parsed.templateId,
|
|
3866
|
-
...(parsed.name ? { name: parsed.name } : {}),
|
|
3867
|
-
});
|
|
3868
|
-
return formatResult({
|
|
3869
|
-
message: "Connector registered from a template, so its endpoint and auth strategy are the vendor's own. Next, bind it to a role's DOMAIN with nestr_bind_connector, then a human connects the account via the Connect button.",
|
|
3870
|
-
connector: fromTemplate.connector,
|
|
3871
|
-
});
|
|
3872
|
-
}
|
|
3873
|
-
if (!parsed.type || !parsed.name) {
|
|
3874
|
-
return formatError({
|
|
3875
|
-
error: true,
|
|
3876
|
-
code: "VALIDATION",
|
|
3877
|
-
message: "Without templateId, both type and name are required. Call nestr_list_connector_templates first: a known vendor almost always has one.",
|
|
3878
|
-
retryable: false,
|
|
3879
|
-
});
|
|
3880
|
-
}
|
|
3881
|
-
const registered = await client.registerConnector(parsed.workspaceId, {
|
|
3632
|
+
const connector = await client.registerConnector(parsed.workspaceId, {
|
|
3882
3633
|
type: parsed.type,
|
|
3883
3634
|
name: parsed.name,
|
|
3884
3635
|
config: parsed.config,
|
|
@@ -3886,27 +3637,9 @@ async function _handleToolCall(client, name, args, context) {
|
|
|
3886
3637
|
exposure: parsed.exposure,
|
|
3887
3638
|
authStrategy: parsed.authStrategy,
|
|
3888
3639
|
});
|
|
3889
|
-
// Enriched so a template hint arrives as a tool call the model can make,
|
|
3890
|
-
// the same treatment every other hint gets. workspaceId is on the entry,
|
|
3891
|
-
// which is what lets enrichHints work on something that is not a nest.
|
|
3892
|
-
const enriched = enrichHints({
|
|
3893
|
-
...registered.connector,
|
|
3894
|
-
...(registered.hints ? { hints: registered.hints } : {}),
|
|
3895
|
-
});
|
|
3896
|
-
return formatResult({
|
|
3897
|
-
message: "Connector registered. It does nothing yet: nobody has access to it. Give a ROLE access with nestr_bind_connector { ownerType: 'role', ownerId: <role nest id> } and its domain is created under that role, which is the usual onboarding path. Then get a link with nestr_get_connect_link and give it to a person to open, since the credential must never pass through you.",
|
|
3898
|
-
connector: enriched,
|
|
3899
|
-
});
|
|
3900
|
-
}
|
|
3901
|
-
case "nestr_create_agent": {
|
|
3902
|
-
const parsed = schemas.createAgent.parse(args);
|
|
3903
|
-
const agent = await client.createAgent(parsed.workspaceId, {
|
|
3904
|
-
name: parsed.name,
|
|
3905
|
-
agentConfig: parsed.agentConfig,
|
|
3906
|
-
});
|
|
3907
3640
|
return formatResult({
|
|
3908
|
-
message: "
|
|
3909
|
-
|
|
3641
|
+
message: "Connector registered. Next, bind it to an owner with nestr_bind_connector (e.g. a role's domain), then a human or agent connects the account via the credentials field's Connect button.",
|
|
3642
|
+
connector,
|
|
3910
3643
|
});
|
|
3911
3644
|
}
|
|
3912
3645
|
case "nestr_bind_connector": {
|
|
@@ -3915,75 +3648,14 @@ async function _handleToolCall(client, name, args, context) {
|
|
|
3915
3648
|
connectorId: parsed.connectorId,
|
|
3916
3649
|
owner: { type: parsed.ownerType, id: parsed.ownerId },
|
|
3917
3650
|
});
|
|
3918
|
-
//
|
|
3919
|
-
//
|
|
3920
|
-
//
|
|
3921
|
-
const message = parsed.ownerType === "role
|
|
3922
|
-
? "
|
|
3923
|
-
: "
|
|
3651
|
+
// For a role-domain binding the API materialises a credentials field on
|
|
3652
|
+
// the domain; surface that explicitly so the caller knows the Connect
|
|
3653
|
+
// button now renders there and the secret is captured out-of-band.
|
|
3654
|
+
const message = parsed.ownerType === "role-domain"
|
|
3655
|
+
? "Connector bound to the role's domain. A credentials field was materialised on the domain (see credentialsField) so the role can use the connector and the Connect button renders. A human or agent now connects the account via that button; the secret is captured out-of-band and never by the agent."
|
|
3656
|
+
: "Connector bound to the owner. The owner now connects the account out-of-band; the secret is never seen by the agent.";
|
|
3924
3657
|
return formatResult({ message, connection });
|
|
3925
3658
|
}
|
|
3926
|
-
case "nestr_update_connector": {
|
|
3927
|
-
const parsed = schemas.updateConnector.parse(args);
|
|
3928
|
-
const { workspaceId, connectorId, ...updates } = parsed;
|
|
3929
|
-
const connector = await client.updateConnector(workspaceId, connectorId, updates);
|
|
3930
|
-
return formatResult({ message: "Connector updated.", connector });
|
|
3931
|
-
}
|
|
3932
|
-
case "nestr_remove_connector": {
|
|
3933
|
-
const parsed = schemas.removeConnector.parse(args);
|
|
3934
|
-
await client.removeConnector(parsed.workspaceId, parsed.connectorId);
|
|
3935
|
-
return formatResult({
|
|
3936
|
-
message: "Connector removed from the catalog. Bindings that named it stop resolving.",
|
|
3937
|
-
connectorId: parsed.connectorId,
|
|
3938
|
-
});
|
|
3939
|
-
}
|
|
3940
|
-
case "nestr_list_connections": {
|
|
3941
|
-
const parsed = schemas.listConnections.parse(args);
|
|
3942
|
-
const connections = await client.listConnections(parsed.workspaceId, {
|
|
3943
|
-
includeDisabled: parsed.includeDisabled,
|
|
3944
|
-
});
|
|
3945
|
-
return formatResult(connections);
|
|
3946
|
-
}
|
|
3947
|
-
case "nestr_remove_connection": {
|
|
3948
|
-
const parsed = schemas.removeConnection.parse(args);
|
|
3949
|
-
const result = await client.removeConnection(parsed.workspaceId, parsed.connectionId);
|
|
3950
|
-
return formatResult({
|
|
3951
|
-
message: `Access removed. ${result.revokedCount} credential(s) revoked.`,
|
|
3952
|
-
connectionId: parsed.connectionId,
|
|
3953
|
-
});
|
|
3954
|
-
}
|
|
3955
|
-
case "nestr_get_connect_link": {
|
|
3956
|
-
const parsed = schemas.getConnectLink.parse(args);
|
|
3957
|
-
const link = await client.getConnectLink(parsed.workspaceId, parsed.connectionId);
|
|
3958
|
-
return formatResult({
|
|
3959
|
-
message: "Give this link to a person to open. They complete the sign-in or paste the key there, so the secret never passes through you.",
|
|
3960
|
-
...link,
|
|
3961
|
-
});
|
|
3962
|
-
}
|
|
3963
|
-
case "nestr_revoke_connection_credential": {
|
|
3964
|
-
const parsed = schemas.revokeConnectionCredential.parse(args);
|
|
3965
|
-
await client.revokeConnectionCredential(parsed.workspaceId, parsed.connectionId);
|
|
3966
|
-
return formatResult({
|
|
3967
|
-
message: "Credential revoked. The binding stays; connect again to restore access.",
|
|
3968
|
-
connectionId: parsed.connectionId,
|
|
3969
|
-
});
|
|
3970
|
-
}
|
|
3971
|
-
case "nestr_get_agent_connectors": {
|
|
3972
|
-
const parsed = schemas.getAgentConnectorReach.parse(args);
|
|
3973
|
-
const reach = await client.getAgentConnectorReach(parsed.workspaceId, parsed.agentUserId);
|
|
3974
|
-
return formatResult(reach);
|
|
3975
|
-
}
|
|
3976
|
-
case "nestr_run_agent": {
|
|
3977
|
-
const parsed = schemas.runAgent.parse(args);
|
|
3978
|
-
const result = await client.runAgent(parsed.workspaceId, parsed.agentUserId, {
|
|
3979
|
-
nestId: parsed.nestId,
|
|
3980
|
-
message: parsed.message,
|
|
3981
|
-
});
|
|
3982
|
-
return formatResult({
|
|
3983
|
-
message: "The agent was dispatched. It reports back on the item it was run on.",
|
|
3984
|
-
...result,
|
|
3985
|
-
});
|
|
3986
|
-
}
|
|
3987
3659
|
case "nestr_get_nest_files": {
|
|
3988
3660
|
const parsed = schemas.getNestFiles.parse(args);
|
|
3989
3661
|
const files = await client.getNestFiles(parsed.nestId);
|