@commandable/mcp-core 0.2.0

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 (157) hide show
  1. package/LICENSE +10 -0
  2. package/README.md +8 -0
  3. package/dist/config/configApply.d.ts +16 -0
  4. package/dist/config/configApply.d.ts.map +1 -0
  5. package/dist/config/configApply.js +77 -0
  6. package/dist/config/configApply.js.map +1 -0
  7. package/dist/config/configLoader.d.ts +9 -0
  8. package/dist/config/configLoader.d.ts.map +1 -0
  9. package/dist/config/configLoader.js +75 -0
  10. package/dist/config/configLoader.js.map +1 -0
  11. package/dist/config/configSchema.d.ts +45 -0
  12. package/dist/config/configSchema.d.ts.map +1 -0
  13. package/dist/config/configSchema.js +23 -0
  14. package/dist/config/configSchema.js.map +1 -0
  15. package/dist/crypto/encryption.d.ts +3 -0
  16. package/dist/crypto/encryption.d.ts.map +1 -0
  17. package/dist/crypto/encryption.js +29 -0
  18. package/dist/crypto/encryption.js.map +1 -0
  19. package/dist/db/client.d.ts +24 -0
  20. package/dist/db/client.d.ts.map +1 -0
  21. package/dist/db/client.js +50 -0
  22. package/dist/db/client.js.map +1 -0
  23. package/dist/db/credentialStore.d.ts +15 -0
  24. package/dist/db/credentialStore.d.ts.map +1 -0
  25. package/dist/db/credentialStore.js +56 -0
  26. package/dist/db/credentialStore.js.map +1 -0
  27. package/dist/db/integrationStore.d.ts +14 -0
  28. package/dist/db/integrationStore.d.ts.map +1 -0
  29. package/dist/db/integrationStore.js +128 -0
  30. package/dist/db/integrationStore.js.map +1 -0
  31. package/dist/db/integrationTypeConfigStore.d.ts +7 -0
  32. package/dist/db/integrationTypeConfigStore.d.ts.map +1 -0
  33. package/dist/db/integrationTypeConfigStore.js +101 -0
  34. package/dist/db/integrationTypeConfigStore.js.map +1 -0
  35. package/dist/db/migrate.d.ts +3 -0
  36. package/dist/db/migrate.d.ts.map +1 -0
  37. package/dist/db/migrate.js +11 -0
  38. package/dist/db/migrate.js.map +1 -0
  39. package/dist/db/migrations/pg/0000_initial.sql +74 -0
  40. package/dist/db/migrations/pg/meta/_journal.json +13 -0
  41. package/dist/db/migrations/sqlite/0000_initial.sql +74 -0
  42. package/dist/db/migrations/sqlite/meta/_journal.json +13 -0
  43. package/dist/db/schema.d.ts +1863 -0
  44. package/dist/db/schema.d.ts.map +1 -0
  45. package/dist/db/schema.js +133 -0
  46. package/dist/db/schema.js.map +1 -0
  47. package/dist/db/toolDefinitionStore.d.ts +9 -0
  48. package/dist/db/toolDefinitionStore.d.ts.map +1 -0
  49. package/dist/db/toolDefinitionStore.js +117 -0
  50. package/dist/db/toolDefinitionStore.js.map +1 -0
  51. package/dist/errors/httpError.d.ts +6 -0
  52. package/dist/errors/httpError.d.ts.map +1 -0
  53. package/dist/errors/httpError.js +11 -0
  54. package/dist/errors/httpError.js.map +1 -0
  55. package/dist/index.d.ts +34 -0
  56. package/dist/index.d.ts.map +1 -0
  57. package/dist/index.js +34 -0
  58. package/dist/index.js.map +1 -0
  59. package/dist/integrations/actionsFactory.d.ts +16 -0
  60. package/dist/integrations/actionsFactory.d.ts.map +1 -0
  61. package/dist/integrations/actionsFactory.js +98 -0
  62. package/dist/integrations/actionsFactory.js.map +1 -0
  63. package/dist/integrations/catalog.d.ts +8 -0
  64. package/dist/integrations/catalog.d.ts.map +1 -0
  65. package/dist/integrations/catalog.js +45 -0
  66. package/dist/integrations/catalog.js.map +1 -0
  67. package/dist/integrations/customToolFactory.d.ts +13 -0
  68. package/dist/integrations/customToolFactory.d.ts.map +1 -0
  69. package/dist/integrations/customToolFactory.js +31 -0
  70. package/dist/integrations/customToolFactory.js.map +1 -0
  71. package/dist/integrations/dataLoader.d.ts +3 -0
  72. package/dist/integrations/dataLoader.d.ts.map +1 -0
  73. package/dist/integrations/dataLoader.js +2 -0
  74. package/dist/integrations/dataLoader.js.map +1 -0
  75. package/dist/integrations/fileIntegrationTypeConfigStore.d.ts +7 -0
  76. package/dist/integrations/fileIntegrationTypeConfigStore.d.ts.map +1 -0
  77. package/dist/integrations/fileIntegrationTypeConfigStore.js +34 -0
  78. package/dist/integrations/fileIntegrationTypeConfigStore.js.map +1 -0
  79. package/dist/integrations/getIntegration.d.ts +14 -0
  80. package/dist/integrations/getIntegration.d.ts.map +1 -0
  81. package/dist/integrations/getIntegration.js +30 -0
  82. package/dist/integrations/getIntegration.js.map +1 -0
  83. package/dist/integrations/googleServiceAccount.d.ts +6 -0
  84. package/dist/integrations/googleServiceAccount.d.ts.map +1 -0
  85. package/dist/integrations/googleServiceAccount.js +54 -0
  86. package/dist/integrations/googleServiceAccount.js.map +1 -0
  87. package/dist/integrations/health.d.ts +20 -0
  88. package/dist/integrations/health.d.ts.map +1 -0
  89. package/dist/integrations/health.js +43 -0
  90. package/dist/integrations/health.js.map +1 -0
  91. package/dist/integrations/integrationTypeConfigLookup.d.ts +12 -0
  92. package/dist/integrations/integrationTypeConfigLookup.d.ts.map +1 -0
  93. package/dist/integrations/integrationTypeConfigLookup.js +11 -0
  94. package/dist/integrations/integrationTypeConfigLookup.js.map +1 -0
  95. package/dist/integrations/providerRegistry.d.ts +2 -0
  96. package/dist/integrations/providerRegistry.d.ts.map +1 -0
  97. package/dist/integrations/providerRegistry.js +72 -0
  98. package/dist/integrations/providerRegistry.js.map +1 -0
  99. package/dist/integrations/proxy.d.ts +19 -0
  100. package/dist/integrations/proxy.d.ts.map +1 -0
  101. package/dist/integrations/proxy.js +377 -0
  102. package/dist/integrations/proxy.js.map +1 -0
  103. package/dist/integrations/sandbox.d.ts +8 -0
  104. package/dist/integrations/sandbox.d.ts.map +1 -0
  105. package/dist/integrations/sandbox.js +221 -0
  106. package/dist/integrations/sandbox.js.map +1 -0
  107. package/dist/integrations/sandboxUtils.d.ts +15 -0
  108. package/dist/integrations/sandboxUtils.d.ts.map +1 -0
  109. package/dist/integrations/sandboxUtils.js +489 -0
  110. package/dist/integrations/sandboxUtils.js.map +1 -0
  111. package/dist/integrations/tools.d.ts +3 -0
  112. package/dist/integrations/tools.d.ts.map +1 -0
  113. package/dist/integrations/tools.js +70 -0
  114. package/dist/integrations/tools.js.map +1 -0
  115. package/dist/mcp/abilityCatalog.d.ts +51 -0
  116. package/dist/mcp/abilityCatalog.d.ts.map +1 -0
  117. package/dist/mcp/abilityCatalog.js +300 -0
  118. package/dist/mcp/abilityCatalog.js.map +1 -0
  119. package/dist/mcp/auth.d.ts +18 -0
  120. package/dist/mcp/auth.d.ts.map +1 -0
  121. package/dist/mcp/auth.js +45 -0
  122. package/dist/mcp/auth.js.map +1 -0
  123. package/dist/mcp/builder_guide.md +441 -0
  124. package/dist/mcp/commandable_readme.md +29 -0
  125. package/dist/mcp/handlers.d.ts +21 -0
  126. package/dist/mcp/handlers.d.ts.map +1 -0
  127. package/dist/mcp/handlers.js +86 -0
  128. package/dist/mcp/handlers.js.map +1 -0
  129. package/dist/mcp/metaTools.d.ts +79 -0
  130. package/dist/mcp/metaTools.d.ts.map +1 -0
  131. package/dist/mcp/metaTools.js +901 -0
  132. package/dist/mcp/metaTools.js.map +1 -0
  133. package/dist/mcp/server.d.ts +25 -0
  134. package/dist/mcp/server.d.ts.map +1 -0
  135. package/dist/mcp/server.js +14 -0
  136. package/dist/mcp/server.js.map +1 -0
  137. package/dist/mcp/sessionState.d.ts +19 -0
  138. package/dist/mcp/sessionState.d.ts.map +1 -0
  139. package/dist/mcp/sessionState.js +79 -0
  140. package/dist/mcp/sessionState.js.map +1 -0
  141. package/dist/mcp/toolAdapter.d.ts +34 -0
  142. package/dist/mcp/toolAdapter.d.ts.map +1 -0
  143. package/dist/mcp/toolAdapter.js +24 -0
  144. package/dist/mcp/toolAdapter.js.map +1 -0
  145. package/dist/runtime/credentialManager.d.ts +19 -0
  146. package/dist/runtime/credentialManager.d.ts.map +1 -0
  147. package/dist/runtime/credentialManager.js +82 -0
  148. package/dist/runtime/credentialManager.js.map +1 -0
  149. package/dist/runtime/stdioSession.d.ts +8 -0
  150. package/dist/runtime/stdioSession.d.ts.map +1 -0
  151. package/dist/runtime/stdioSession.js +79 -0
  152. package/dist/runtime/stdioSession.js.map +1 -0
  153. package/dist/types.d.ts +92 -0
  154. package/dist/types.d.ts.map +1 -0
  155. package/dist/types.js +2 -0
  156. package/dist/types.js.map +1 -0
  157. package/package.json +64 -0
@@ -0,0 +1,441 @@
1
+ # Commandable Builder — vibe-coding new tools
2
+
3
+ ## Overview
4
+
5
+ You now have the tools to create **new custom tools** against any connected integration.
6
+
7
+ Each custom tool you create is:
8
+
9
+ - persisted to the Commandable database
10
+ - registered into the current session immediately (you can call it right away)
11
+ - available in `commandable_search_tools` for future sessions
12
+
13
+ Your job is to write four things for each tool:
14
+
15
+ - a **name** — the stable snake_case identifier (e.g. `list_sprint_tickets`)
16
+ - a **description** — one sentence, what it does and when to use it
17
+ - an **input schema** — a JSON Schema object that defines the arguments the caller passes
18
+ - **handler code** — raw JavaScript that calls the integration and returns data
19
+
20
+ When you're done with a tool, call `commandable_upsert_custom_tool`. It persists (or updates) the tool and triggers a live `tools/list_changed` notification — the tool is callable immediately.
21
+
22
+ ## Creating brand new integrations (full vibe-coded integrations)
23
+
24
+ If the user needs an API that isn't connected yet, you can create a brand new integration type from scratch using:
25
+
26
+ - `commandable_upsert_custom_integration`
27
+
28
+ This creates:
29
+
30
+ - a new **integration type slug** (auto-generated like `stripe-a3f2`)
31
+ - a new **integration instance** (with an `integration_id`)
32
+ - a **credential form schema** (so the user can enter credentials out-of-band)
33
+ - an **auth injection rule** (so Commandable injects credentials into API calls)
34
+
35
+ After creation, the user must open the returned `credential_url` and enter credentials. Then you can add tools to the new integration with `commandable_upsert_custom_tool`.
36
+
37
+ ### `commandable_upsert_custom_integration` inputs
38
+
39
+ - **`label`**: display name, e.g. `"Stripe"`
40
+ - **`base_url`**: API base URL, e.g. `"https://api.stripe.com/v1"`
41
+ - **`auth_type`**: `"custom"` or `"basic"`
42
+ - **`credential_fields`**: fields the user will enter (name + label, optional description)
43
+ - **`credential_injection`** (required for `auth_type: "custom"`): headers/query templates using `{{fieldName}}`
44
+ - **`basic_username_field` / `basic_password_field`** (required for `auth_type: "basic"`): which credential fields map to username/password
45
+ - **`connection_hint`**: markdown shown in the credential form. **Always include this.** Write it as a numbered list of every step the user must follow to obtain the credentials — starting from opening the right website, navigating to the correct settings page, creating or copying the token/key/secret, and pasting it into the field. Leave nothing implicit. See the Stripe example below.
46
+
47
+ #### Auth types
48
+
49
+ | `auth_type` | When to use | How it works |
50
+ |---|---|---|
51
+ | `custom` | Bearer tokens, API keys, custom headers/query params | You provide template strings like `Authorization: Bearer {{apiKey}}` and Commandable injects them |
52
+ | `basic` | APIs that use HTTP Basic Auth | Commandable base64 encodes `username:password` from the mapped credential fields and injects `Authorization: Basic <token>` |
53
+
54
+ #### Template expressions in `credential_injection`
55
+
56
+ Inside `custom` injection templates you can use more than just plain `{{fieldName}}` references. The following expressions are supported:
57
+
58
+ | Expression | What it produces |
59
+ |---|---|
60
+ | `{{fieldName}}` | The raw value of that credential field |
61
+ | `{{base64(expr)}}` | Base64-encodes the result of `expr`. `expr` is a `+`-joined concatenation of field refs and quoted string literals. |
62
+
63
+ **Example — Atlassian API token (email + token combined into Basic auth):**
64
+
65
+ ```json
66
+ "credential_injection": {
67
+ "headers": {
68
+ "Authorization": "Basic {{base64(email + \":\" + apiToken)}}",
69
+ "Accept": "application/json"
70
+ }
71
+ }
72
+ ```
73
+
74
+ Use this pattern whenever an API takes a `username:password` style Basic auth but exposes them as two separate credential fields. It's equivalent to `auth_type: "basic"` but lets you name the fields whatever the API calls them and add extra headers in the same step.
75
+
76
+ ## The mental model
77
+
78
+ Think of each tool as a tiny, focused action. One API call, one clear purpose.
79
+
80
+ The best tools are:
81
+
82
+ - **small** — one endpoint, one responsibility
83
+ - **reliable** — GET tools that return IDs + names so write tools always have correct input
84
+ - **easy to call** — input schema matches how a human would naturally describe what they want
85
+ - **honest** — `console.log` lines tell the user what's happening in plain English
86
+
87
+ The typical pattern is: build a `list_*` or `get_*` read tool first to discover IDs and field structure, then build the write tool that uses those IDs. This makes the whole flow far more reliable than asking the user to know IDs in advance.
88
+
89
+ ## What is available in handler code
90
+
91
+ Handler code runs in a **sandboxed Node.js VM**. There is no internet access, no file system, no native packages.
92
+
93
+ ### Available globals
94
+
95
+ | Global | What it is |
96
+ |---|---|
97
+ | `integration` | The connected integration client for this tool (see below) |
98
+ | `console.log(...)` | User-facing log messages (friendly, not technical) |
99
+ | `URL` | For safe URL construction |
100
+ | `URLSearchParams` | For building query strings |
101
+ | `atob` / `btoa` | Base64 encode/decode |
102
+ | `encodeURIComponent` / `decodeURIComponent` | URL encoding helpers |
103
+
104
+ ### Blocked (not available)
105
+
106
+ `fetch`, `axios`, `process`, `require`, `Buffer`, `global`, `globalThis`, `setTimeout`, `setInterval`, `eval`, `Function`
107
+
108
+ If you need to call an API, use `integration.*`. There is no other way.
109
+
110
+ ### The `integration` object
111
+
112
+ `integration` is the pre-authenticated client for this tool's integration instance. Commandable injects credentials automatically — the handler never touches secrets.
113
+
114
+ ```js
115
+ integration.fetch(path, init?) // → Response (standard Fetch Response)
116
+ integration.get(path, init?) // GET shorthand
117
+ integration.post(path, body, init?) // POST shorthand
118
+ integration.put(path, body, init?) // PUT shorthand
119
+ integration.patch(path, body, init?) // PATCH shorthand
120
+ integration.delete(path, init?) // DELETE shorthand
121
+ ```
122
+
123
+ All paths are **relative to the integration's base URL**. So if the base is `https://api.github.com`, write `/repos/owner/repo/issues` — not the full URL.
124
+
125
+ `integration.fetch` and all verb shorthands return a standard Fetch `Response`. Always do `await res.json()` or `await res.text()` to read the body.
126
+
127
+ ## Handler code rules (strict)
128
+
129
+ Handler code **must**:
130
+
131
+ 1. Be a raw JavaScript expression — **no TypeScript**, no `import`, no `require`
132
+ 2. Start with `async (input) => {`
133
+ 3. Use `input.*` for any parameters (they're validated by your input schema before the handler runs — no need to check them)
134
+ 4. Return something meaningful — small JSON objects are easiest for the agent to reason with
135
+
136
+ ```js
137
+ async (input) => {
138
+ const res = await integration.fetch(`/some/path/${input.id}`)
139
+ const data = await res.json()
140
+ console.log('Fetched item:', data.name)
141
+ return { id: data.id, name: data.name }
142
+ }
143
+ ```
144
+
145
+ ## Input schema rules
146
+
147
+ `input_schema` is a **JSON Schema object** (not a string).
148
+
149
+ - Use `type: "object"` at the top level
150
+ - Set `additionalProperties: false` — keeps calls clean
151
+ - List every field the handler actually reads in `properties`
152
+ - Put required fields in `required`
153
+ - Keep types simple — `string`, `number`, `boolean`, `array`, `object`
154
+
155
+ ```json
156
+ {
157
+ "type": "object",
158
+ "properties": {
159
+ "owner": { "type": "string" },
160
+ "repo": { "type": "string" },
161
+ "title": { "type": "string" }
162
+ },
163
+ "required": ["owner", "repo", "title"],
164
+ "additionalProperties": false
165
+ }
166
+ ```
167
+
168
+ ## Base URLs — don't duplicate the version segment
169
+
170
+ Every integration has a base URL baked in. Your paths are appended to it. If the base is already `https://api.notion.com/v1`, write `/pages` — not `/v1/pages`.
171
+
172
+ ## Vibe-coding workflow
173
+
174
+ 1. **Understand** what the user wants the tool to do
175
+ 2. **Explore first** — use existing read tools (or build a quick `list_*` tool) to understand the API shape, field names, and ID formats
176
+ 3. **Draft** the write tool with the correct input schema and handler
177
+ 4. **Test** with `commandable_test_custom_tool` — run it with representative input before persisting
178
+ 5. **Persist** with `commandable_upsert_custom_tool` — the tool is live immediately
179
+
180
+ ---
181
+
182
+ ## Examples
183
+
184
+ The following are real tools taken from the Commandable integration library. Use these as reference for style, shape, and handler patterns.
185
+
186
+ ---
187
+
188
+ ### Worked example — Create a Stripe integration + add `list_customers`
189
+
190
+ **Step 1: Create the integration**
191
+
192
+ Call `commandable_upsert_custom_integration`:
193
+
194
+ ```json
195
+ {
196
+ "label": "Stripe",
197
+ "base_url": "https://api.stripe.com/v1",
198
+ "auth_type": "custom",
199
+ "credential_fields": [
200
+ { "name": "apiKey", "label": "API Key", "description": "Stripe secret key (starts with sk_)", "sensitive": true }
201
+ ],
202
+ "credential_injection": {
203
+ "headers": {
204
+ "Authorization": "Bearer {{apiKey}}"
205
+ }
206
+ },
207
+ "health_check_path": "/customers?limit=1",
208
+ "connection_hint": "1. Open https://dashboard.stripe.com/apikeys\\n2. In **Standard keys**, copy your **Secret key** (`sk_...`)\\n3. Paste it here as `apiKey`"
209
+ }
210
+ ```
211
+
212
+ This returns an `integration_id` and `credential_url`.
213
+
214
+ **Step 2: User enters credentials**
215
+
216
+ The user opens `credential_url` and enters `apiKey`. The model never sees it.
217
+
218
+ **Step 3: Add a tool against the new integration**
219
+
220
+ Call `commandable_upsert_custom_tool`:
221
+
222
+ ```json
223
+ {
224
+ "integration_id": "<integration_id_from_create_integration>",
225
+ "name": "list_customers",
226
+ "label": "List Customers",
227
+ "description": "List Stripe customers (optionally limit the count).",
228
+ "scope": "read",
229
+ "input_schema": {
230
+ "type": "object",
231
+ "properties": {
232
+ "limit": { "type": "number", "minimum": 1, "maximum": 100 }
233
+ },
234
+ "required": [],
235
+ "additionalProperties": false
236
+ },
237
+ "handler_code": "async (input) => {\\n const params = new URLSearchParams();\\n if (input.limit) params.set('limit', String(input.limit));\\n const q = params.toString() ? `?${params.toString()}` : '';\\n const res = await integration.fetch(`/customers${q}`);\\n return await res.json();\\n}"
238
+ }
239
+ ```
240
+
241
+ ---
242
+
243
+ ### Worked example — Create a Basic Auth integration
244
+
245
+ If an API uses Basic Auth, do:
246
+
247
+ ```json
248
+ {
249
+ "label": "My Internal API",
250
+ "base_url": "https://internal.example.com/api",
251
+ "auth_type": "basic",
252
+ "credential_fields": [
253
+ { "name": "username", "label": "Username" },
254
+ { "name": "password", "label": "Password", "sensitive": true }
255
+ ],
256
+ "basic_username_field": "username",
257
+ "basic_password_field": "password",
258
+ "health_check_path": "/health",
259
+ "connection_hint": "Please enter the user name and password provided on the my account page https://internal.example.com/my-account"
260
+ }
261
+ ```
262
+
263
+ ---
264
+
265
+ ### Example 1 — Trello: list the cards on a board list (read)
266
+
267
+ **What it does**: fetches all cards in a given Trello list (you need the list ID, which you can get from `get_board_lists`).
268
+
269
+ ```js
270
+ // handler_code
271
+ async (input) => {
272
+ const res = await integration.fetch(`/lists/${input.listId}/cards`)
273
+ return await res.json()
274
+ }
275
+ ```
276
+
277
+ ```json
278
+ // input_schema
279
+ {
280
+ "type": "object",
281
+ "properties": {
282
+ "listId": { "type": "string" }
283
+ },
284
+ "required": ["listId"],
285
+ "additionalProperties": false
286
+ }
287
+ ```
288
+
289
+ ---
290
+
291
+ ### Example 2 — Trello: create a card (write)
292
+
293
+ **What it does**: creates a new card in a list. Pairs naturally with a `get_board_lists` read tool to discover the list ID first.
294
+
295
+ ```js
296
+ // handler_code
297
+ async (input) => {
298
+ const params = new URLSearchParams()
299
+ params.set('idList', input.idList)
300
+ params.set('name', input.name)
301
+ if (input.desc) params.set('desc', input.desc)
302
+ if (input.due) params.set('due', input.due)
303
+ const res = await integration.fetch(`/cards?${params.toString()}`, { method: 'POST' })
304
+ const card = await res.json()
305
+ console.log('Created card:', card.name, card.url)
306
+ return { id: card.id, name: card.name, url: card.url }
307
+ }
308
+ ```
309
+
310
+ ```json
311
+ // input_schema
312
+ {
313
+ "type": "object",
314
+ "properties": {
315
+ "idList": { "type": "string" },
316
+ "name": { "type": "string" },
317
+ "desc": { "type": "string" },
318
+ "due": { "type": "string", "description": "ISO 8601 due date, e.g. 2025-12-01T12:00:00Z" }
319
+ },
320
+ "required": ["idList", "name"],
321
+ "additionalProperties": false
322
+ }
323
+ ```
324
+
325
+ ---
326
+
327
+ ### Example 3 — GitHub: list open issues on a repo (read)
328
+
329
+ **What it does**: lists issues for a given owner/repo, filterable by state and labels.
330
+
331
+ ```js
332
+ // handler_code
333
+ async (input) => {
334
+ const params = new URLSearchParams()
335
+ if (input.state) params.set('state', input.state)
336
+ if (input.labels) params.set('labels', input.labels)
337
+ if (input.per_page) params.set('per_page', String(input.per_page))
338
+ const query = params.toString() ? `?${params.toString()}` : ''
339
+ const res = await integration.fetch(`/repos/${input.owner}/${input.repo}/issues${query}`)
340
+ return await res.json()
341
+ }
342
+ ```
343
+
344
+ ```json
345
+ // input_schema
346
+ {
347
+ "type": "object",
348
+ "properties": {
349
+ "owner": { "type": "string" },
350
+ "repo": { "type": "string" },
351
+ "state": { "type": "string", "enum": ["open", "closed", "all"] },
352
+ "labels": { "type": "string", "description": "Comma-separated label names" },
353
+ "per_page": { "type": "number" }
354
+ },
355
+ "required": ["owner", "repo"],
356
+ "additionalProperties": false
357
+ }
358
+ ```
359
+
360
+ ---
361
+
362
+ ### Example 4 — GitHub: create an issue (write)
363
+
364
+ **What it does**: opens a new GitHub issue on a repo.
365
+
366
+ ```js
367
+ // handler_code
368
+ async (input) => {
369
+ const res = await integration.fetch(`/repos/${input.owner}/${input.repo}/issues`, {
370
+ method: 'POST',
371
+ body: {
372
+ title: input.title,
373
+ body: input.body,
374
+ assignees: input.assignees,
375
+ labels: input.labels,
376
+ },
377
+ })
378
+ const issue = await res.json()
379
+ console.log('Created issue #' + issue.number + ':', issue.title)
380
+ return { number: issue.number, url: issue.html_url, title: issue.title }
381
+ }
382
+ ```
383
+
384
+ ```json
385
+ // input_schema
386
+ {
387
+ "type": "object",
388
+ "properties": {
389
+ "owner": { "type": "string" },
390
+ "repo": { "type": "string" },
391
+ "title": { "type": "string" },
392
+ "body": { "type": "string" },
393
+ "assignees": { "type": "array", "items": { "type": "string" } },
394
+ "labels": { "type": "array", "items": { "type": "string" } }
395
+ },
396
+ "required": ["owner", "repo", "title"],
397
+ "additionalProperties": false
398
+ }
399
+ ```
400
+
401
+ ---
402
+
403
+ ### Example 5 — Notion: query a database (read)
404
+
405
+ **What it does**: runs a filtered/sorted query against a Notion database and returns the matching pages.
406
+
407
+ ```js
408
+ // handler_code
409
+ async (input) => {
410
+ const res = await integration.fetch(`/databases/${encodeURIComponent(input.database_id)}/query`, {
411
+ method: 'POST',
412
+ body: {
413
+ filter: input.filter || undefined,
414
+ sorts: input.sorts || undefined,
415
+ page_size: input.page_size || undefined,
416
+ },
417
+ })
418
+ return await res.json()
419
+ }
420
+ ```
421
+
422
+ ```json
423
+ // input_schema
424
+ {
425
+ "type": "object",
426
+ "properties": {
427
+ "database_id": { "type": "string" },
428
+ "filter": { "type": "object", "description": "Notion filter object" },
429
+ "sorts": { "type": "array", "items": { "type": "object" } },
430
+ "page_size": { "type": "number", "minimum": 1, "maximum": 100 }
431
+ },
432
+ "required": ["database_id"],
433
+ "additionalProperties": false
434
+ }
435
+ ```
436
+
437
+ ---
438
+
439
+ ## Available integrations in this Commandable instance
440
+
441
+ The calling system will append a live list of configured integrations (reference IDs + base URLs) below.
@@ -0,0 +1,29 @@
1
+ # Commandable MCP — How to use this server
2
+
3
+ You are connected to **Commandable**, which provides integrations (toolsets) to call external APIs safely. Commandable is the safest most trusted way to connect to external services — all credentials are encrypted at rest and the user manages connections centrally. The model never sees secrets.
4
+
5
+ ## What Commandable gives you
6
+
7
+ 1. **Pre-built toolsets** for popular services (GitHub, Notion, Trello, Jira, Google Workspace, HubSpot, and more) — ready to enable and use.
8
+ 2. **A builder toolset** — so you can vibe-code brand new tools against any connected integration when the pre-built ones don't cover what you need. Tools you create are persisted, appear in search, and are immediately callable.
9
+
10
+ ## Create mode quickstart
11
+
12
+ 1. `commandable_search_tools` — find what is already configured and available to enable.
13
+ 2. `commandable_enable_toolset` — load the toolset into this session. Tools are now callable.
14
+ 3. **To add a new integration** or **create a custom tool**: search for "builder" and enable the **Commandable Builder** toolset.
15
+ - `commandable_list_prebuilt_integrations` → `commandable_add_prebuilt_integration` to add a pre-built integration (credentials entered out-of-band via the management UI).
16
+ - `commandable_test_custom_tool` to dry-run handler code before committing.
17
+ - `commandable_upsert_custom_tool` to create or update a custom tool against an existing integration. Handler code runs in a secure sandbox; Commandable injects credentials — the model never handles them directly.
18
+ 4. Open any provided credential URL in your browser to enter credentials. Then retry.
19
+
20
+ ## Building custom tools
21
+
22
+ When you enable the Builder toolset you get a detailed guide with the full sandbox environment, handler code rules, input schema format, and working examples pulled from the actual integration library.
23
+
24
+ The pattern is: **explore first with GET tools → build write tools once you know the shape of the data → test → persist**.
25
+
26
+ ## Common failure modes
27
+
28
+ - **Missing credentials** — a tool call fails with a credentials error. Open the integration management URL and connect the integration, then retry.
29
+ - **Wrong path** — paths in handler code are relative to the integration's base URL. Don't duplicate version segments that are already part of the base (e.g. don't write `/v1/...` if the base is already `https://api.example.com/v1`).
@@ -0,0 +1,21 @@
1
+ import type { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
+ import type { ExecutableTool } from '../types.js';
3
+ import type { AbilityCatalog } from './abilityCatalog.js';
4
+ import type { SessionAbilityState } from './sessionState.js';
5
+ import type { MetaToolContext } from './metaTools.js';
6
+ export interface ToolIndex {
7
+ list: Array<{
8
+ name: string;
9
+ description?: string;
10
+ inputSchema: any;
11
+ }>;
12
+ byName: Map<string, ExecutableTool>;
13
+ }
14
+ export declare function registerToolHandlers(server: Server, tools: ToolIndex, createMode?: {
15
+ catalogRef: {
16
+ current: AbilityCatalog;
17
+ };
18
+ sessionState: SessionAbilityState;
19
+ ctx?: MetaToolContext;
20
+ }): void;
21
+ //# sourceMappingURL=handlers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../src/mcp/handlers.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAA;AACvE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AACjD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AACzD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AAE5D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAErD,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,GAAG,CAAA;KAAE,CAAC,CAAA;IACrE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAA;CACpC;AAaD,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,SAAS,EAChB,UAAU,CAAC,EAAE;IAAE,UAAU,EAAE;QAAE,OAAO,EAAE,cAAc,CAAA;KAAE,CAAC;IAAC,YAAY,EAAE,mBAAmB,CAAC;IAAC,GAAG,CAAC,EAAE,eAAe,CAAA;CAAE,GACjH,IAAI,CAwFN"}
@@ -0,0 +1,86 @@
1
+ import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
2
+ import { getMetaToolDefinitions, handleMetaToolCall } from './metaTools.js';
3
+ function formatAsText(value) {
4
+ if (typeof value === 'string')
5
+ return value;
6
+ try {
7
+ return JSON.stringify(value, null, 2);
8
+ }
9
+ catch {
10
+ return String(value);
11
+ }
12
+ }
13
+ export function registerToolHandlers(server, tools, createMode) {
14
+ const metaToolDefs = getMetaToolDefinitions();
15
+ server.setRequestHandler(ListToolsRequestSchema, async (_req, extra) => {
16
+ if (!createMode)
17
+ return { tools: tools.list };
18
+ const sessionId = extra?.sessionId;
19
+ const active = createMode.sessionState.getActiveToolNames(sessionId);
20
+ const toolDefs = createMode.catalogRef.current.getToolDefinitions([...active]);
21
+ return { tools: [...metaToolDefs, ...toolDefs] };
22
+ });
23
+ server.setRequestHandler(CallToolRequestSchema, async (req, extra) => {
24
+ const name = req.params.name;
25
+ const args = (req.params.arguments ?? {});
26
+ const sessionId = extra?.sessionId;
27
+ if (createMode) {
28
+ const metaRes = await handleMetaToolCall({
29
+ name,
30
+ args,
31
+ sessionId,
32
+ catalog: createMode.catalogRef.current,
33
+ sessionState: createMode.sessionState,
34
+ ctx: createMode.ctx,
35
+ });
36
+ if (metaRes.handled) {
37
+ if (metaRes.listChanged) {
38
+ await server.sendToolListChanged();
39
+ }
40
+ return {
41
+ content: [{ type: 'text', text: formatAsText(metaRes.result) }],
42
+ };
43
+ }
44
+ if (!createMode.sessionState.isToolActive(sessionId, name)) {
45
+ throw new Error(`Tool not enabled in this session: ${name}. Use ${metaToolDefs[0].name} + ${metaToolDefs[1].name} to enable a toolset first.`);
46
+ }
47
+ const tool = createMode.catalogRef.current.getExecutableTool(name);
48
+ if (!tool)
49
+ throw new Error(`Unknown tool: ${name}`);
50
+ const res = await tool.run(args);
51
+ if (!res.success) {
52
+ return {
53
+ content: [
54
+ { type: 'text', text: `Tool error: ${formatAsText(res.result)}` },
55
+ ...(res.logs?.length ? [{ type: 'text', text: `Logs:\n${res.logs.join('\n')}` }] : []),
56
+ ],
57
+ };
58
+ }
59
+ return {
60
+ content: [
61
+ { type: 'text', text: formatAsText(res.result) },
62
+ ...(res.logs?.length ? [{ type: 'text', text: `Logs:\n${res.logs.join('\n')}` }] : []),
63
+ ],
64
+ };
65
+ }
66
+ const tool = tools.byName.get(name);
67
+ if (!tool)
68
+ throw new Error(`Unknown tool: ${name}`);
69
+ const res = await tool.run(args);
70
+ if (!res.success) {
71
+ return {
72
+ content: [
73
+ { type: 'text', text: `Tool error: ${formatAsText(res.result)}` },
74
+ ...(res.logs?.length ? [{ type: 'text', text: `Logs:\n${res.logs.join('\n')}` }] : []),
75
+ ],
76
+ };
77
+ }
78
+ return {
79
+ content: [
80
+ { type: 'text', text: formatAsText(res.result) },
81
+ ...(res.logs?.length ? [{ type: 'text', text: `Logs:\n${res.logs.join('\n')}` }] : []),
82
+ ],
83
+ };
84
+ });
85
+ }
86
+ //# sourceMappingURL=handlers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handlers.js","sourceRoot":"","sources":["../../src/mcp/handlers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,79 @@
1
+ import type { AbilityCatalog } from './abilityCatalog.js';
2
+ import type { SessionAbilityState } from './sessionState.js';
3
+ import type { McpToolDefinition } from './toolAdapter.js';
4
+ import type { DbClient } from '../db/client.js';
5
+ import type { SqlCredentialStore } from '../db/credentialStore.js';
6
+ import type { IntegrationData } from '../types.js';
7
+ import type { IntegrationProxy } from '../integrations/proxy.js';
8
+ import type { IntegrationTypeConfig } from '../types.js';
9
+ export declare const META_TOOL_NAMES: {
10
+ readonly readme: "commandable_readme";
11
+ readonly searchTools: "commandable_search_tools";
12
+ readonly enableToolset: "commandable_enable_toolset";
13
+ readonly disableToolset: "commandable_disable_toolset";
14
+ readonly listPrebuiltIntegrations: "commandable_list_prebuilt_integrations";
15
+ readonly addPrebuiltIntegration: "commandable_add_prebuilt_integration";
16
+ readonly upsertCustomIntegration: "commandable_upsert_custom_integration";
17
+ readonly upsertCustomTool: "commandable_upsert_custom_tool";
18
+ readonly deleteCustomIntegration: "commandable_delete_custom_integration";
19
+ readonly deleteCustomTool: "commandable_delete_custom_tool";
20
+ readonly testCustomTool: "commandable_test_custom_tool";
21
+ };
22
+ export type MetaToolName = typeof META_TOOL_NAMES[keyof typeof META_TOOL_NAMES];
23
+ export type { McpToolDefinition } from './toolAdapter.js';
24
+ export type MetaToolContext = {
25
+ spaceId: string;
26
+ db: DbClient;
27
+ credentialStore: SqlCredentialStore;
28
+ proxy: IntegrationProxy;
29
+ /**
30
+ * Optional. If set, meta-tools can return URLs like:
31
+ * `${credentialSetupBaseUrl}/integrations/<integrationId>`
32
+ */
33
+ credentialSetupBaseUrl?: string;
34
+ /**
35
+ * Mutable reference used by create mode. If supplied, meta-tools may update
36
+ * it after adding integrations (e.g., push new record or reload from DB).
37
+ */
38
+ integrationsRef?: {
39
+ current: IntegrationData[];
40
+ };
41
+ integrationTypeConfigsRef?: {
42
+ current: IntegrationTypeConfig[];
43
+ };
44
+ /**
45
+ * Mutable reference to tool index + catalog. If supplied, meta-tools may
46
+ * dynamically register new tools and rebuild the catalog.
47
+ *
48
+ * (Wired in during the \"dynamic tool loading\" step.)
49
+ */
50
+ toolIndexRef?: {
51
+ byName: Map<string, any>;
52
+ list?: Array<{
53
+ name: string;
54
+ description?: string;
55
+ inputSchema: any;
56
+ }>;
57
+ };
58
+ catalogRef?: {
59
+ current: AbilityCatalog;
60
+ };
61
+ };
62
+ export declare function getMetaToolDefinitions(): McpToolDefinition[];
63
+ export declare function getBuilderToolDefinitions(): McpToolDefinition[];
64
+ export type MetaToolCallResult = {
65
+ handled: false;
66
+ } | {
67
+ handled: true;
68
+ listChanged: boolean;
69
+ result: any;
70
+ };
71
+ export declare function handleMetaToolCall(params: {
72
+ name: string;
73
+ args: any;
74
+ sessionId: string | undefined;
75
+ catalog: AbilityCatalog;
76
+ sessionState: SessionAbilityState;
77
+ ctx?: MetaToolContext;
78
+ }): Promise<MetaToolCallResult>;
79
+ //# sourceMappingURL=metaTools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metaTools.d.ts","sourceRoot":"","sources":["../../src/mcp/metaTools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAEzD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AAC5D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AASzD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAE/C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAClE,OAAO,KAAK,EAAgC,eAAe,EAAE,MAAM,aAAa,CAAA;AAChF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AAYhE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AAExD,eAAO,MAAM,eAAe;;;;;;;;;;;;CAYlB,CAAA;AAEV,MAAM,MAAM,YAAY,GAAG,OAAO,eAAe,CAAC,MAAM,OAAO,eAAe,CAAC,CAAA;AAE/E,YAAY,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AAoCzD,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,MAAM,CAAA;IACf,EAAE,EAAE,QAAQ,CAAA;IACZ,eAAe,EAAE,kBAAkB,CAAA;IACnC,KAAK,EAAE,gBAAgB,CAAA;IACvB;;;OAGG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAA;IAC/B;;;OAGG;IACH,eAAe,CAAC,EAAE;QAAE,OAAO,EAAE,eAAe,EAAE,CAAA;KAAE,CAAA;IAChD,yBAAyB,CAAC,EAAE;QAAE,OAAO,EAAE,qBAAqB,EAAE,CAAA;KAAE,CAAA;IAChE;;;;;OAKG;IACH,YAAY,CAAC,EAAE;QACb,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;QACxB,IAAI,CAAC,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,WAAW,CAAC,EAAE,MAAM,CAAC;YAAC,WAAW,EAAE,GAAG,CAAA;SAAE,CAAC,CAAA;KACvE,CAAA;IACD,UAAU,CAAC,EAAE;QAAE,OAAO,EAAE,cAAc,CAAA;KAAE,CAAA;CACzC,CAAA;AAED,wBAAgB,sBAAsB,IAAI,iBAAiB,EAAE,CA6C5D;AAED,wBAAgB,yBAAyB,IAAI,iBAAiB,EAAE,CAyI/D;AAED,MAAM,MAAM,kBAAkB,GAC1B;IAAE,OAAO,EAAE,KAAK,CAAA;CAAE,GAClB;IAAE,OAAO,EAAE,IAAI,CAAC;IAAC,WAAW,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,GAAG,CAAA;CAAE,CAAA;AAExD,wBAAsB,kBAAkB,CAAC,MAAM,EAAE;IAC/C,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,GAAG,CAAA;IACT,SAAS,EAAE,MAAM,GAAG,SAAS,CAAA;IAC7B,OAAO,EAAE,cAAc,CAAA;IACvB,YAAY,EAAE,mBAAmB,CAAA;IACjC,GAAG,CAAC,EAAE,eAAe,CAAA;CACtB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAisB9B"}