digital-tools 2.1.3 → 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 (294) hide show
  1. package/CHANGELOG.md +9 -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 +21 -11
  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 +31 -14
  132. package/src/client.ts +136 -0
  133. package/src/define.ts +30 -24
  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 -4
  199. package/LICENSE +0 -21
  200. package/dist/providers/voice/vapi.d.ts +0 -27
  201. package/dist/providers/voice/vapi.d.ts.map +0 -1
  202. package/dist/providers/voice/vapi.js +0 -440
  203. package/dist/providers/voice/vapi.js.map +0 -1
  204. package/src/define.js +0 -259
  205. package/src/entities/advertising.js +0 -999
  206. package/src/entities/ai.js +0 -756
  207. package/src/entities/analytics.js +0 -1588
  208. package/src/entities/automation.js +0 -601
  209. package/src/entities/communication.js +0 -1150
  210. package/src/entities/crm.js +0 -1386
  211. package/src/entities/design.js +0 -546
  212. package/src/entities/development.js +0 -2212
  213. package/src/entities/document.js +0 -874
  214. package/src/entities/ecommerce.js +0 -1429
  215. package/src/entities/experiment.js +0 -1039
  216. package/src/entities/finance.js +0 -3478
  217. package/src/entities/forms.js +0 -1892
  218. package/src/entities/hr.js +0 -661
  219. package/src/entities/identity.js +0 -997
  220. package/src/entities/index.js +0 -282
  221. package/src/entities/infrastructure.js +0 -1153
  222. package/src/entities/knowledge.js +0 -1438
  223. package/src/entities/marketing.js +0 -1610
  224. package/src/entities/media.js +0 -1634
  225. package/src/entities/notification.js +0 -1199
  226. package/src/entities/presentation.js +0 -1274
  227. package/src/entities/productivity.js +0 -1317
  228. package/src/entities/project-management.js +0 -1136
  229. package/src/entities/recruiting.js +0 -736
  230. package/src/entities/shipping.js +0 -509
  231. package/src/entities/signature.js +0 -1102
  232. package/src/entities/site.js +0 -222
  233. package/src/entities/spreadsheet.js +0 -1341
  234. package/src/entities/storage.js +0 -1198
  235. package/src/entities/support.js +0 -1166
  236. package/src/entities/video-conferencing.js +0 -1750
  237. package/src/entities/video.js +0 -950
  238. package/src/entities.js +0 -1663
  239. package/src/index.js +0 -74
  240. package/src/providers/analytics/index.js +0 -17
  241. package/src/providers/analytics/mixpanel.js +0 -255
  242. package/src/providers/calendar/cal-com.js +0 -303
  243. package/src/providers/calendar/google-calendar.js +0 -335
  244. package/src/providers/calendar/index.js +0 -20
  245. package/src/providers/crm/hubspot.js +0 -566
  246. package/src/providers/crm/index.js +0 -17
  247. package/src/providers/development/github.js +0 -472
  248. package/src/providers/development/index.js +0 -17
  249. package/src/providers/ecommerce/index.js +0 -17
  250. package/src/providers/ecommerce/shopify.js +0 -378
  251. package/src/providers/email/index.js +0 -20
  252. package/src/providers/email/resend.js +0 -258
  253. package/src/providers/email/sendgrid.js +0 -161
  254. package/src/providers/finance/index.js +0 -17
  255. package/src/providers/finance/stripe.js +0 -549
  256. package/src/providers/forms/index.js +0 -17
  257. package/src/providers/forms/typeform.js +0 -500
  258. package/src/providers/index.js +0 -123
  259. package/src/providers/knowledge/index.js +0 -17
  260. package/src/providers/knowledge/notion.js +0 -389
  261. package/src/providers/marketing/index.js +0 -17
  262. package/src/providers/marketing/mailchimp.js +0 -443
  263. package/src/providers/media/cloudinary.js +0 -318
  264. package/src/providers/media/index.js +0 -17
  265. package/src/providers/messaging/index.js +0 -20
  266. package/src/providers/messaging/slack.js +0 -393
  267. package/src/providers/messaging/twilio-sms.js +0 -249
  268. package/src/providers/project-management/index.js +0 -17
  269. package/src/providers/project-management/linear.js +0 -575
  270. package/src/providers/registry.js +0 -86
  271. package/src/providers/spreadsheet/google-sheets.js +0 -375
  272. package/src/providers/spreadsheet/index.js +0 -20
  273. package/src/providers/spreadsheet/xlsx.js +0 -423
  274. package/src/providers/storage/index.js +0 -24
  275. package/src/providers/storage/s3.js +0 -419
  276. package/src/providers/support/index.js +0 -17
  277. package/src/providers/support/zendesk.js +0 -373
  278. package/src/providers/tasks/index.js +0 -17
  279. package/src/providers/tasks/todoist.js +0 -286
  280. package/src/providers/types.js +0 -9
  281. package/src/providers/video-conferencing/google-meet.js +0 -286
  282. package/src/providers/video-conferencing/index.js +0 -31
  283. package/src/providers/video-conferencing/jitsi.js +0 -254
  284. package/src/providers/video-conferencing/teams.js +0 -270
  285. package/src/providers/video-conferencing/zoom.js +0 -332
  286. package/src/registry.js +0 -128
  287. package/src/tools/communication.js +0 -184
  288. package/src/tools/data.js +0 -205
  289. package/src/tools/index.js +0 -11
  290. package/src/tools/web.js +0 -137
  291. package/src/types.js +0 -10
  292. package/test/define.test.js +0 -306
  293. package/test/registry.test.js +0 -357
  294. 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