checkbox-mcp-server 2.0.0 → 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.
Files changed (2) hide show
  1. package/dist/index.js +103 -38
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -34,7 +34,11 @@ async function apiGet(path) {
34
34
  method: 'GET',
35
35
  headers: getHeaders(),
36
36
  });
37
- 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;
38
42
  }
39
43
  async function apiPost(path, body) {
40
44
  const res = await fetch(`${API_URL}${path}`, {
@@ -42,7 +46,28 @@ async function apiPost(path, body) {
42
46
  headers: getHeaders(),
43
47
  body: JSON.stringify(body),
44
48
  });
45
- 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);
46
71
  }
47
72
  function resultText(data) {
48
73
  return JSON.stringify(data, null, 2);
@@ -50,12 +75,17 @@ function resultText(data) {
50
75
  // ── MCP Server ──────────────────────────────────────────────────────
51
76
  const server = new McpServer({
52
77
  name: 'checkbox',
53
- version: '2.0.0',
78
+ version: '2.0.1',
54
79
  });
55
80
  // 1. List capabilities ───────────────────────────────────────────────
56
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 () => {
57
- const data = await apiGet('/api/v2/introspection/capabilities');
58
- 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
+ }
59
89
  });
60
90
  // 2. Execute command ─────────────────────────────────────────────────
61
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.', {
@@ -63,11 +93,16 @@ server.tool('execute_command', 'Execute a Checkbox capability command. Use list_
63
93
  organization_id: z.string().optional().describe('Organization ID. Required for org-scoped commands; omit for authenticated-scope commands like create_organization.'),
64
94
  payload: z.record(z.unknown()).describe('Command payload — structure depends on the command type. Use get_schema to discover the expected fields.'),
65
95
  }, async ({ type, organization_id, payload }) => {
66
- const body = { type, payload };
67
- if (organization_id)
68
- body.organization_id = organization_id;
69
- const data = await apiPost('/api/v2/commands', body);
70
- 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
+ }
71
106
  });
72
107
  // 3. Execute query ───────────────────────────────────────────────────
73
108
  server.tool('execute_query', 'Execute a Checkbox capability query. Use list_capabilities to see available query types.', {
@@ -75,19 +110,29 @@ server.tool('execute_query', 'Execute a Checkbox capability query. Use list_capa
75
110
  organization_id: z.string().describe('Organization ID'),
76
111
  payload: z.record(z.unknown()).describe('Query payload — structure depends on the query type. Use get_schema to discover the expected fields.'),
77
112
  }, async ({ type, organization_id, payload }) => {
78
- const data = await apiPost('/api/v2/query', {
79
- type,
80
- organization_id,
81
- payload: { ...payload, organization_id },
82
- });
83
- 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
+ }
84
124
  });
85
125
  // 4. Get workspace graph ─────────────────────────────────────────────
86
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.', {
87
127
  organization_id: z.string().describe('Organization ID'),
88
128
  }, async ({ organization_id }) => {
89
- const data = await apiPost('/api/v2/introspection/workspace', { organization_id });
90
- 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
+ }
91
136
  });
92
137
  // 5. Find entities ───────────────────────────────────────────────────
93
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.', {
@@ -96,13 +141,18 @@ server.tool('find_entities', 'Search for entities in the workspace by type and/o
96
141
  search: z.string().optional().describe('Text search across entity names'),
97
142
  limit: z.number().optional().describe('Max results to return (default varies by type)'),
98
143
  }, async ({ organization_id, entity_type, search, limit }) => {
99
- const data = await apiPost('/api/v2/introspection/entities', {
100
- organization_id,
101
- entity_type,
102
- search,
103
- limit,
104
- });
105
- 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
+ }
106
156
  });
107
157
  // 6. Get schema ──────────────────────────────────────────────────────
108
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.', {
@@ -110,27 +160,42 @@ server.tool('get_schema', 'Get the schema for entity types — describes fields,
110
160
  entity_type: z.string().optional().describe('Specific entity type to get schema for (e.g. "task", "plan", "table")'),
111
161
  entity_id: z.string().optional().describe('Specific entity ID to get schema for (useful for table records whose schema is defined per-table)'),
112
162
  }, async ({ organization_id, entity_type, entity_id }) => {
113
- const data = await apiPost('/api/v2/introspection/schema', {
114
- organization_id,
115
- entity_type,
116
- entity_id,
117
- });
118
- 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
+ }
119
174
  });
120
175
  // 7. Check permissions ───────────────────────────────────────────────
121
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.', {
122
177
  organization_id: z.string().describe('Organization ID'),
123
178
  }, async ({ organization_id }) => {
124
- const data = await apiPost('/api/v2/introspection/permissions', { organization_id });
125
- 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
+ }
126
186
  });
127
187
  // 8. List my organizations ──────────────────────────────────────────
128
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 () => {
129
- const data = await apiPost('/api/v2/query', {
130
- type: 'list_my_organizations',
131
- payload: {},
132
- });
133
- 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
+ }
134
199
  });
135
200
  // ── Start ───────────────────────────────────────────────────────────
136
201
  async function main() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "checkbox-mcp-server",
3
- "version": "2.0.0",
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",