@sutraspaces/mcp-server 1.0.1 → 1.1.2
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 +69 -8
- package/package.json +5 -5
- package/src/client.js +4 -0
- package/src/index.js +18 -2
- package/src/resources/admin-api.js +136 -0
- package/src/resources/design.js +77 -0
- package/src/tools/automations.js +89 -0
- package/src/tools/blog.js +204 -0
- package/src/tools/design.js +136 -0
- package/src/tools/documents.js +108 -0
- package/src/tools/help-center.js +498 -0
- package/src/tools/media.js +86 -0
- package/src/tools/members.js +71 -3
- package/src/tools/properties.js +69 -0
- package/src/tools/spaces.js +95 -3
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Sutra MCP Server
|
|
2
2
|
|
|
3
|
-
An
|
|
3
|
+
An MCP server that connects AI agents to the Sutra Admin API. Manage spaces, members, contacts, content, discussions, surveys, plans, broadcasts, and more — all through your AI tools. See the [Sutra developer docs](https://sutra.co/developers/docs) for API documentation.
|
|
4
4
|
|
|
5
5
|
## Quick Start
|
|
6
6
|
|
|
@@ -52,6 +52,8 @@ Point your MCP client at `npx -y @sutraspaces/mcp-server` with the `SUTRA_API_TO
|
|
|
52
52
|
|---|---|---|
|
|
53
53
|
| `SUTRA_API_TOKEN` | Yes | Your Sutra Admin API token (`sutra_live_sk_...`) |
|
|
54
54
|
| `SUTRA_BASE_URL` | No | Override the API URL (default: `https://api.sutra.co/api/admin/v1`) |
|
|
55
|
+
| `SUTRA_HELP_CENTER_TOKEN` | No | Internal scoped admin key (`sutra_admin_...`) for Lotus Help Center tools |
|
|
56
|
+
| `SUTRA_HELP_CENTER_BASE_URL` | No | Override the Help Center API URL (default: `https://api.sutra.co/api/v4/lotus/help`) |
|
|
55
57
|
|
|
56
58
|
## Available Tools
|
|
57
59
|
|
|
@@ -60,15 +62,22 @@ Point your MCP client at `npx -y @sutraspaces/mcp-server` with the `SUTRA_API_TO
|
|
|
60
62
|
|
|
61
63
|
### Spaces
|
|
62
64
|
- **list_spaces** — List all spaces the API token can access
|
|
65
|
+
- **list_membership_spaces** — List basic read-only information about spaces where the API token owner is a member
|
|
63
66
|
- **get_space** — Get details for a single space
|
|
64
67
|
- **list_child_spaces** — List direct child spaces
|
|
65
68
|
- **create_space** — Create a new space (top-level or child)
|
|
69
|
+
- **create_child_space** — Create a child space and make it visible through placement
|
|
70
|
+
- **attach_child_space** — Attach an existing space under a parent and make it visible
|
|
71
|
+
- **update_child_space_placement** — Move or repair a child space display
|
|
72
|
+
- **detach_child_space** — Detach a child from one parent without deleting it
|
|
66
73
|
- **update_space** — Update space name, description, type, privacy, or state
|
|
67
74
|
- **delete_space** — Delete/archive a space
|
|
68
75
|
- **reorder_child_spaces** — Reorder children within a parent
|
|
69
76
|
|
|
77
|
+
Use `sp_...` IDs, not slugs. For normal course and section pages, use `placement.surface = "auto"`.
|
|
78
|
+
|
|
70
79
|
### Members
|
|
71
|
-
- **list_members** — List space members with optional email
|
|
80
|
+
- **list_members** — List space members with optional email, user_id, search, role, state, and custom property filtering
|
|
72
81
|
- **get_member** — Get member details
|
|
73
82
|
- **add_member** — Add a member by email or user ID
|
|
74
83
|
- **update_member** — Update role or notification settings
|
|
@@ -77,15 +86,42 @@ Point your MCP client at `npx -y @sutraspaces/mcp-server` with the `SUTRA_API_TO
|
|
|
77
86
|
- **bulk_add_members** — Add up to 100 members at once
|
|
78
87
|
- **bulk_remove_members** — Remove up to 100 members at once
|
|
79
88
|
|
|
89
|
+
### Contacts
|
|
90
|
+
- **list_contacts** — List CRM contacts from the Contacts/Interested layer with optional email, search, and custom property filtering
|
|
91
|
+
- **get_contact** — Get contact details
|
|
92
|
+
- **get_contact_property_values** — Read property values for a contact
|
|
93
|
+
- **update_contact_property_values** — Set or clear property values for a contact
|
|
94
|
+
- **bulk_update_contact_property_values** — Set or clear property values for up to 100 contacts at once
|
|
95
|
+
|
|
80
96
|
### Content & Discussions
|
|
81
|
-
- **
|
|
97
|
+
- **get_document_capabilities** / **get_document** — Read document editing rules and the visible Tiptap document
|
|
98
|
+
- **replace_document** / **insert_document_nodes** / **update_document_node** / **delete_document_node** / **move_document_node** — Edit visible document content by Tiptap node UID
|
|
99
|
+
- **list_content** / **get_content_block** / **create_content** / **update_content** / **delete_content** — Legacy content-block mirror tools
|
|
82
100
|
- **reorder_content** — Reorder content blocks within a space
|
|
101
|
+
|
|
102
|
+
### Media Uploads
|
|
103
|
+
- **create_media_upload** — Create a server-mediated direct-to-S3 upload session
|
|
104
|
+
- **complete_media_upload** — Verify the uploaded object and queue video processing
|
|
105
|
+
- **get_media_upload** — Read upload and processing status
|
|
106
|
+
- **cancel_media_upload** — Abandon a pending upload
|
|
107
|
+
- **create_media_reference** — Insert a Loom, YouTube, or Vimeo embed without uploading a file
|
|
108
|
+
|
|
109
|
+
### Design Tools
|
|
110
|
+
|
|
111
|
+
- **get_design_capabilities** — Read Sutra's renderer-aware design manifest
|
|
112
|
+
- **get_space_design** — Read current page-design nodes and digest for a space
|
|
113
|
+
- **validate_space_design** — Validate proposed page-design nodes
|
|
114
|
+
- **create_or_update_design_draft** — Create or update a digest-guarded design draft
|
|
115
|
+
- **publish_design_draft** — Publish a draft with conflict and destructive-omission protection
|
|
116
|
+
- **restore_design_draft** — Restore the pre-publish backup for a published design draft
|
|
117
|
+
- **import_design_asset** — Re-host an external image URL and return the canonical Sutra URL
|
|
83
118
|
- **list_messages** / **get_message** / **create_message** / **update_message** / **delete_message** — Manage discussion messages
|
|
84
119
|
- **list_reflections** / **get_reflection** / **create_reflection** / **update_reflection** / **delete_reflection** — Manage threaded replies
|
|
85
120
|
|
|
86
121
|
### Properties
|
|
87
122
|
- **list_member_properties** / **create_member_property** / **update_member_property** / **delete_member_property** — Manage custom member property definitions
|
|
88
|
-
- **get_member_property_values** / **update_member_property_values** — Read and set property values
|
|
123
|
+
- **get_member_property_values** / **update_member_property_values** / **bulk_update_member_property_values** — Read, set, and bulk set property values for members
|
|
124
|
+
- **get_contact_property_values** / **update_contact_property_values** / **bulk_update_contact_property_values** — Read, set, and bulk set property values for contacts
|
|
89
125
|
- **list_space_properties** / **create_space_property** / **update_space_property** / **delete_space_property** — Manage custom space property definitions
|
|
90
126
|
- **get_space_property_values** / **update_space_property_values** — Read and set property values per space
|
|
91
127
|
|
|
@@ -111,27 +147,52 @@ Point your MCP client at `npx -y @sutraspaces/mcp-server` with the `SUTRA_API_TO
|
|
|
111
147
|
- **send_broadcast** — Send a broadcast to space members
|
|
112
148
|
- **get_broadcast_delivery_status** — Check delivery progress
|
|
113
149
|
|
|
150
|
+
### Help Center
|
|
151
|
+
These tools are available only when `SUTRA_HELP_CENTER_TOKEN` is set.
|
|
152
|
+
|
|
153
|
+
- **help_list_articles** / **help_get_article** — Read Lotus Help Center articles, metadata, and version history
|
|
154
|
+
- **help_create_article** / **help_update_article** — Human/admin-style create and update through Lotus
|
|
155
|
+
- **help_propose_article** — Submit an AI-authored draft as `ai_cobolt` and mark it pending human review
|
|
156
|
+
- **help_propose_article_update** — Submit AI-authored changes to an existing article without publishing them
|
|
157
|
+
- **help_review_article** — Approve or reject a pending article version
|
|
158
|
+
- **help_publish_article** — Explicitly publish an article version
|
|
159
|
+
- **help_list_collections** / **help_create_collection** / **help_update_collection** / **help_delete_collection** — Manage Help Center collections
|
|
160
|
+
|
|
161
|
+
## Available Resources
|
|
162
|
+
|
|
163
|
+
- **sutra://admin-api/overview** — Core Admin API concepts, public ID prefixes, scopes, pagination, and filtering
|
|
164
|
+
- **sutra://admin-api/membership-spaces** — Difference between admin-manageable spaces and limited membership-space inventory
|
|
165
|
+
- **sutra://admin-api/contacts-properties** — Contact listing and member/contact property value workflows
|
|
166
|
+
- **sutra://admin-api/people-filtering** — Member and contact search, email, state, role, and custom property filtering
|
|
167
|
+
- **sutra://design/capabilities** — Renderer-aware design rules and supported fonts for Sutra page-design tools
|
|
168
|
+
|
|
114
169
|
## API Concepts
|
|
115
170
|
|
|
116
171
|
**Spaces** are the core building block — they can be courses, communities, forums, or any structured container. Spaces form hierarchies through parent-child relationships.
|
|
117
172
|
|
|
173
|
+
**Membership spaces** are spaces where the API token owner is enrolled as a member. Use `list_membership_spaces` for basic read-only inventory. Use `list_spaces` for spaces where the token has admin access; membership-only spaces may still return 404 or 403 from admin subresource tools.
|
|
174
|
+
|
|
118
175
|
**Members** belong to spaces. They have roles (member, editor, moderator) and can have custom properties attached.
|
|
119
176
|
|
|
177
|
+
**Contacts** are CRM people in a space who are not enrolled members. Contacts use the same member property definitions as members, so AI agents can tag imported contacts before they join.
|
|
178
|
+
|
|
120
179
|
**Content blocks** are structured content created by facilitators. **Messages** are discussion posts from participants. **Reflections** are threaded replies to messages.
|
|
121
180
|
|
|
122
|
-
**All IDs** use readable prefixes: `sp_` (space), `mem_` (member), `usr_` (user), `blk_` (block/message), `reply_` (reflection), `surv_` (survey), `plan_` (plan), `enr_` (enrollment), `pay_` (payment), `cpn_` (coupon), `bcst_` (broadcast), `inv_` (invitation), `mprop_` / `sprop_` (property definitions).
|
|
181
|
+
**All IDs** use readable prefixes: `sp_` (space), `mem_` (member), `contact_` (contact), `usr_` (user), `blk_` (block/message), `reply_` (reflection), `surv_` (survey), `plan_` (plan), `enr_` (enrollment), `pay_` (payment), `cpn_` (coupon), `bcst_` (broadcast), `inv_` (invitation), `mprop_` / `sprop_` (property definitions).
|
|
123
182
|
|
|
124
183
|
**Pagination** is cursor-based. List endpoints return `{ data, pagination: { next_cursor, has_more } }`. Pass `cursor` to get the next page.
|
|
125
184
|
|
|
126
185
|
**Filtering & Search** — most list endpoints accept optional query parameters to narrow results:
|
|
127
|
-
- `q` — case-insensitive text search (spaces by name, members by name/email, surveys/broadcasts/plans by title/name, coupons by code)
|
|
186
|
+
- `q` — case-insensitive text search (spaces by name, members/contacts by name/email when scoped, surveys/broadcasts/plans by title/name, coupons by code)
|
|
128
187
|
- `state` / `status` — exact-match enum filters (e.g. `state=active` for spaces, `status=sent` for broadcasts)
|
|
129
188
|
- `role` — filter members or invitations by role
|
|
130
189
|
- `type` / `privacy` / `frequency` — filter by type, privacy level, or billing frequency
|
|
131
190
|
- `user_id` — filter messages or payments by author/user
|
|
132
|
-
- `email` — filter invitations by email (requires `members.email:read` scope)
|
|
191
|
+
- `email` — filter members, contacts, or invitations by email (requires `members.email:read` scope)
|
|
192
|
+
- `property_key` / `property_value` — filter members or contacts by one custom member property (requires `member_properties:read` scope)
|
|
193
|
+
- `property_filters` — filter members or contacts by multiple custom member properties
|
|
133
194
|
|
|
134
|
-
**Scopes** control what the API token can access. Tokens have read/write scope pairs like `spaces:read`, `members:write`, etc. Operations that exceed the token's scopes will return 403.
|
|
195
|
+
**Scopes** control what the API token can access. Tokens have read/write scope pairs like `spaces:read`, `members:write`, etc. `membership_spaces:read` is required for read-only membership-space inventory. Operations that exceed the token's scopes will return 403.
|
|
135
196
|
|
|
136
197
|
## Architecture
|
|
137
198
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sutraspaces/mcp-server",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "MCP server for the Sutra Admin API — manage spaces, members, content, and more via AI agents",
|
|
3
|
+
"version": "1.1.2",
|
|
4
|
+
"description": "MCP server for the Sutra Admin API — manage spaces, members, contacts, content, and more via AI agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
7
7
|
"bin": {
|
|
@@ -33,12 +33,12 @@
|
|
|
33
33
|
],
|
|
34
34
|
"author": "Sutra <support@sutra.co> (https://sutra.co)",
|
|
35
35
|
"license": "MIT",
|
|
36
|
-
"homepage": "https://github.com/
|
|
36
|
+
"homepage": "https://github.com/lorenzsell/sutra-mcp",
|
|
37
37
|
"repository": {
|
|
38
38
|
"type": "git",
|
|
39
|
-
"url": "https://github.com/
|
|
39
|
+
"url": "git+https://github.com/lorenzsell/sutra-mcp.git"
|
|
40
40
|
},
|
|
41
41
|
"bugs": {
|
|
42
|
-
"url": "https://github.com/
|
|
42
|
+
"url": "https://github.com/lorenzsell/sutra-mcp/issues"
|
|
43
43
|
}
|
|
44
44
|
}
|
package/src/client.js
CHANGED
|
@@ -47,6 +47,10 @@ export class SutraAdminClient {
|
|
|
47
47
|
return this.request("PATCH", path, { body, headers });
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
put(path, body, headers) {
|
|
51
|
+
return this.request("PUT", path, { body, headers });
|
|
52
|
+
}
|
|
53
|
+
|
|
50
54
|
delete(path, headers) {
|
|
51
55
|
return this.request("DELETE", path, { headers });
|
|
52
56
|
}
|
package/src/index.js
CHANGED
|
@@ -7,6 +7,8 @@ import { SutraAdminClient } from "./client.js";
|
|
|
7
7
|
import { registerSpaceTools } from "./tools/spaces.js";
|
|
8
8
|
import { registerMemberTools } from "./tools/members.js";
|
|
9
9
|
import { registerContentTools } from "./tools/content.js";
|
|
10
|
+
import { registerDocumentTools } from "./tools/documents.js";
|
|
11
|
+
import { registerMediaTools } from "./tools/media.js";
|
|
10
12
|
import { registerPropertyTools } from "./tools/properties.js";
|
|
11
13
|
import { registerInvitationTools } from "./tools/invitations.js";
|
|
12
14
|
import { registerSurveyTools } from "./tools/surveys.js";
|
|
@@ -14,6 +16,12 @@ import { registerPlanTools } from "./tools/plans.js";
|
|
|
14
16
|
import { registerCouponTools } from "./tools/coupons.js";
|
|
15
17
|
import { registerBroadcastTools } from "./tools/broadcasts.js";
|
|
16
18
|
import { registerDeepTool } from "./tools/deep.js";
|
|
19
|
+
import { registerAutomationTools } from "./tools/automations.js";
|
|
20
|
+
import { registerHelpCenterTools } from "./tools/help-center.js";
|
|
21
|
+
import { registerBlogTools } from "./tools/blog.js";
|
|
22
|
+
import { registerDesignTools } from "./tools/design.js";
|
|
23
|
+
import { registerAdminApiResources } from "./resources/admin-api.js";
|
|
24
|
+
import { registerDesignResources } from "./resources/design.js";
|
|
17
25
|
|
|
18
26
|
const SUTRA_API_TOKEN = process.env.SUTRA_API_TOKEN;
|
|
19
27
|
const SUTRA_BASE_URL = process.env.SUTRA_BASE_URL;
|
|
@@ -29,9 +37,9 @@ if (!SUTRA_API_TOKEN) {
|
|
|
29
37
|
|
|
30
38
|
const server = new McpServer({
|
|
31
39
|
name: "sutra",
|
|
32
|
-
version: "1.
|
|
40
|
+
version: "1.1.0",
|
|
33
41
|
description:
|
|
34
|
-
"Sutra Admin API — manage spaces, members, content, discussions, surveys, plans, broadcasts, and more.",
|
|
42
|
+
"Sutra Admin API — manage spaces, members, contacts, content, discussions, surveys, plans, broadcasts, and more.",
|
|
35
43
|
});
|
|
36
44
|
|
|
37
45
|
const client = new SutraAdminClient(SUTRA_API_TOKEN, SUTRA_BASE_URL);
|
|
@@ -39,6 +47,8 @@ const client = new SutraAdminClient(SUTRA_API_TOKEN, SUTRA_BASE_URL);
|
|
|
39
47
|
registerSpaceTools(server, client);
|
|
40
48
|
registerMemberTools(server, client);
|
|
41
49
|
registerContentTools(server, client);
|
|
50
|
+
registerDocumentTools(server, client);
|
|
51
|
+
registerMediaTools(server, client);
|
|
42
52
|
registerPropertyTools(server, client);
|
|
43
53
|
registerInvitationTools(server, client);
|
|
44
54
|
registerSurveyTools(server, client);
|
|
@@ -46,6 +56,12 @@ registerPlanTools(server, client);
|
|
|
46
56
|
registerCouponTools(server, client);
|
|
47
57
|
registerBroadcastTools(server, client);
|
|
48
58
|
registerDeepTool(server, client);
|
|
59
|
+
registerAutomationTools(server, client);
|
|
60
|
+
registerDesignTools(server, client);
|
|
61
|
+
registerHelpCenterTools(server);
|
|
62
|
+
registerBlogTools(server);
|
|
63
|
+
registerAdminApiResources(server);
|
|
64
|
+
registerDesignResources(server, client);
|
|
49
65
|
|
|
50
66
|
async function main() {
|
|
51
67
|
const transport = new StdioServerTransport();
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
const RESOURCES = [
|
|
2
|
+
{
|
|
3
|
+
name: "sutra-admin-api-overview",
|
|
4
|
+
title: "Sutra Admin API Overview",
|
|
5
|
+
uri: "sutra://admin-api/overview",
|
|
6
|
+
description: "Core Sutra Admin API concepts, IDs, scopes, pagination, and filtering.",
|
|
7
|
+
text: `# Sutra Admin API Overview
|
|
8
|
+
|
|
9
|
+
The Sutra Admin API lets authorized tokens manage spaces, members, contacts, content, invitations, properties, surveys, plans, coupons, and broadcasts.
|
|
10
|
+
|
|
11
|
+
Important IDs:
|
|
12
|
+
- Spaces use sp_ IDs.
|
|
13
|
+
- Members use mem_ IDs.
|
|
14
|
+
- Contacts use contact_ IDs.
|
|
15
|
+
- Users use usr_ IDs.
|
|
16
|
+
- Member property definitions use mprop_ IDs.
|
|
17
|
+
- Space property definitions use sprop_ IDs.
|
|
18
|
+
|
|
19
|
+
Pagination:
|
|
20
|
+
- List tools return data and pagination.
|
|
21
|
+
- Pass cursor to fetch the next page.
|
|
22
|
+
- Limit defaults to 25 unless the tool says otherwise.
|
|
23
|
+
|
|
24
|
+
Scopes:
|
|
25
|
+
- members:read reads members and contacts.
|
|
26
|
+
- members:write writes member/contact-related values where supported.
|
|
27
|
+
- spaces:read lists spaces where the token owner has admin access.
|
|
28
|
+
- membership_spaces:read lists basic read-only inventory for spaces where the token owner is a member.
|
|
29
|
+
- members.email:read is required to see or filter by email.
|
|
30
|
+
- member_properties:read is required to list definitions, read values, and filter people by properties.
|
|
31
|
+
- member_properties:write is required to create/update definitions or set values.
|
|
32
|
+
`,
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: "sutra-contacts-properties",
|
|
36
|
+
title: "Contacts and Member Properties",
|
|
37
|
+
uri: "sutra://admin-api/contacts-properties",
|
|
38
|
+
description: "How to list contacts and set custom property values for members or contacts.",
|
|
39
|
+
text: `# Contacts and Member Properties
|
|
40
|
+
|
|
41
|
+
Contacts are CRM people in a space who are not enrolled members. They appear in Sutra's Contacts/Interested layer and use the same member property definitions as members.
|
|
42
|
+
|
|
43
|
+
Useful tools:
|
|
44
|
+
- list_contacts: list contacts in a space.
|
|
45
|
+
- get_contact: get one contact by contact_ ID.
|
|
46
|
+
- get_contact_property_values: read values for one contact.
|
|
47
|
+
- update_contact_property_values: set or clear values for one contact.
|
|
48
|
+
- bulk_update_contact_property_values: set or clear values for up to 100 contacts in one idempotent request.
|
|
49
|
+
- bulk_update_member_property_values: set or clear values for up to 100 members in one idempotent request.
|
|
50
|
+
|
|
51
|
+
Property value payloads are objects keyed by property key or mprop_ ID. Use null to clear a value.
|
|
52
|
+
|
|
53
|
+
Example contact bulk update item:
|
|
54
|
+
{
|
|
55
|
+
"contact_id": "contact_abc123",
|
|
56
|
+
"properties": {
|
|
57
|
+
"mailing_list_opt_in": "Not yet subscribed",
|
|
58
|
+
"in_person_workshop": "Sweden"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
Use an idempotency_key for bulk updates so retries are safe.
|
|
63
|
+
`,
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: "sutra-people-filtering",
|
|
67
|
+
title: "Member and Contact Filtering",
|
|
68
|
+
uri: "sutra://admin-api/people-filtering",
|
|
69
|
+
description: "Filtering members and contacts by search, email, state, role, and custom property values.",
|
|
70
|
+
text: `# Member and Contact Filtering
|
|
71
|
+
|
|
72
|
+
list_members supports:
|
|
73
|
+
- email: exact email filter, requires members.email:read.
|
|
74
|
+
- q: case-insensitive name/email search.
|
|
75
|
+
- user_id: exact usr_ filter.
|
|
76
|
+
- role: member, editor, or moderator.
|
|
77
|
+
- state: active or pending.
|
|
78
|
+
- property_key and property_value: filter by one custom property.
|
|
79
|
+
- property_filters: filter by multiple custom properties.
|
|
80
|
+
|
|
81
|
+
list_contacts supports:
|
|
82
|
+
- email: exact email filter, requires members.email:read.
|
|
83
|
+
- q: case-insensitive name/email search.
|
|
84
|
+
- property_key and property_value: filter by one custom property.
|
|
85
|
+
- property_filters: filter by multiple custom properties.
|
|
86
|
+
|
|
87
|
+
Property filtering requires member_properties:read. property_key can be a property key or an mprop_ ID. Select and multi_select filters may use option names or stored option values.
|
|
88
|
+
|
|
89
|
+
Example multiple filters:
|
|
90
|
+
[
|
|
91
|
+
{ "property_key": "mailing_list_opt_in", "property_value": "Not yet subscribed" },
|
|
92
|
+
{ "property_key": "in_person_workshop", "property_value": "Sweden" }
|
|
93
|
+
]
|
|
94
|
+
`,
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
name: "sutra-membership-spaces",
|
|
98
|
+
title: "Membership Spaces",
|
|
99
|
+
uri: "sutra://admin-api/membership-spaces",
|
|
100
|
+
description: "Difference between admin-manageable spaces and limited membership-space inventory.",
|
|
101
|
+
text: `# Membership Spaces
|
|
102
|
+
|
|
103
|
+
Use list_spaces when you need spaces where the API token can perform admin operations.
|
|
104
|
+
|
|
105
|
+
Use list_membership_spaces when you only need a basic inventory of spaces where the token owner is a member. It returns IDs, names, type, state, privacy, timestamps, membership state, membership role, joined_at, and admin_access.
|
|
106
|
+
|
|
107
|
+
Membership-space results are read-only. A space with admin_access: false is included for inventory, but admin subresource tools such as list_members, list_content, list_child_spaces, or get_space can still return 404 or 403 for that space.
|
|
108
|
+
|
|
109
|
+
Required scope:
|
|
110
|
+
- membership_spaces:read
|
|
111
|
+
`,
|
|
112
|
+
},
|
|
113
|
+
];
|
|
114
|
+
|
|
115
|
+
export function registerAdminApiResources(server) {
|
|
116
|
+
for (const resource of RESOURCES) {
|
|
117
|
+
server.registerResource(
|
|
118
|
+
resource.name,
|
|
119
|
+
resource.uri,
|
|
120
|
+
{
|
|
121
|
+
title: resource.title,
|
|
122
|
+
description: resource.description,
|
|
123
|
+
mimeType: "text/markdown",
|
|
124
|
+
},
|
|
125
|
+
async () => ({
|
|
126
|
+
contents: [
|
|
127
|
+
{
|
|
128
|
+
uri: resource.uri,
|
|
129
|
+
mimeType: "text/markdown",
|
|
130
|
+
text: resource.text,
|
|
131
|
+
},
|
|
132
|
+
],
|
|
133
|
+
})
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
const BUNDLED_CAPABILITIES_MARKDOWN = `# Sutra Design Capabilities
|
|
2
|
+
|
|
3
|
+
Read this before designing Sutra pages.
|
|
4
|
+
|
|
5
|
+
- Use get_design_capabilities for the live manifest whenever possible.
|
|
6
|
+
- Read get_space_design before changing a page and keep its content_digest as base_digest.
|
|
7
|
+
- Validate nodes before creating or publishing a draft.
|
|
8
|
+
- Use create_or_update_design_draft and publish_design_draft instead of raw content writes.
|
|
9
|
+
- If publish returns 409 conflict, re-read the design and rebase instead of retrying blindly.
|
|
10
|
+
- Do not emit image_keywords; direct design writes require real image URLs.
|
|
11
|
+
- Use actionCallbackValue and actionCallbackTarget for action buttons.
|
|
12
|
+
- Use 12-unit grid dist arrays such as [6,6], [8,4], or [4,4,4].
|
|
13
|
+
- Use only font families returned by get_design_capabilities fonts.available; other fonts fall back at render time.
|
|
14
|
+
`;
|
|
15
|
+
|
|
16
|
+
export function registerDesignResources(server, client) {
|
|
17
|
+
server.registerResource(
|
|
18
|
+
"sutra-design-capabilities",
|
|
19
|
+
"sutra://design/capabilities",
|
|
20
|
+
{
|
|
21
|
+
title: "Sutra Design Capabilities",
|
|
22
|
+
description: "Renderer-aware Sutra design rules and supported fonts for page-generation tools.",
|
|
23
|
+
mimeType: "text/markdown",
|
|
24
|
+
},
|
|
25
|
+
async () => {
|
|
26
|
+
let text = BUNDLED_CAPABILITIES_MARKDOWN;
|
|
27
|
+
try {
|
|
28
|
+
const live = await client.get("/design/capabilities");
|
|
29
|
+
text = manifestToMarkdown(live.data || live);
|
|
30
|
+
} catch (_err) {
|
|
31
|
+
text += "\n\nLive capabilities could not be fetched; this bundled guidance may be stale.\n";
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
contents: [
|
|
36
|
+
{
|
|
37
|
+
uri: "sutra://design/capabilities",
|
|
38
|
+
mimeType: "text/markdown",
|
|
39
|
+
text,
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function manifestToMarkdown(manifest) {
|
|
48
|
+
const lines = [
|
|
49
|
+
"# Sutra Design Capabilities",
|
|
50
|
+
"",
|
|
51
|
+
`Version: ${manifest.version || "unknown"}`,
|
|
52
|
+
"",
|
|
53
|
+
"## Rules",
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
for (const rule of Object.values(manifest.rules || {})) lines.push(`- ${rule}`);
|
|
57
|
+
lines.push("", "## Node Types");
|
|
58
|
+
for (const [type, spec] of Object.entries(manifest.nodes || {})) {
|
|
59
|
+
lines.push(`- \`${type}\` attrs: ${(spec.attrs || []).map((attr) => `\`${attr}\``).join(", ")}`);
|
|
60
|
+
}
|
|
61
|
+
lines.push("", "## Marks");
|
|
62
|
+
for (const [type, spec] of Object.entries(manifest.marks || {})) {
|
|
63
|
+
lines.push(`- \`${type}\` attrs: ${(spec.attrs || []).map((attr) => `\`${attr}\``).join(", ")}`);
|
|
64
|
+
}
|
|
65
|
+
const fonts = manifest.fonts || {};
|
|
66
|
+
const availableFonts = fonts.available || [];
|
|
67
|
+
lines.push("", "## Fonts");
|
|
68
|
+
if (availableFonts.length > 0) {
|
|
69
|
+
for (const font of availableFonts) lines.push(`- ${font}`);
|
|
70
|
+
} else {
|
|
71
|
+
lines.push("- No font list returned by the live manifest.");
|
|
72
|
+
}
|
|
73
|
+
if (fonts.note) lines.push(`- ${fonts.note}`);
|
|
74
|
+
lines.push("", "## Anti-patterns");
|
|
75
|
+
for (const rule of manifest.anti_patterns || []) lines.push(`- ${rule}`);
|
|
76
|
+
return lines.join("\n");
|
|
77
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
export function registerAutomationTools(server, client) {
|
|
4
|
+
server.tool(
|
|
5
|
+
"list_automations",
|
|
6
|
+
"List automations configured on a space. Returns automation IDs (auto_...), trigger type, step summary, and member counts.",
|
|
7
|
+
{
|
|
8
|
+
space_id: z.string().describe("Space ID (sp_...)"),
|
|
9
|
+
limit: z.number().optional().describe("Max results (default 25)"),
|
|
10
|
+
cursor: z.string().optional().describe("Pagination cursor"),
|
|
11
|
+
},
|
|
12
|
+
async ({ space_id, limit, cursor }) => {
|
|
13
|
+
const data = await client.get(`/spaces/${space_id}/automations`, { limit, cursor });
|
|
14
|
+
return json(data);
|
|
15
|
+
}
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
server.tool(
|
|
19
|
+
"get_automation",
|
|
20
|
+
"Get details for a specific automation, including its steps.",
|
|
21
|
+
{
|
|
22
|
+
automation_id: z.string().describe("Automation ID (auto_...)"),
|
|
23
|
+
},
|
|
24
|
+
async ({ automation_id }) => {
|
|
25
|
+
const data = await client.get(`/automations/${automation_id}`);
|
|
26
|
+
return json(data);
|
|
27
|
+
}
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
server.tool(
|
|
31
|
+
"list_automation_members",
|
|
32
|
+
"List members enrolled in an automation. Filter by status (ongoing or completed).",
|
|
33
|
+
{
|
|
34
|
+
automation_id: z.string().describe("Automation ID (auto_...)"),
|
|
35
|
+
status: z.enum(["ongoing", "completed"]).optional().describe("Filter by enrollment status"),
|
|
36
|
+
limit: z.number().optional().describe("Max results (default 25)"),
|
|
37
|
+
cursor: z.string().optional().describe("Pagination cursor"),
|
|
38
|
+
},
|
|
39
|
+
async ({ automation_id, status, limit, cursor }) => {
|
|
40
|
+
const data = await client.get(`/automations/${automation_id}/members`, { status, limit, cursor });
|
|
41
|
+
return json(data);
|
|
42
|
+
}
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
server.tool(
|
|
46
|
+
"get_automation_member",
|
|
47
|
+
"Get a specific member's enrollment status in an automation.",
|
|
48
|
+
{
|
|
49
|
+
automation_id: z.string().describe("Automation ID (auto_...)"),
|
|
50
|
+
enrollment_id: z.string().describe("Automation enrollment ID (aenr_...)"),
|
|
51
|
+
},
|
|
52
|
+
async ({ automation_id, enrollment_id }) => {
|
|
53
|
+
const data = await client.get(`/automations/${automation_id}/members/${enrollment_id}`);
|
|
54
|
+
return json(data);
|
|
55
|
+
}
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
server.tool(
|
|
59
|
+
"add_automation_member",
|
|
60
|
+
"Enroll a member in an automation. This triggers the automation flow for that member. Identify the member by member_id, user_id, or email.",
|
|
61
|
+
{
|
|
62
|
+
automation_id: z.string().describe("Automation ID (auto_...)"),
|
|
63
|
+
member_id: z.string().optional().describe("Member ID (mem_...) — member must belong to the automation's space"),
|
|
64
|
+
user_id: z.string().optional().describe("User ID (usr_...) — alternative to member_id"),
|
|
65
|
+
email: z.string().optional().describe("User email — alternative to member_id or user_id"),
|
|
66
|
+
},
|
|
67
|
+
async ({ automation_id, ...body }) => {
|
|
68
|
+
const data = await client.post(`/automations/${automation_id}/members`, body);
|
|
69
|
+
return json(data);
|
|
70
|
+
}
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
server.tool(
|
|
74
|
+
"remove_automation_member",
|
|
75
|
+
"Remove a member from an automation, stopping the automation flow for them.",
|
|
76
|
+
{
|
|
77
|
+
automation_id: z.string().describe("Automation ID (auto_...)"),
|
|
78
|
+
enrollment_id: z.string().describe("Automation enrollment ID (aenr_...)"),
|
|
79
|
+
},
|
|
80
|
+
async ({ automation_id, enrollment_id }) => {
|
|
81
|
+
const data = await client.delete(`/automations/${automation_id}/members/${enrollment_id}`);
|
|
82
|
+
return json(data);
|
|
83
|
+
}
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function json(data) {
|
|
88
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
89
|
+
}
|