@sutraspaces/mcp-server 1.0.0 → 1.0.1
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 +15 -7
- package/package.json +1 -1
- package/src/tools/broadcasts.js +5 -3
- package/src/tools/content.js +4 -3
- package/src/tools/coupons.js +5 -3
- package/src/tools/invitations.js +6 -3
- package/src/tools/members.js +6 -3
- package/src/tools/plans.js +11 -6
- package/src/tools/spaces.js +7 -3
- package/src/tools/surveys.js +5 -3
package/README.md
CHANGED
|
@@ -5,13 +5,13 @@ An [MCP](https://modelcontextprotocol.io) server that connects AI agents to the
|
|
|
5
5
|
## Quick Start
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npm install
|
|
8
|
+
npm install -g @sutraspaces/mcp-server
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
Or run directly with npx:
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
|
-
|
|
14
|
+
SUTRA_API_TOKEN="sutra_live_sk_..." npx -y @sutraspaces/mcp-server
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
You can get an API token from your Sutra account settings or by contacting support@sutra.co.
|
|
@@ -26,8 +26,8 @@ Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
|
|
|
26
26
|
{
|
|
27
27
|
"mcpServers": {
|
|
28
28
|
"sutra": {
|
|
29
|
-
"command": "
|
|
30
|
-
"args": ["/
|
|
29
|
+
"command": "npx",
|
|
30
|
+
"args": ["-y", "@sutraspaces/mcp-server"],
|
|
31
31
|
"env": {
|
|
32
32
|
"SUTRA_API_TOKEN": "sutra_live_sk_..."
|
|
33
33
|
}
|
|
@@ -39,12 +39,12 @@ Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
|
|
|
39
39
|
### Claude Code
|
|
40
40
|
|
|
41
41
|
```bash
|
|
42
|
-
claude mcp add sutra -- env SUTRA_API_TOKEN=sutra_live_sk_...
|
|
42
|
+
claude mcp add sutra -- env SUTRA_API_TOKEN=sutra_live_sk_... npx -y @sutraspaces/mcp-server
|
|
43
43
|
```
|
|
44
44
|
|
|
45
45
|
### Cursor / Windsurf / Other MCP Clients
|
|
46
46
|
|
|
47
|
-
Point your MCP client at
|
|
47
|
+
Point your MCP client at `npx -y @sutraspaces/mcp-server` with the `SUTRA_API_TOKEN` environment variable set. The server communicates over stdio.
|
|
48
48
|
|
|
49
49
|
## Configuration
|
|
50
50
|
|
|
@@ -123,6 +123,14 @@ Point your MCP client at the server entry point with the `SUTRA_API_TOKEN` envir
|
|
|
123
123
|
|
|
124
124
|
**Pagination** is cursor-based. List endpoints return `{ data, pagination: { next_cursor, has_more } }`. Pass `cursor` to get the next page.
|
|
125
125
|
|
|
126
|
+
**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)
|
|
128
|
+
- `state` / `status` — exact-match enum filters (e.g. `state=active` for spaces, `status=sent` for broadcasts)
|
|
129
|
+
- `role` — filter members or invitations by role
|
|
130
|
+
- `type` / `privacy` / `frequency` — filter by type, privacy level, or billing frequency
|
|
131
|
+
- `user_id` — filter messages or payments by author/user
|
|
132
|
+
- `email` — filter invitations by email (requires `members.email:read` scope)
|
|
133
|
+
|
|
126
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.
|
|
127
135
|
|
|
128
136
|
## Architecture
|
package/package.json
CHANGED
package/src/tools/broadcasts.js
CHANGED
|
@@ -3,14 +3,16 @@ import { z } from "zod";
|
|
|
3
3
|
export function registerBroadcastTools(server, client) {
|
|
4
4
|
server.tool(
|
|
5
5
|
"list_broadcasts",
|
|
6
|
-
"List broadcasts for a space. Broadcasts are mass communications sent to space members.",
|
|
6
|
+
"List broadcasts for a space. Broadcasts are mass communications sent to space members. Use `q` to search by title.",
|
|
7
7
|
{
|
|
8
8
|
space_id: z.string().describe("Space ID (sp_...)"),
|
|
9
9
|
limit: z.number().optional().describe("Max results (default 25)"),
|
|
10
10
|
cursor: z.string().optional().describe("Pagination cursor"),
|
|
11
|
+
q: z.string().optional().describe("Search broadcasts by title (case-insensitive)"),
|
|
12
|
+
status: z.enum(["draft", "scheduled", "sent"]).optional().describe("Filter by status"),
|
|
11
13
|
},
|
|
12
|
-
async ({ space_id, limit, cursor }) => {
|
|
13
|
-
const data = await client.get(`/spaces/${space_id}/broadcasts`, { limit, cursor });
|
|
14
|
+
async ({ space_id, limit, cursor, q, status }) => {
|
|
15
|
+
const data = await client.get(`/spaces/${space_id}/broadcasts`, { limit, cursor, q, status });
|
|
14
16
|
return json(data);
|
|
15
17
|
}
|
|
16
18
|
);
|
package/src/tools/content.js
CHANGED
|
@@ -89,14 +89,15 @@ export function registerContentTools(server, client) {
|
|
|
89
89
|
|
|
90
90
|
server.tool(
|
|
91
91
|
"list_messages",
|
|
92
|
-
"List discussion messages in a space. Messages are top-level discussion posts that members create.",
|
|
92
|
+
"List discussion messages in a space. Messages are top-level discussion posts that members create. Filter by author with user_id.",
|
|
93
93
|
{
|
|
94
94
|
space_id: z.string().describe("Space ID (sp_...)"),
|
|
95
95
|
limit: z.number().optional().describe("Max results (default 25)"),
|
|
96
96
|
cursor: z.string().optional().describe("Pagination cursor"),
|
|
97
|
+
user_id: z.string().optional().describe("Filter by author user ID (usr_...)"),
|
|
97
98
|
},
|
|
98
|
-
async ({ space_id, limit, cursor }) => {
|
|
99
|
-
const data = await client.get(`/spaces/${space_id}/messages`, { limit, cursor });
|
|
99
|
+
async ({ space_id, limit, cursor, user_id }) => {
|
|
100
|
+
const data = await client.get(`/spaces/${space_id}/messages`, { limit, cursor, user_id });
|
|
100
101
|
return json(data);
|
|
101
102
|
}
|
|
102
103
|
);
|
package/src/tools/coupons.js
CHANGED
|
@@ -3,14 +3,16 @@ import { z } from "zod";
|
|
|
3
3
|
export function registerCouponTools(server, client) {
|
|
4
4
|
server.tool(
|
|
5
5
|
"list_coupons",
|
|
6
|
-
"List coupons for a space.",
|
|
6
|
+
"List coupons for a space. Use `q` to search by coupon code.",
|
|
7
7
|
{
|
|
8
8
|
space_id: z.string().describe("Space ID (sp_...)"),
|
|
9
9
|
limit: z.number().optional().describe("Max results (default 25)"),
|
|
10
10
|
cursor: z.string().optional().describe("Pagination cursor"),
|
|
11
|
+
q: z.string().optional().describe("Search coupons by code (case-insensitive)"),
|
|
12
|
+
status: z.string().optional().describe("Filter by coupon status"),
|
|
11
13
|
},
|
|
12
|
-
async ({ space_id, limit, cursor }) => {
|
|
13
|
-
const data = await client.get(`/spaces/${space_id}/coupons`, { limit, cursor });
|
|
14
|
+
async ({ space_id, limit, cursor, q, status }) => {
|
|
15
|
+
const data = await client.get(`/spaces/${space_id}/coupons`, { limit, cursor, q, status });
|
|
14
16
|
return json(data);
|
|
15
17
|
}
|
|
16
18
|
);
|
package/src/tools/invitations.js
CHANGED
|
@@ -3,14 +3,17 @@ import { z } from "zod";
|
|
|
3
3
|
export function registerInvitationTools(server, client) {
|
|
4
4
|
server.tool(
|
|
5
5
|
"list_invitations",
|
|
6
|
-
"List invitations for a space.",
|
|
6
|
+
"List invitations for a space. Filter by email, state, or role.",
|
|
7
7
|
{
|
|
8
8
|
space_id: z.string().describe("Space ID (sp_...)"),
|
|
9
9
|
limit: z.number().optional().describe("Max results (default 25)"),
|
|
10
10
|
cursor: z.string().optional().describe("Pagination cursor"),
|
|
11
|
+
email: z.string().optional().describe("Search by email (case-insensitive, requires members.email:read scope)"),
|
|
12
|
+
state: z.enum(["pending", "accepted", "declined"]).optional().describe("Filter by invitation state"),
|
|
13
|
+
role: z.enum(["member", "editor", "moderator"]).optional().describe("Filter by role"),
|
|
11
14
|
},
|
|
12
|
-
async ({ space_id, limit, cursor }) => {
|
|
13
|
-
const data = await client.get(`/spaces/${space_id}/invitations`, { limit, cursor });
|
|
15
|
+
async ({ space_id, limit, cursor, email, state, role }) => {
|
|
16
|
+
const data = await client.get(`/spaces/${space_id}/invitations`, { limit, cursor, email, state, role });
|
|
14
17
|
return json(data);
|
|
15
18
|
}
|
|
16
19
|
);
|
package/src/tools/members.js
CHANGED
|
@@ -3,16 +3,19 @@ import { z } from "zod";
|
|
|
3
3
|
export function registerMemberTools(server, client) {
|
|
4
4
|
server.tool(
|
|
5
5
|
"list_members",
|
|
6
|
-
"List members of a space. Returns member IDs (mem_...), user info, roles, and states.",
|
|
6
|
+
"List members of a space. Returns member IDs (mem_...), user info, roles, and states. Use `q` to search by name or email.",
|
|
7
7
|
{
|
|
8
8
|
space_id: z.string().describe("Space ID (sp_...)"),
|
|
9
9
|
limit: z.number().optional().describe("Max results (default 25)"),
|
|
10
10
|
cursor: z.string().optional().describe("Pagination cursor"),
|
|
11
11
|
user_id: z.string().optional().describe("Filter by user ID (usr_...)"),
|
|
12
12
|
email: z.string().optional().describe("Filter by exact email (requires members.email:read scope)"),
|
|
13
|
+
q: z.string().optional().describe("Search by member name or email (case-insensitive)"),
|
|
14
|
+
role: z.enum(["member", "editor", "moderator"]).optional().describe("Filter by role"),
|
|
15
|
+
state: z.enum(["active", "pending"]).optional().describe("Filter by state"),
|
|
13
16
|
},
|
|
14
|
-
async ({ space_id, limit, cursor, user_id, email }) => {
|
|
15
|
-
const data = await client.get(`/spaces/${space_id}/members`, { limit, cursor, user_id, email });
|
|
17
|
+
async ({ space_id, limit, cursor, user_id, email, q, role, state }) => {
|
|
18
|
+
const data = await client.get(`/spaces/${space_id}/members`, { limit, cursor, user_id, email, q, role, state });
|
|
16
19
|
return json(data);
|
|
17
20
|
}
|
|
18
21
|
);
|
package/src/tools/plans.js
CHANGED
|
@@ -5,14 +5,17 @@ export function registerPlanTools(server, client) {
|
|
|
5
5
|
|
|
6
6
|
server.tool(
|
|
7
7
|
"list_plans",
|
|
8
|
-
"List plans (offerings) for a space. Plans describe what can be purchased or enrolled in.",
|
|
8
|
+
"List plans (offerings) for a space. Plans describe what can be purchased or enrolled in. Use `q` to search by name.",
|
|
9
9
|
{
|
|
10
10
|
space_id: z.string().describe("Space ID (sp_...)"),
|
|
11
11
|
limit: z.number().optional().describe("Max results (default 25)"),
|
|
12
12
|
cursor: z.string().optional().describe("Pagination cursor"),
|
|
13
|
+
q: z.string().optional().describe("Search plans by name (case-insensitive)"),
|
|
14
|
+
status: z.string().optional().describe("Filter by status"),
|
|
15
|
+
frequency: z.string().optional().describe("Filter by billing frequency"),
|
|
13
16
|
},
|
|
14
|
-
async ({ space_id, limit, cursor }) => {
|
|
15
|
-
const data = await client.get(`/spaces/${space_id}/plans`, { limit, cursor });
|
|
17
|
+
async ({ space_id, limit, cursor, q, status, frequency }) => {
|
|
18
|
+
const data = await client.get(`/spaces/${space_id}/plans`, { limit, cursor, q, status, frequency });
|
|
16
19
|
return json(data);
|
|
17
20
|
}
|
|
18
21
|
);
|
|
@@ -101,14 +104,16 @@ export function registerPlanTools(server, client) {
|
|
|
101
104
|
|
|
102
105
|
server.tool(
|
|
103
106
|
"list_payments",
|
|
104
|
-
"List payments for a space.",
|
|
107
|
+
"List payments for a space. Filter by user or status.",
|
|
105
108
|
{
|
|
106
109
|
space_id: z.string().describe("Space ID (sp_...)"),
|
|
107
110
|
limit: z.number().optional().describe("Max results (default 25)"),
|
|
108
111
|
cursor: z.string().optional().describe("Pagination cursor"),
|
|
112
|
+
user_id: z.string().optional().describe("Filter by user ID (usr_...)"),
|
|
113
|
+
status: z.string().optional().describe("Filter by payment status"),
|
|
109
114
|
},
|
|
110
|
-
async ({ space_id, limit, cursor }) => {
|
|
111
|
-
const data = await client.get(`/spaces/${space_id}/payments`, { limit, cursor });
|
|
115
|
+
async ({ space_id, limit, cursor, user_id, status }) => {
|
|
116
|
+
const data = await client.get(`/spaces/${space_id}/payments`, { limit, cursor, user_id, status });
|
|
112
117
|
return json(data);
|
|
113
118
|
}
|
|
114
119
|
);
|
package/src/tools/spaces.js
CHANGED
|
@@ -3,13 +3,17 @@ import { z } from "zod";
|
|
|
3
3
|
export function registerSpaceTools(server, client) {
|
|
4
4
|
server.tool(
|
|
5
5
|
"list_spaces",
|
|
6
|
-
"List all spaces the API token can access. Returns space IDs (sp_...), names, types, and states.
|
|
6
|
+
"List all spaces the API token can access. Returns space IDs (sp_...), names, types, and states. Use `q` to search by name.",
|
|
7
7
|
{
|
|
8
8
|
limit: z.number().optional().describe("Max results per page (default 25, max 100)"),
|
|
9
9
|
cursor: z.string().optional().describe("Pagination cursor from a previous response"),
|
|
10
|
+
q: z.string().optional().describe("Search spaces by name (case-insensitive)"),
|
|
11
|
+
state: z.enum(["active", "archived"]).optional().describe("Filter by state"),
|
|
12
|
+
type: z.string().optional().describe("Filter by type (e.g. content, forum)"),
|
|
13
|
+
privacy: z.enum(["open", "closed", "secret"]).optional().describe("Filter by privacy level"),
|
|
10
14
|
},
|
|
11
|
-
async ({ limit, cursor }) => {
|
|
12
|
-
const data = await client.get("/spaces", { limit, cursor });
|
|
15
|
+
async ({ limit, cursor, q, state, type, privacy }) => {
|
|
16
|
+
const data = await client.get("/spaces", { limit, cursor, q, state, type, privacy });
|
|
13
17
|
return json(data);
|
|
14
18
|
}
|
|
15
19
|
);
|
package/src/tools/surveys.js
CHANGED
|
@@ -3,14 +3,16 @@ import { z } from "zod";
|
|
|
3
3
|
export function registerSurveyTools(server, client) {
|
|
4
4
|
server.tool(
|
|
5
5
|
"list_surveys",
|
|
6
|
-
"List surveys in a space.",
|
|
6
|
+
"List surveys in a space. Use `q` to search by title.",
|
|
7
7
|
{
|
|
8
8
|
space_id: z.string().describe("Space ID (sp_...)"),
|
|
9
9
|
limit: z.number().optional().describe("Max results (default 25)"),
|
|
10
10
|
cursor: z.string().optional().describe("Pagination cursor"),
|
|
11
|
+
q: z.string().optional().describe("Search surveys by title (case-insensitive)"),
|
|
12
|
+
type: z.string().optional().describe("Filter by survey type"),
|
|
11
13
|
},
|
|
12
|
-
async ({ space_id, limit, cursor }) => {
|
|
13
|
-
const data = await client.get(`/spaces/${space_id}/surveys`, { limit, cursor });
|
|
14
|
+
async ({ space_id, limit, cursor, q, type }) => {
|
|
15
|
+
const data = await client.get(`/spaces/${space_id}/surveys`, { limit, cursor, q, type });
|
|
14
16
|
return json(data);
|
|
15
17
|
}
|
|
16
18
|
);
|