digital-tools 2.1.1 → 2.3.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 (293) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +2 -0
  3. package/dist/client.d.ts +109 -0
  4. package/dist/client.d.ts.map +1 -0
  5. package/dist/client.js +69 -0
  6. package/dist/client.js.map +1 -0
  7. package/dist/define.d.ts +2 -2
  8. package/dist/define.d.ts.map +1 -1
  9. package/dist/define.js +22 -20
  10. package/dist/define.js.map +1 -1
  11. package/dist/function-ref.d.ts +229 -0
  12. package/dist/function-ref.d.ts.map +1 -0
  13. package/dist/function-ref.js +28 -0
  14. package/dist/function-ref.js.map +1 -0
  15. package/dist/function-sugar.d.ts +57 -0
  16. package/dist/function-sugar.d.ts.map +1 -0
  17. package/dist/function-sugar.js +79 -0
  18. package/dist/function-sugar.js.map +1 -0
  19. package/dist/index.d.ts +10 -3
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +24 -4
  22. package/dist/index.js.map +1 -1
  23. package/dist/providers/analytics/mixpanel.d.ts.map +1 -1
  24. package/dist/providers/analytics/mixpanel.js +21 -18
  25. package/dist/providers/analytics/mixpanel.js.map +1 -1
  26. package/dist/providers/calendar/cal-com.d.ts.map +1 -1
  27. package/dist/providers/calendar/cal-com.js +10 -10
  28. package/dist/providers/calendar/cal-com.js.map +1 -1
  29. package/dist/providers/calendar/google-calendar.d.ts.map +1 -1
  30. package/dist/providers/calendar/google-calendar.js +4 -4
  31. package/dist/providers/calendar/google-calendar.js.map +1 -1
  32. package/dist/providers/crm/hubspot.d.ts.map +1 -1
  33. package/dist/providers/crm/hubspot.js +107 -85
  34. package/dist/providers/crm/hubspot.js.map +1 -1
  35. package/dist/providers/development/github.d.ts.map +1 -1
  36. package/dist/providers/development/github.js +40 -43
  37. package/dist/providers/development/github.js.map +1 -1
  38. package/dist/providers/ecommerce/shopify.d.ts.map +1 -1
  39. package/dist/providers/ecommerce/shopify.js +79 -62
  40. package/dist/providers/ecommerce/shopify.js.map +1 -1
  41. package/dist/providers/email/resend.d.ts.map +1 -1
  42. package/dist/providers/email/resend.js +20 -16
  43. package/dist/providers/email/resend.js.map +1 -1
  44. package/dist/providers/email/sendgrid.d.ts.map +1 -1
  45. package/dist/providers/email/sendgrid.js +12 -9
  46. package/dist/providers/email/sendgrid.js.map +1 -1
  47. package/dist/providers/finance/stripe.d.ts.map +1 -1
  48. package/dist/providers/finance/stripe.js +44 -42
  49. package/dist/providers/finance/stripe.js.map +1 -1
  50. package/dist/providers/forms/typeform.d.ts.map +1 -1
  51. package/dist/providers/forms/typeform.js +68 -58
  52. package/dist/providers/forms/typeform.js.map +1 -1
  53. package/dist/providers/knowledge/notion.d.ts.map +1 -1
  54. package/dist/providers/knowledge/notion.js +75 -41
  55. package/dist/providers/knowledge/notion.js.map +1 -1
  56. package/dist/providers/marketing/mailchimp.d.ts.map +1 -1
  57. package/dist/providers/marketing/mailchimp.js +74 -61
  58. package/dist/providers/marketing/mailchimp.js.map +1 -1
  59. package/dist/providers/media/cloudinary.d.ts.map +1 -1
  60. package/dist/providers/media/cloudinary.js +30 -28
  61. package/dist/providers/media/cloudinary.js.map +1 -1
  62. package/dist/providers/messaging/slack.d.ts.map +1 -1
  63. package/dist/providers/messaging/slack.js +75 -58
  64. package/dist/providers/messaging/slack.js.map +1 -1
  65. package/dist/providers/messaging/twilio-sms.d.ts.map +1 -1
  66. package/dist/providers/messaging/twilio-sms.js +33 -15
  67. package/dist/providers/messaging/twilio-sms.js.map +1 -1
  68. package/dist/providers/project-management/linear.d.ts.map +1 -1
  69. package/dist/providers/project-management/linear.js +31 -27
  70. package/dist/providers/project-management/linear.js.map +1 -1
  71. package/dist/providers/spreadsheet/google-sheets.d.ts.map +1 -1
  72. package/dist/providers/spreadsheet/google-sheets.js +21 -18
  73. package/dist/providers/spreadsheet/google-sheets.js.map +1 -1
  74. package/dist/providers/spreadsheet/xlsx.d.ts.map +1 -1
  75. package/dist/providers/spreadsheet/xlsx.js +4 -4
  76. package/dist/providers/spreadsheet/xlsx.js.map +1 -1
  77. package/dist/providers/storage/index.js +1 -0
  78. package/dist/providers/storage/index.js.map +1 -1
  79. package/dist/providers/storage/s3.d.ts.map +1 -1
  80. package/dist/providers/storage/s3.js +36 -27
  81. package/dist/providers/storage/s3.js.map +1 -1
  82. package/dist/providers/support/zendesk.d.ts.map +1 -1
  83. package/dist/providers/support/zendesk.js +24 -25
  84. package/dist/providers/support/zendesk.js.map +1 -1
  85. package/dist/providers/tasks/todoist.d.ts.map +1 -1
  86. package/dist/providers/tasks/todoist.js +18 -18
  87. package/dist/providers/tasks/todoist.js.map +1 -1
  88. package/dist/providers/video-conferencing/google-meet.d.ts.map +1 -1
  89. package/dist/providers/video-conferencing/google-meet.js +11 -11
  90. package/dist/providers/video-conferencing/google-meet.js.map +1 -1
  91. package/dist/providers/video-conferencing/jitsi.js +14 -14
  92. package/dist/providers/video-conferencing/jitsi.js.map +1 -1
  93. package/dist/providers/video-conferencing/teams.d.ts.map +1 -1
  94. package/dist/providers/video-conferencing/teams.js +9 -7
  95. package/dist/providers/video-conferencing/teams.js.map +1 -1
  96. package/dist/providers/video-conferencing/zoom.d.ts.map +1 -1
  97. package/dist/providers/video-conferencing/zoom.js +26 -24
  98. package/dist/providers/video-conferencing/zoom.js.map +1 -1
  99. package/dist/tools/data.d.ts.map +1 -1
  100. package/dist/tools/data.js +5 -12
  101. package/dist/tools/data.js.map +1 -1
  102. package/dist/tools/index.d.ts +1 -0
  103. package/dist/tools/index.d.ts.map +1 -1
  104. package/dist/tools/index.js +1 -0
  105. package/dist/tools/index.js.map +1 -1
  106. package/dist/tools/system.d.ts +289 -0
  107. package/dist/tools/system.d.ts.map +1 -0
  108. package/dist/tools/system.js +752 -0
  109. package/dist/tools/system.js.map +1 -0
  110. package/dist/tools/web.d.ts.map +1 -1
  111. package/dist/tools/web.js +22 -10
  112. package/dist/tools/web.js.map +1 -1
  113. package/dist/track-record.d.ts +101 -0
  114. package/dist/track-record.d.ts.map +1 -0
  115. package/dist/track-record.js +17 -0
  116. package/dist/track-record.js.map +1 -0
  117. package/dist/types.d.ts +210 -9
  118. package/dist/types.d.ts.map +1 -1
  119. package/dist/verb-registration.d.ts +122 -0
  120. package/dist/verb-registration.d.ts.map +1 -0
  121. package/dist/verb-registration.js +176 -0
  122. package/dist/verb-registration.js.map +1 -0
  123. package/dist/worker.d.ts +93 -0
  124. package/dist/worker.d.ts.map +1 -0
  125. package/dist/worker.js +315 -0
  126. package/dist/worker.js.map +1 -0
  127. package/dist/wrap.d.ts +89 -0
  128. package/dist/wrap.d.ts.map +1 -0
  129. package/dist/wrap.js +225 -0
  130. package/dist/wrap.js.map +1 -0
  131. package/package.json +21 -4
  132. package/src/client.ts +136 -0
  133. package/src/define.ts +31 -37
  134. package/src/function-ref.ts +264 -0
  135. package/src/function-sugar.ts +134 -0
  136. package/src/index.ts +132 -10
  137. package/src/providers/analytics/mixpanel.ts +19 -18
  138. package/src/providers/calendar/cal-com.ts +29 -18
  139. package/src/providers/calendar/google-calendar.ts +20 -14
  140. package/src/providers/crm/hubspot.ts +225 -99
  141. package/src/providers/development/github.ts +206 -135
  142. package/src/providers/ecommerce/shopify.ts +250 -89
  143. package/src/providers/email/resend.ts +101 -28
  144. package/src/providers/email/sendgrid.ts +12 -9
  145. package/src/providers/finance/stripe.ts +128 -49
  146. package/src/providers/forms/typeform.ts +74 -58
  147. package/src/providers/knowledge/notion.ts +340 -88
  148. package/src/providers/marketing/mailchimp.ts +86 -70
  149. package/src/providers/media/cloudinary.ts +99 -41
  150. package/src/providers/messaging/slack.ts +283 -85
  151. package/src/providers/messaging/twilio-sms.ts +35 -15
  152. package/src/providers/project-management/linear.ts +143 -55
  153. package/src/providers/spreadsheet/google-sheets.ts +222 -56
  154. package/src/providers/spreadsheet/xlsx.ts +47 -16
  155. package/src/providers/storage/s3.ts +119 -47
  156. package/src/providers/support/zendesk.ts +196 -46
  157. package/src/providers/tasks/todoist.ts +20 -26
  158. package/src/providers/video-conferencing/google-meet.ts +17 -20
  159. package/src/providers/video-conferencing/jitsi.ts +14 -14
  160. package/src/providers/video-conferencing/teams.ts +14 -13
  161. package/src/providers/video-conferencing/zoom.ts +54 -49
  162. package/src/tools/data.ts +6 -16
  163. package/src/tools/index.ts +1 -0
  164. package/src/tools/system.ts +887 -0
  165. package/src/tools/web.ts +22 -10
  166. package/src/track-record.ts +106 -0
  167. package/src/types.ts +241 -13
  168. package/src/verb-registration.ts +197 -0
  169. package/src/worker.ts +370 -0
  170. package/src/wrap.ts +260 -0
  171. package/test/client.test.ts +146 -0
  172. package/test/communication-tools-extended.test.ts +734 -0
  173. package/test/data-tools-extended.test.ts +743 -0
  174. package/test/define-extended.test.ts +819 -0
  175. package/test/define.test.ts +150 -41
  176. package/test/entities.test.ts +623 -0
  177. package/test/extended-entities.test.ts +1228 -0
  178. package/test/provider-implementations.test.ts +725 -0
  179. package/test/provider-registry-extended.test.ts +583 -0
  180. package/test/providers/google-sheets.test.ts +851 -0
  181. package/test/providers/helpers.ts +554 -0
  182. package/test/providers/hubspot.test.ts +576 -0
  183. package/test/providers/slack.test.ts +932 -0
  184. package/test/providers/stripe.test.ts +701 -0
  185. package/test/providers.test.ts +578 -0
  186. package/test/system-tools-extended.test.ts +632 -0
  187. package/test/system.test.ts +673 -0
  188. package/test/tools.test.ts +15 -11
  189. package/test/types.test.ts +402 -0
  190. package/test/verb-registration.test.ts +395 -0
  191. package/test/web-tools.test.ts +553 -0
  192. package/test/worker-extended.test.ts +699 -0
  193. package/test/worker.test.ts +576 -0
  194. package/test/wrap.test.ts +366 -0
  195. package/tsconfig.json +3 -13
  196. package/vitest.config.ts +37 -0
  197. package/wrangler.jsonc +9 -0
  198. package/.turbo/turbo-build.log +0 -5
  199. package/dist/providers/voice/vapi.d.ts +0 -27
  200. package/dist/providers/voice/vapi.d.ts.map +0 -1
  201. package/dist/providers/voice/vapi.js +0 -440
  202. package/dist/providers/voice/vapi.js.map +0 -1
  203. package/src/define.js +0 -267
  204. package/src/entities/advertising.js +0 -999
  205. package/src/entities/ai.js +0 -756
  206. package/src/entities/analytics.js +0 -1588
  207. package/src/entities/automation.js +0 -601
  208. package/src/entities/communication.js +0 -1150
  209. package/src/entities/crm.js +0 -1386
  210. package/src/entities/design.js +0 -546
  211. package/src/entities/development.js +0 -2212
  212. package/src/entities/document.js +0 -874
  213. package/src/entities/ecommerce.js +0 -1429
  214. package/src/entities/experiment.js +0 -1039
  215. package/src/entities/finance.js +0 -3478
  216. package/src/entities/forms.js +0 -1892
  217. package/src/entities/hr.js +0 -661
  218. package/src/entities/identity.js +0 -997
  219. package/src/entities/index.js +0 -282
  220. package/src/entities/infrastructure.js +0 -1153
  221. package/src/entities/knowledge.js +0 -1438
  222. package/src/entities/marketing.js +0 -1610
  223. package/src/entities/media.js +0 -1634
  224. package/src/entities/notification.js +0 -1199
  225. package/src/entities/presentation.js +0 -1274
  226. package/src/entities/productivity.js +0 -1317
  227. package/src/entities/project-management.js +0 -1136
  228. package/src/entities/recruiting.js +0 -736
  229. package/src/entities/shipping.js +0 -509
  230. package/src/entities/signature.js +0 -1102
  231. package/src/entities/site.js +0 -222
  232. package/src/entities/spreadsheet.js +0 -1341
  233. package/src/entities/storage.js +0 -1198
  234. package/src/entities/support.js +0 -1166
  235. package/src/entities/video-conferencing.js +0 -1750
  236. package/src/entities/video.js +0 -950
  237. package/src/entities.js +0 -1663
  238. package/src/index.js +0 -74
  239. package/src/providers/analytics/index.js +0 -17
  240. package/src/providers/analytics/mixpanel.js +0 -255
  241. package/src/providers/calendar/cal-com.js +0 -303
  242. package/src/providers/calendar/google-calendar.js +0 -335
  243. package/src/providers/calendar/index.js +0 -20
  244. package/src/providers/crm/hubspot.js +0 -566
  245. package/src/providers/crm/index.js +0 -17
  246. package/src/providers/development/github.js +0 -472
  247. package/src/providers/development/index.js +0 -17
  248. package/src/providers/ecommerce/index.js +0 -17
  249. package/src/providers/ecommerce/shopify.js +0 -378
  250. package/src/providers/email/index.js +0 -20
  251. package/src/providers/email/resend.js +0 -258
  252. package/src/providers/email/sendgrid.js +0 -161
  253. package/src/providers/finance/index.js +0 -17
  254. package/src/providers/finance/stripe.js +0 -549
  255. package/src/providers/forms/index.js +0 -17
  256. package/src/providers/forms/typeform.js +0 -500
  257. package/src/providers/index.js +0 -123
  258. package/src/providers/knowledge/index.js +0 -17
  259. package/src/providers/knowledge/notion.js +0 -389
  260. package/src/providers/marketing/index.js +0 -17
  261. package/src/providers/marketing/mailchimp.js +0 -443
  262. package/src/providers/media/cloudinary.js +0 -318
  263. package/src/providers/media/index.js +0 -17
  264. package/src/providers/messaging/index.js +0 -20
  265. package/src/providers/messaging/slack.js +0 -393
  266. package/src/providers/messaging/twilio-sms.js +0 -249
  267. package/src/providers/project-management/index.js +0 -17
  268. package/src/providers/project-management/linear.js +0 -575
  269. package/src/providers/registry.js +0 -86
  270. package/src/providers/spreadsheet/google-sheets.js +0 -375
  271. package/src/providers/spreadsheet/index.js +0 -20
  272. package/src/providers/spreadsheet/xlsx.js +0 -423
  273. package/src/providers/storage/index.js +0 -24
  274. package/src/providers/storage/s3.js +0 -419
  275. package/src/providers/support/index.js +0 -17
  276. package/src/providers/support/zendesk.js +0 -373
  277. package/src/providers/tasks/index.js +0 -17
  278. package/src/providers/tasks/todoist.js +0 -286
  279. package/src/providers/types.js +0 -9
  280. package/src/providers/video-conferencing/google-meet.js +0 -286
  281. package/src/providers/video-conferencing/index.js +0 -31
  282. package/src/providers/video-conferencing/jitsi.js +0 -254
  283. package/src/providers/video-conferencing/teams.js +0 -270
  284. package/src/providers/video-conferencing/zoom.js +0 -332
  285. package/src/registry.js +0 -128
  286. package/src/tools/communication.js +0 -184
  287. package/src/tools/data.js +0 -205
  288. package/src/tools/index.js +0 -11
  289. package/src/tools/web.js +0 -137
  290. package/src/types.js +0 -10
  291. package/test/define.test.js +0 -306
  292. package/test/registry.test.js +0 -357
  293. package/test/tools.test.js +0 -363
package/src/worker.ts ADDED
@@ -0,0 +1,370 @@
1
+ /**
2
+ * Tool Worker - provides tool service via RPC
3
+ *
4
+ * This worker can be deployed to Cloudflare Workers or run locally via Miniflare.
5
+ * It exposes tool registration, discovery, execution, and MCP conversion via Workers RPC.
6
+ *
7
+ * Uses Cloudflare Workers RPC (WorkerEntrypoint, RpcTarget) for communication.
8
+ */
9
+
10
+ import { WorkerEntrypoint, RpcTarget } from 'cloudflare:workers'
11
+ import type { AnyTool, ToolCategory, ToolQuery, MCPTool, ToolSubcategory } from './types.js'
12
+
13
+ /**
14
+ * Core tool service - extends RpcTarget so it can be passed over RPC
15
+ *
16
+ * Contains all tool functionality: registration, discovery, execution, and MCP conversion
17
+ */
18
+ export class ToolServiceCore extends RpcTarget {
19
+ private tools: Map<string, AnyTool> = new Map()
20
+
21
+ constructor() {
22
+ super()
23
+ // Register built-in tools
24
+ this.registerBuiltins()
25
+ }
26
+
27
+ /**
28
+ * Register built-in tools
29
+ */
30
+ private registerBuiltins(): void {
31
+ // data.json.parse
32
+ this.register({
33
+ id: 'data.json.parse',
34
+ name: 'Parse JSON',
35
+ description: 'Parse a JSON string into an object',
36
+ category: 'data',
37
+ subcategory: 'transform',
38
+ parameters: [
39
+ {
40
+ name: 'text',
41
+ description: 'JSON string to parse',
42
+ schema: { type: 'string' },
43
+ required: true,
44
+ },
45
+ ],
46
+ handler: async (input: { text: string }) => {
47
+ try {
48
+ const data = JSON.parse(input.text)
49
+ return { data, valid: true }
50
+ } catch (e) {
51
+ return {
52
+ data: null,
53
+ valid: false,
54
+ error: e instanceof Error ? e.message : 'Invalid JSON',
55
+ }
56
+ }
57
+ },
58
+ tags: ['json', 'parse', 'transform'],
59
+ })
60
+
61
+ // data.csv.parse
62
+ this.register({
63
+ id: 'data.csv.parse',
64
+ name: 'Parse CSV',
65
+ description: 'Parse CSV text into an array of objects',
66
+ category: 'data',
67
+ subcategory: 'transform',
68
+ parameters: [
69
+ {
70
+ name: 'text',
71
+ description: 'CSV text to parse',
72
+ schema: { type: 'string' },
73
+ required: true,
74
+ },
75
+ {
76
+ name: 'delimiter',
77
+ description: 'Column delimiter (default: comma)',
78
+ schema: { type: 'string' },
79
+ required: false,
80
+ },
81
+ {
82
+ name: 'hasHeaders',
83
+ description: 'First row is headers (default: true)',
84
+ schema: { type: 'boolean' },
85
+ required: false,
86
+ },
87
+ ],
88
+ handler: async (input: { text: string; delimiter?: string; hasHeaders?: boolean }) => {
89
+ const delimiter = input.delimiter || ','
90
+ const hasHeaders = input.hasHeaders !== false
91
+ const lines = input.text.split('\n').filter((line) => line.trim())
92
+
93
+ if (lines.length === 0) {
94
+ return { rows: [], headers: [], rowCount: 0 }
95
+ }
96
+
97
+ const firstLine = lines[0]!
98
+ const headers = hasHeaders
99
+ ? firstLine.split(delimiter).map((h) => h.trim())
100
+ : firstLine.split(delimiter).map((_, i) => `column${i + 1}`)
101
+
102
+ const dataLines = hasHeaders ? lines.slice(1) : lines
103
+
104
+ const rows = dataLines.map((line) => {
105
+ const values = line.split(delimiter).map((v) => v.trim())
106
+ const row: Record<string, string> = {}
107
+ headers.forEach((header, i) => {
108
+ row[header] = values[i] || ''
109
+ })
110
+ return row
111
+ })
112
+
113
+ return { rows, headers, rowCount: rows.length }
114
+ },
115
+ tags: ['csv', 'parse', 'transform'],
116
+ })
117
+
118
+ // data.transform
119
+ this.register({
120
+ id: 'data.transform',
121
+ name: 'Transform Data',
122
+ description: 'Transform data by mapping fields to new structure',
123
+ category: 'data',
124
+ subcategory: 'transform',
125
+ parameters: [
126
+ {
127
+ name: 'data',
128
+ description: 'Source data to transform',
129
+ schema: {},
130
+ required: true,
131
+ },
132
+ {
133
+ name: 'transform',
134
+ description: 'Mapping of output fields to input paths',
135
+ schema: { type: 'object' },
136
+ required: true,
137
+ },
138
+ ],
139
+ handler: async (input: { data: unknown; transform: Record<string, string> }) => {
140
+ const result: Record<string, unknown> = {}
141
+
142
+ for (const [outputKey, inputPath] of Object.entries(input.transform)) {
143
+ const pathParts = inputPath.split('.')
144
+ let value: unknown = input.data
145
+
146
+ for (const part of pathParts) {
147
+ if (value && typeof value === 'object' && part in (value as Record<string, unknown>)) {
148
+ value = (value as Record<string, unknown>)[part]
149
+ } else {
150
+ value = undefined
151
+ break
152
+ }
153
+ }
154
+
155
+ result[outputKey] = value
156
+ }
157
+
158
+ return { result }
159
+ },
160
+ tags: ['transform', 'map', 'extract'],
161
+ })
162
+ }
163
+
164
+ /**
165
+ * Register a tool in the service registry
166
+ */
167
+ register(tool: AnyTool): void {
168
+ this.tools.set(tool.id, tool)
169
+ }
170
+
171
+ /**
172
+ * Unregister a tool
173
+ */
174
+ unregister(id: string): boolean {
175
+ return this.tools.delete(id)
176
+ }
177
+
178
+ /**
179
+ * Get a tool by ID
180
+ */
181
+ get(id: string): AnyTool | undefined {
182
+ return this.tools.get(id)
183
+ }
184
+
185
+ /**
186
+ * Check if a tool exists
187
+ */
188
+ has(id: string): boolean {
189
+ return this.tools.has(id)
190
+ }
191
+
192
+ /**
193
+ * List all tool IDs
194
+ */
195
+ list(): string[] {
196
+ return Array.from(this.tools.keys())
197
+ }
198
+
199
+ /**
200
+ * Query tools with filtering
201
+ */
202
+ query(options: ToolQuery): AnyTool[] {
203
+ let results = Array.from(this.tools.values())
204
+
205
+ // Filter by category
206
+ if (options.category) {
207
+ results = results.filter((t) => t.category === options.category)
208
+ }
209
+
210
+ // Filter by subcategory
211
+ if (options.subcategory) {
212
+ results = results.filter((t) => t.subcategory === options.subcategory)
213
+ }
214
+
215
+ // Filter by tags
216
+ if (options.tags && options.tags.length > 0) {
217
+ results = results.filter((t) => t.tags && options.tags!.some((tag) => t.tags!.includes(tag)))
218
+ }
219
+
220
+ // Filter by audience
221
+ if (options.audience) {
222
+ results = results.filter(
223
+ (t) => t.audience === options.audience || t.audience === 'both' || !t.audience
224
+ )
225
+ }
226
+
227
+ // Text search
228
+ if (options.search) {
229
+ const search = options.search.toLowerCase()
230
+ results = results.filter(
231
+ (t) =>
232
+ t.name.toLowerCase().includes(search) ||
233
+ t.description.toLowerCase().includes(search) ||
234
+ t.id.toLowerCase().includes(search)
235
+ )
236
+ }
237
+
238
+ // Pagination
239
+ const offset = options.offset ?? 0
240
+ const limit = options.limit ?? results.length
241
+ results = results.slice(offset, offset + limit)
242
+
243
+ return results
244
+ }
245
+
246
+ /**
247
+ * Get tools by category
248
+ */
249
+ byCategory(category: ToolCategory): AnyTool[] {
250
+ return this.query({ category })
251
+ }
252
+
253
+ /**
254
+ * Execute a tool by ID
255
+ */
256
+ async executeTool<TInput = unknown, TOutput = unknown>(
257
+ id: string,
258
+ input: TInput
259
+ ): Promise<TOutput> {
260
+ const tool = this.tools.get(id)
261
+ if (!tool) {
262
+ throw new Error(`Tool "${id}" not found`)
263
+ }
264
+ return tool.handler(input) as Promise<TOutput>
265
+ }
266
+
267
+ /**
268
+ * Convert a Tool to MCP format
269
+ */
270
+ toMCP(tool: AnyTool): MCPTool {
271
+ return {
272
+ name: tool.id,
273
+ description: tool.description,
274
+ inputSchema: {
275
+ type: 'object',
276
+ properties: Object.fromEntries(
277
+ tool.parameters.map((p) => [
278
+ p.name,
279
+ typeof p.schema === 'object' && 'type' in p.schema
280
+ ? { ...p.schema, description: p.description }
281
+ : { description: p.description },
282
+ ])
283
+ ),
284
+ required: tool.parameters.filter((p) => p.required).map((p) => p.name),
285
+ },
286
+ }
287
+ }
288
+
289
+ /**
290
+ * Convert all registered tools to MCP format
291
+ */
292
+ listMCPTools(): MCPTool[] {
293
+ return this.list().map((id) => this.toMCP(this.get(id)!))
294
+ }
295
+
296
+ /**
297
+ * Import a tool from MCP format
298
+ */
299
+ fromMCP(
300
+ mcpTool: MCPTool,
301
+ handler: (input: unknown) => unknown | Promise<unknown>,
302
+ options?: {
303
+ category?: ToolCategory
304
+ subcategory?: ToolSubcategory
305
+ tags?: string[]
306
+ }
307
+ ): AnyTool {
308
+ const inputSchema = mcpTool.inputSchema
309
+ const properties = inputSchema.properties || {}
310
+ const required = inputSchema.required || []
311
+
312
+ const parameters = Object.entries(properties).map(([name, schema]) => ({
313
+ name,
314
+ description: (schema as { description?: string }).description || `Parameter: ${name}`,
315
+ schema: schema as { type?: string },
316
+ required: required.includes(name),
317
+ }))
318
+
319
+ const tool: AnyTool = {
320
+ id: mcpTool.name,
321
+ name: mcpTool.name,
322
+ description: mcpTool.description,
323
+ category: options?.category || 'integration',
324
+ ...(options?.subcategory !== undefined && { subcategory: options.subcategory }),
325
+ ...(options?.tags !== undefined && { tags: options.tags }),
326
+ parameters,
327
+ handler,
328
+ }
329
+
330
+ return tool
331
+ }
332
+
333
+ /**
334
+ * Clear all tools (except built-ins if keepBuiltins is true)
335
+ */
336
+ clear(keepBuiltins: boolean = false): void {
337
+ if (keepBuiltins) {
338
+ const builtinIds = ['data.json.parse', 'data.csv.parse', 'data.transform']
339
+ const builtins = builtinIds.map((id) => this.tools.get(id)).filter(Boolean) as AnyTool[]
340
+ this.tools.clear()
341
+ builtins.forEach((tool) => this.tools.set(tool.id, tool))
342
+ } else {
343
+ this.tools.clear()
344
+ }
345
+ }
346
+ }
347
+
348
+ /**
349
+ * Main tool service exposed via RPC as WorkerEntrypoint
350
+ *
351
+ * Usage:
352
+ * const tools = await env.TOOLS.connect()
353
+ * tools.register({ id: 'my.tool', ... })
354
+ * tools.list()
355
+ * const result = await tools.executeTool('data.json.parse', { text: '{}' })
356
+ */
357
+ export class ToolService extends WorkerEntrypoint {
358
+ /**
359
+ * Get a tool service instance - returns an RpcTarget that can be used directly
360
+ */
361
+ connect(): ToolServiceCore {
362
+ return new ToolServiceCore()
363
+ }
364
+ }
365
+
366
+ // Export as default for WorkerEntrypoint pattern
367
+ export default ToolService
368
+
369
+ // Export aliases
370
+ export { ToolService as ToolWorker }
package/src/wrap.ts ADDED
@@ -0,0 +1,260 @@
1
+ /**
2
+ * `wrapTool()` — bridge from a `Tool` definition to an HTTP-shaped handler
3
+ * gated by `id.org.ai` brokers.
4
+ *
5
+ * Mirrors id.org.ai's `wrap()` shape but is specific to digital-tools:
6
+ *
7
+ * 1. Calls `AuthBroker.gate(req, need)` to authenticate the caller.
8
+ * `need` is derived from the tool's `auth` declaration.
9
+ * On rejection, returns the broker's pre-baked `denialResponse(...)`.
10
+ *
11
+ * 2. When the tool declares `pricing`, calls
12
+ * `PaymentBroker.settle(req, identity, required)`. On rejection,
13
+ * returns the broker's pre-baked 402 (`PaymentRejection.response`).
14
+ * `required` is translated from our local MDXLD `PaymentRequired`
15
+ * shape into id.org.ai's discriminated `PaymentRequired` (`charge`
16
+ * intent — `session` intent is not yet supported in 0.3.0 and we
17
+ * throw a clear error if a tool requests it).
18
+ *
19
+ * 3. Reads `application/json` body, calls `tool.handler(input, ctx)`
20
+ * with `ctx.identity = Identity` and (when paid) `ctx.paymentReceipt
21
+ * = PaymentReceipt`. The receipt's `responseHeader` (e.g.
22
+ * `PAYMENT-RESPONSE` for x402 or `Payment-Receipt` for MPP) is
23
+ * stamped onto the success response.
24
+ *
25
+ * 4. Tools without `auth` and without `pricing` pass through directly;
26
+ * the handler is invoked with `ctx.identity` undefined-cast (we set
27
+ * it to a synthetic anonymous ref) so the handler signature stays
28
+ * consistent.
29
+ *
30
+ * Tests should pass structural fakes for `AuthBroker` and `PaymentBroker`
31
+ * — both are interfaces in id.org.ai. We intentionally do NOT depend on
32
+ * `AuthBrokerImpl` / `PaymentBrokerImpl` so this module is portable into
33
+ * Workers without pulling concrete adapters.
34
+ *
35
+ * @packageDocumentation
36
+ */
37
+
38
+ import type {
39
+ AuthBroker,
40
+ AuthRequirement as IdAuthRequirement,
41
+ Identity,
42
+ PaymentBroker,
43
+ PaymentRequired as IdPaymentRequired,
44
+ PaymentReceipt,
45
+ RailQuote,
46
+ } from 'id.org.ai'
47
+ import { denialResponse } from 'id.org.ai'
48
+ import type { AuthRequirement, PaymentRequired, Tool, ToolHandlerContext } from './types.js'
49
+
50
+ /**
51
+ * Translate the local MDXLD-shaped `AuthRequirement` (scopes + required
52
+ * mechanism) into id.org.ai's typed `AuthRequirement`. We always emit
53
+ * `minLevel: 1` for `oauth`/`apiKey` (a credential must be presented) and
54
+ * `minLevel: 0` for `none` (anonymous OK). Scopes flow through unchanged.
55
+ */
56
+ function toIdAuthRequirement(req: AuthRequirement): IdAuthRequirement {
57
+ const minLevel = req.required === 'none' ? 0 : 1
58
+ return {
59
+ minLevel,
60
+ ...(req.scopes.length > 0 && { scopes: req.scopes }),
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Translate the local MDXLD-shaped `PaymentRequired` (single amount +
66
+ * accepted protocols) into id.org.ai's discriminated `PaymentRequired`
67
+ * (`charge` intent with explicit `RailQuote[]`). Each `accepts` entry
68
+ * fans out into `(method)` quotes — we use `exact` for x402 and `tempo`
69
+ * for MPP per phase-1 of `PaymentBrokerImpl`.
70
+ *
71
+ * Throws on `intent: 'session'` style requests — our local
72
+ * `PaymentRequired` doesn't currently model session intent, but if a
73
+ * caller mutates it to add one we surface the limitation early.
74
+ */
75
+ function toIdPaymentRequired(pricing: PaymentRequired): IdPaymentRequired {
76
+ // Local `PaymentRequired` doesn't carry an `intent` discriminator;
77
+ // any future widening that does should fail fast here.
78
+ if ((pricing as unknown as { intent?: string }).intent === 'session') {
79
+ throw new Error(
80
+ "wrapTool: 'session' intent pricing is not yet supported (id.org.ai PaymentBroker.session() is not implemented in 0.3.0). " +
81
+ "Use 'charge' intent or wait for the next id.org.ai release."
82
+ )
83
+ }
84
+
85
+ const accepts: RailQuote[] = pricing.accepts.map((protocol): RailQuote => {
86
+ if (protocol === 'x402') {
87
+ return {
88
+ rail: { protocol: 'x402', method: 'exact', asset: pricing.currency },
89
+ amount: pricing.amount,
90
+ asset: pricing.currency,
91
+ payTo: pricing.recipient,
92
+ }
93
+ }
94
+ // protocol === 'mpp'
95
+ return {
96
+ rail: { protocol: 'mpp', method: 'tempo', asset: pricing.currency },
97
+ amount: pricing.amount,
98
+ asset: pricing.currency,
99
+ payTo: pricing.recipient,
100
+ }
101
+ })
102
+
103
+ return {
104
+ intent: 'charge',
105
+ accepts,
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Read the request body as JSON. Returns `undefined` for empty bodies
111
+ * and non-JSON content types so tools with no input still work.
112
+ */
113
+ async function readJsonBody(req: Request): Promise<unknown> {
114
+ const ct = req.headers.get('content-type') ?? ''
115
+ if (!ct.toLowerCase().includes('application/json')) return undefined
116
+ const text = await req.text()
117
+ if (!text) return undefined
118
+ try {
119
+ return JSON.parse(text)
120
+ } catch {
121
+ return undefined
122
+ }
123
+ }
124
+
125
+ /**
126
+ * Build a JSON `Response` for a successful tool invocation. Stamps the
127
+ * payment receipt's `responseHeader` (e.g. `PAYMENT-RESPONSE` for x402
128
+ * or `Payment-Receipt` for MPP) when present.
129
+ */
130
+ function jsonOk(body: unknown, receipt?: PaymentReceipt): Response {
131
+ const headers: Record<string, string> = { 'content-type': 'application/json' }
132
+ if (receipt?.responseHeader) {
133
+ const [name, value] = receipt.responseHeader
134
+ headers[name] = value
135
+ }
136
+ return new Response(JSON.stringify(body), { status: 200, headers })
137
+ }
138
+
139
+ /**
140
+ * Build a JSON error `Response`.
141
+ */
142
+ function jsonError(status: number, code: string, message: string): Response {
143
+ return new Response(JSON.stringify({ error: code, error_description: message }), {
144
+ status,
145
+ headers: { 'content-type': 'application/json' },
146
+ })
147
+ }
148
+
149
+ /**
150
+ * Wrap a Tool with broker-aware HTTP handling.
151
+ *
152
+ * @param broker AuthBroker (id.org.ai). Required when the tool has `auth`.
153
+ * @param paymentBroker PaymentBroker (id.org.ai). Required when the tool has `pricing`.
154
+ * @param tool The tool definition.
155
+ * @returns A function `(req: Request) => Promise<Response>` honoring the
156
+ * tool's `auth` and `pricing` declarations.
157
+ *
158
+ * @example
159
+ * ```ts
160
+ * const handler = wrapTool(authBroker, paymentBroker, sendEmailTool)
161
+ * // Use as a Worker fetch handler:
162
+ * export default { fetch: handler }
163
+ * ```
164
+ *
165
+ * Tools without `auth` / `pricing` pass through with no broker calls.
166
+ * In that case the broker arguments may still be provided but are unused.
167
+ */
168
+ export function wrapTool<TInput, TOutput>(
169
+ broker: AuthBroker | undefined,
170
+ paymentBroker: PaymentBroker | undefined,
171
+ tool: Tool<TInput, TOutput>
172
+ ): (req: Request) => Promise<Response> {
173
+ // Pre-translate auth/pricing once so per-request work stays minimal,
174
+ // and so a `session`-intent pricing fails at wrap-time rather than at
175
+ // first request — easier to debug.
176
+ const idAuth = tool.auth ? toIdAuthRequirement(tool.auth) : null
177
+ const idPricing = tool.pricing ? toIdPaymentRequired(tool.pricing) : null
178
+
179
+ return async function wrappedTool(req: Request): Promise<Response> {
180
+ let identity: Identity | undefined
181
+ let receipt: PaymentReceipt | undefined
182
+
183
+ // --- Auth gate ----------------------------------------------------
184
+ if (idAuth) {
185
+ if (!broker) {
186
+ return jsonError(
187
+ 500,
188
+ 'configuration_error',
189
+ `Tool "${tool.id}" declares auth but no AuthBroker was provided to wrapTool()`
190
+ )
191
+ }
192
+ const decision = await broker.gate(req, idAuth)
193
+ if (!decision.ok) {
194
+ return denialResponse(decision)
195
+ }
196
+ identity = decision.identity
197
+ }
198
+
199
+ // --- Payment settle ----------------------------------------------
200
+ if (idPricing) {
201
+ if (!paymentBroker) {
202
+ return jsonError(
203
+ 500,
204
+ 'configuration_error',
205
+ `Tool "${tool.id}" declares pricing but no PaymentBroker was provided to wrapTool()`
206
+ )
207
+ }
208
+ // Auth is required for paid tools so we have an Identity to settle
209
+ // against. If a tool has pricing without auth we fall back to a
210
+ // synthetic anonymous identity — the broker still accepts it but
211
+ // rail negotiation will fail unless the request carries proof.
212
+ const settleIdentity: Identity = identity ?? {
213
+ id: 'anonymous',
214
+ type: 'agent',
215
+ name: 'anonymous',
216
+ verified: false,
217
+ level: 0,
218
+ claimStatus: 'unclaimed',
219
+ }
220
+ const outcome = await paymentBroker.settle(req, settleIdentity, idPricing)
221
+ if (!outcome.ok) {
222
+ return outcome.response
223
+ }
224
+ receipt = outcome
225
+ }
226
+
227
+ // --- Invoke handler -----------------------------------------------
228
+ let input: TInput
229
+ try {
230
+ input = (await readJsonBody(req)) as TInput
231
+ } catch {
232
+ return jsonError(400, 'invalid_request', 'Could not parse request body as JSON')
233
+ }
234
+
235
+ const ctx: ToolHandlerContext = {
236
+ identity: identity ?? 'anonymous',
237
+ ...(receipt && { paymentReceipt: receipt }),
238
+ }
239
+
240
+ try {
241
+ const result = await tool.handler(input, ctx)
242
+ return jsonOk(result, receipt)
243
+ } catch (err) {
244
+ const message = err instanceof Error ? err.message : String(err)
245
+ return jsonError(500, 'execution_error', message)
246
+ }
247
+ }
248
+ }
249
+
250
+ /**
251
+ * Internal: exported only for tests. Turn a local MDXLD `PaymentRequired`
252
+ * into id.org.ai's `PaymentRequired`. Throws on session intent.
253
+ */
254
+ export const __toIdPaymentRequired = toIdPaymentRequired
255
+
256
+ /**
257
+ * Internal: exported only for tests. Turn a local `AuthRequirement`
258
+ * into id.org.ai's `AuthRequirement`.
259
+ */
260
+ export const __toIdAuthRequirement = toIdAuthRequirement