@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # Sutra MCP Server
2
2
 
3
- An [MCP](https://modelcontextprotocol.io) server that connects AI agents to the [Sutra Admin API](https://sutra.co). Manage spaces, members, content, discussions, surveys, plans, broadcasts, and more — all through your AI tools.
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/user_id filtering
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
- - **list_content** / **get_content_block** / **create_content** / **update_content** / **delete_content** Manage content blocks
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 per member
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.0.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/joinsutra/mcp-server",
36
+ "homepage": "https://github.com/lorenzsell/sutra-mcp",
37
37
  "repository": {
38
38
  "type": "git",
39
- "url": "https://github.com/joinsutra/mcp-server.git"
39
+ "url": "git+https://github.com/lorenzsell/sutra-mcp.git"
40
40
  },
41
41
  "bugs": {
42
- "url": "https://github.com/joinsutra/mcp-server/issues"
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.0.0",
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
+ }