checkbox-mcp-server 1.1.1 → 2.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/dist/index.d.ts CHANGED
@@ -8,7 +8,8 @@
8
8
  *
9
9
  * Environment variables:
10
10
  * CHECKBOX_API_URL — Base URL of the Checkbox instance (e.g. https://checkbox.my)
11
- * CHECKBOX_API_TOKEN — API key (ck_live_...) or Supabase JWT
11
+ * CHECKBOX_API_KEY — API key (ck_live_...) preferred
12
+ * CHECKBOX_API_TOKEN — Alias for CHECKBOX_API_KEY (backward compat)
12
13
  *
13
14
  * Get your API key:
14
15
  * 1. Log in to checkbox.my
package/dist/index.js CHANGED
@@ -8,7 +8,8 @@
8
8
  *
9
9
  * Environment variables:
10
10
  * CHECKBOX_API_URL — Base URL of the Checkbox instance (e.g. https://checkbox.my)
11
- * CHECKBOX_API_TOKEN — API key (ck_live_...) or Supabase JWT
11
+ * CHECKBOX_API_KEY — API key (ck_live_...) preferred
12
+ * CHECKBOX_API_TOKEN — Alias for CHECKBOX_API_KEY (backward compat)
12
13
  *
13
14
  * Get your API key:
14
15
  * 1. Log in to checkbox.my
@@ -20,7 +21,7 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
20
21
  import { z } from 'zod';
21
22
  // ── Config ──────────────────────────────────────────────────────────
22
23
  const API_URL = process.env.CHECKBOX_API_URL ?? 'https://checkbox.my';
23
- const API_TOKEN = process.env.CHECKBOX_API_TOKEN ?? '';
24
+ const API_TOKEN = process.env.CHECKBOX_API_KEY || process.env.CHECKBOX_API_TOKEN || '';
24
25
  function getHeaders() {
25
26
  return {
26
27
  'Content-Type': 'application/json',
@@ -33,7 +34,11 @@ async function apiGet(path) {
33
34
  method: 'GET',
34
35
  headers: getHeaders(),
35
36
  });
36
- return res.json();
37
+ const json = await res.json().catch(() => null);
38
+ if (!res.ok) {
39
+ throw new Error(extractError(json) || `Request failed (${res.status})`);
40
+ }
41
+ return json;
37
42
  }
38
43
  async function apiPost(path, body) {
39
44
  const res = await fetch(`${API_URL}${path}`, {
@@ -41,7 +46,28 @@ async function apiPost(path, body) {
41
46
  headers: getHeaders(),
42
47
  body: JSON.stringify(body),
43
48
  });
44
- return res.json();
49
+ const json = await res.json().catch(() => null);
50
+ if (!res.ok) {
51
+ const msg = extractError(json) || `Request failed (${res.status})`;
52
+ throw new Error(msg);
53
+ }
54
+ if (json && json.success === false) {
55
+ throw new Error(extractError(json) || 'Request failed');
56
+ }
57
+ return json;
58
+ }
59
+ function extractError(obj) {
60
+ if (!obj)
61
+ return '';
62
+ if (typeof obj === 'string')
63
+ return obj;
64
+ if (typeof obj.error === 'string')
65
+ return obj.error;
66
+ if (obj.error?.message)
67
+ return obj.error.message;
68
+ if (obj.message)
69
+ return obj.message;
70
+ return JSON.stringify(obj);
45
71
  }
46
72
  function resultText(data) {
47
73
  return JSON.stringify(data, null, 2);
@@ -49,12 +75,17 @@ function resultText(data) {
49
75
  // ── MCP Server ──────────────────────────────────────────────────────
50
76
  const server = new McpServer({
51
77
  name: 'checkbox',
52
- version: '1.1.0',
78
+ version: '2.0.1',
53
79
  });
54
80
  // 1. List capabilities ───────────────────────────────────────────────
55
81
  server.tool('list_capabilities', 'List all available Checkbox commands and queries with their descriptions, required roles, and domains. Call this first to understand what actions are available.', {}, async () => {
56
- const data = await apiGet('/api/v2/introspection/capabilities');
57
- return { content: [{ type: 'text', text: resultText(data) }] };
82
+ try {
83
+ const data = await apiGet('/api/v2/introspection/capabilities');
84
+ return { content: [{ type: 'text', text: resultText(data) }] };
85
+ }
86
+ catch (err) {
87
+ return { content: [{ type: 'text', text: err.message }], isError: true };
88
+ }
58
89
  });
59
90
  // 2. Execute command ─────────────────────────────────────────────────
60
91
  server.tool('execute_command', 'Execute a Checkbox capability command. Use list_capabilities to see available command types and get_schema to understand the payload shape for each entity type.', {
@@ -62,11 +93,16 @@ server.tool('execute_command', 'Execute a Checkbox capability command. Use list_
62
93
  organization_id: z.string().optional().describe('Organization ID. Required for org-scoped commands; omit for authenticated-scope commands like create_organization.'),
63
94
  payload: z.record(z.unknown()).describe('Command payload — structure depends on the command type. Use get_schema to discover the expected fields.'),
64
95
  }, async ({ type, organization_id, payload }) => {
65
- const body = { type, payload };
66
- if (organization_id)
67
- body.organization_id = organization_id;
68
- const data = await apiPost('/api/v2/commands', body);
69
- return { content: [{ type: 'text', text: resultText(data) }] };
96
+ try {
97
+ const body = { type, payload };
98
+ if (organization_id)
99
+ body.organization_id = organization_id;
100
+ const data = await apiPost('/api/v2/commands', body);
101
+ return { content: [{ type: 'text', text: resultText(data) }] };
102
+ }
103
+ catch (err) {
104
+ return { content: [{ type: 'text', text: err.message }], isError: true };
105
+ }
70
106
  });
71
107
  // 3. Execute query ───────────────────────────────────────────────────
72
108
  server.tool('execute_query', 'Execute a Checkbox capability query. Use list_capabilities to see available query types.', {
@@ -74,19 +110,29 @@ server.tool('execute_query', 'Execute a Checkbox capability query. Use list_capa
74
110
  organization_id: z.string().describe('Organization ID'),
75
111
  payload: z.record(z.unknown()).describe('Query payload — structure depends on the query type. Use get_schema to discover the expected fields.'),
76
112
  }, async ({ type, organization_id, payload }) => {
77
- const data = await apiPost('/api/v2/query', {
78
- type,
79
- organization_id,
80
- payload: { ...payload, organization_id },
81
- });
82
- return { content: [{ type: 'text', text: resultText(data) }] };
113
+ try {
114
+ const data = await apiPost('/api/v2/query', {
115
+ type,
116
+ organization_id,
117
+ payload: { ...payload, organization_id },
118
+ });
119
+ return { content: [{ type: 'text', text: resultText(data) }] };
120
+ }
121
+ catch (err) {
122
+ return { content: [{ type: 'text', text: err.message }], isError: true };
123
+ }
83
124
  });
84
125
  // 4. Get workspace graph ─────────────────────────────────────────────
85
126
  server.tool('get_workspace', 'Get the full workspace graph — all entities (tasks, plans, goals, rules, requirements, tables, documents, etc.) and their relationships. Useful for understanding the structure of an organization before taking actions.', {
86
127
  organization_id: z.string().describe('Organization ID'),
87
128
  }, async ({ organization_id }) => {
88
- const data = await apiPost('/api/v2/introspection/workspace', { organization_id });
89
- return { content: [{ type: 'text', text: resultText(data) }] };
129
+ try {
130
+ const data = await apiPost('/api/v2/introspection/workspace', { organization_id });
131
+ return { content: [{ type: 'text', text: resultText(data) }] };
132
+ }
133
+ catch (err) {
134
+ return { content: [{ type: 'text', text: err.message }], isError: true };
135
+ }
90
136
  });
91
137
  // 5. Find entities ───────────────────────────────────────────────────
92
138
  server.tool('find_entities', 'Search for entities in the workspace by type and/or text search. Returns matching entities with their IDs, names, and metadata.', {
@@ -95,13 +141,18 @@ server.tool('find_entities', 'Search for entities in the workspace by type and/o
95
141
  search: z.string().optional().describe('Text search across entity names'),
96
142
  limit: z.number().optional().describe('Max results to return (default varies by type)'),
97
143
  }, async ({ organization_id, entity_type, search, limit }) => {
98
- const data = await apiPost('/api/v2/introspection/entities', {
99
- organization_id,
100
- entity_type,
101
- search,
102
- limit,
103
- });
104
- return { content: [{ type: 'text', text: resultText(data) }] };
144
+ try {
145
+ const data = await apiPost('/api/v2/introspection/entities', {
146
+ organization_id,
147
+ entity_type,
148
+ search,
149
+ limit,
150
+ });
151
+ return { content: [{ type: 'text', text: resultText(data) }] };
152
+ }
153
+ catch (err) {
154
+ return { content: [{ type: 'text', text: err.message }], isError: true };
155
+ }
105
156
  });
106
157
  // 6. Get schema ──────────────────────────────────────────────────────
107
158
  server.tool('get_schema', 'Get the schema for entity types — describes fields, writable fields, and filterable fields. Use this to understand what parameters to pass when creating or updating entities.', {
@@ -109,27 +160,42 @@ server.tool('get_schema', 'Get the schema for entity types — describes fields,
109
160
  entity_type: z.string().optional().describe('Specific entity type to get schema for (e.g. "task", "plan", "table")'),
110
161
  entity_id: z.string().optional().describe('Specific entity ID to get schema for (useful for table records whose schema is defined per-table)'),
111
162
  }, async ({ organization_id, entity_type, entity_id }) => {
112
- const data = await apiPost('/api/v2/introspection/schema', {
113
- organization_id,
114
- entity_type,
115
- entity_id,
116
- });
117
- return { content: [{ type: 'text', text: resultText(data) }] };
163
+ try {
164
+ const data = await apiPost('/api/v2/introspection/schema', {
165
+ organization_id,
166
+ entity_type,
167
+ entity_id,
168
+ });
169
+ return { content: [{ type: 'text', text: resultText(data) }] };
170
+ }
171
+ catch (err) {
172
+ return { content: [{ type: 'text', text: err.message }], isError: true };
173
+ }
118
174
  });
119
175
  // 7. Check permissions ───────────────────────────────────────────────
120
176
  server.tool('check_permissions', 'Check which capabilities the current user has in an organization. Returns the user role and a list of all commands/queries with allowed/denied status.', {
121
177
  organization_id: z.string().describe('Organization ID'),
122
178
  }, async ({ organization_id }) => {
123
- const data = await apiPost('/api/v2/introspection/permissions', { organization_id });
124
- return { content: [{ type: 'text', text: resultText(data) }] };
179
+ try {
180
+ const data = await apiPost('/api/v2/introspection/permissions', { organization_id });
181
+ return { content: [{ type: 'text', text: resultText(data) }] };
182
+ }
183
+ catch (err) {
184
+ return { content: [{ type: 'text', text: err.message }], isError: true };
185
+ }
125
186
  });
126
187
  // 8. List my organizations ──────────────────────────────────────────
127
188
  server.tool('list_my_organizations', 'List all organizations the authenticated user belongs to, with their role in each. Call this first to discover which organization_id to use for subsequent commands and queries.', {}, async () => {
128
- const data = await apiPost('/api/v2/query', {
129
- type: 'list_my_organizations',
130
- payload: {},
131
- });
132
- return { content: [{ type: 'text', text: resultText(data) }] };
189
+ try {
190
+ const data = await apiPost('/api/v2/query', {
191
+ type: 'list_my_organizations',
192
+ payload: {},
193
+ });
194
+ return { content: [{ type: 'text', text: resultText(data) }] };
195
+ }
196
+ catch (err) {
197
+ return { content: [{ type: 'text', text: err.message }], isError: true };
198
+ }
133
199
  });
134
200
  // ── Start ───────────────────────────────────────────────────────────
135
201
  async function main() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "checkbox-mcp-server",
3
- "version": "1.1.1",
3
+ "version": "2.0.1",
4
4
  "description": "MCP server for the Checkbox capability API — lets AI assistants (Claude, Cursor, etc.) manage compliance plans, tasks, documents, and more through the Model Context Protocol.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",